如何从Bash变量中修剪空格?


920

我有一个带有以下代码的shell脚本:

var=`hg st -R "$path"`
if [ -n "$var" ]; then
    echo $var
fi

但是条件代码总是执行,因为hg st总是打印至少一个换行符。

  • 是否有一种简单的方法来删除空格$var(例如trim()PHP中)?

要么

  • 有没有解决此问题的标准方法?

我可以使用sedAWK,但是我想认为有一个更优雅的解决方案。


3
相关的,如果您想修剪整数空间并得到整数,请用$((($ var))换行,甚至在双引号内也可以这样做。当我使用date语句和文件名时,这一点变得很重要。
Volomike 2012年

“有没有解决此问题的标准方法?” 是的,使用[[代替[。$ var=$(echo) $ [ -n $var ]; echo $? #undesired test return 0 $ [[ -n $var ]]; echo $? 1
user.friendly

如果有帮助,请至少在Ubuntu 16.04上进行测试。使用以下匹配项以各种方式进行修剪: echo " This is a string of char " | xargs。但是,如果文本中有单引号,则可以执行以下操作:echo " This i's a string of char " | xargs -0。请注意,我提到了最新的xargs(4.6.0)
Luis Alvarado

由于换行符导致的条件不正确,因为反引号会吞下最后一个换行符。这将不会打印任何内容test=`echo`; if [ -n "$test" ]; then echo "Not empty"; fi,但是会打印test=`echo "a"`; if [ -n "$test" ]; then echo "Not empty"; fi-因此末尾必须不仅仅是换行符。
Mecki

A =“ 123 4 5 6”; B = echo $A | sed -r 's/( )+//g';
bruziuz

Answers:


1021

让我们定义一个包含前导,尾随和中间空格的变量:

FOO=' test test test '
echo -e "FOO='${FOO}'"
# > FOO=' test test test '
echo -e "length(FOO)==${#FOO}"
# > length(FOO)==16

如何移除所有的空格(记[:space:]tr):

FOO=' test test test '
FOO_NO_WHITESPACE="$(echo -e "${FOO}" | tr -d '[:space:]')"
echo -e "FOO_NO_WHITESPACE='${FOO_NO_WHITESPACE}'"
# > FOO_NO_WHITESPACE='testtesttest'
echo -e "length(FOO_NO_WHITESPACE)==${#FOO_NO_WHITESPACE}"
# > length(FOO_NO_WHITESPACE)==12

如何仅删除前导空格:

FOO=' test test test '
FOO_NO_LEAD_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//')"
echo -e "FOO_NO_LEAD_SPACE='${FOO_NO_LEAD_SPACE}'"
# > FOO_NO_LEAD_SPACE='test test test '
echo -e "length(FOO_NO_LEAD_SPACE)==${#FOO_NO_LEAD_SPACE}"
# > length(FOO_NO_LEAD_SPACE)==15

如何仅删除尾随空白:

FOO=' test test test '
FOO_NO_TRAIL_SPACE="$(echo -e "${FOO}" | sed -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_TRAIL_SPACE='${FOO_NO_TRAIL_SPACE}'"
# > FOO_NO_TRAIL_SPACE=' test test test'
echo -e "length(FOO_NO_TRAIL_SPACE)==${#FOO_NO_TRAIL_SPACE}"
# > length(FOO_NO_TRAIL_SPACE)==15

如何同时删除前导空格和尾随空格-将seds链接:

FOO=' test test test '
FOO_NO_EXTERNAL_SPACE="$(echo -e "${FOO}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
echo -e "FOO_NO_EXTERNAL_SPACE='${FOO_NO_EXTERNAL_SPACE}'"
# > FOO_NO_EXTERNAL_SPACE='test test test'
echo -e "length(FOO_NO_EXTERNAL_SPACE)==${#FOO_NO_EXTERNAL_SPACE}"
# > length(FOO_NO_EXTERNAL_SPACE)==14

或者,如果您的bash支持它,则可以将替换echo -e "${FOO}" | sed ...sed ... <<<${FOO},如下所示(用于尾随空白):

FOO_NO_TRAIL_SPACE="$(sed -e 's/[[:space:]]*$//' <<<${FOO})"

63
要泛化处理所有形式的空格的解决方案,请使用替换trand sed命令中的空格字符[[:space:]]。请注意,该sed方法仅适用于单行输入。对于适用于多行输入并且还使用bash内置功能的方法,请参见@bashfu和@GuruM的答案。@Nicholas Sushkin解决方案的通用嵌入式版本如下所示: trimmed=$([[ " test test test " =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]; echo -n "${BASH_REMATCH[1]}")
mklement0 2012年

7
如果你这样做的时候,附加alias trim="sed -e 's/^[[:space:]]*//g' -e 's/[[:space:]]*\$//g'"到你~/.profile允许你使用echo $SOMEVAR | trimcat somefile | trim
instanceof我

我写了一个sed只使用一个表达式而不使用两个表达式的解决方案:sed -r 's/^\s*(\S+(\s+\S+)*)\s*$/\1/'。它修剪前导和尾随空格,并在中间捕获任何由空格分隔的非空格字符序列。请享用!
维克多·扎曼尼安

@VictorZamanian如果输入仅包含空格,则您的解决方案将不起作用。MattyV和instanceof我给出的两模式sed解决方案在仅空格输入的情况下可以正常工作。
Torben 2014年

@Torben公平点。我想可以使用来使单个表达式成为条件表达式|,以便将其保留为一个单个表达式,而不是多个。
维克多·扎曼尼安

964

一个简单的答案是:

echo "   lol  " | xargs

Xargs将为您进行修剪。这是一个命令/程序,没有参数,返回修整后的字符串,就这么简单!

注意:这不会删除所有内部空间,因此"foo bar"保持不变;它不会成为"foobar"。但是,多个空间将被压缩为单个空间,因此"foo bar"将变为"foo bar"。此外,它不会删除行尾字符。


27
真好 这真的很好。我已决定将其传递xargs echo给我正在做的事情是很冗长的,但是默认情况下,xargs本身将使用echo。
2013年

24
不错的技巧,但是要小心,您可以将其用于单行字符串,但是-通过xargs设计-它不仅可以对多行​​管道内容进行修剪。sed是你的朋友。
2013年

22
xargs的唯一问题是它将引入换行符,如果您希望不使用该换行符,我建议您sed 's/ *$//'选择一种替代方法。你可以看到xargs像这样的新行:echo -n "hey thiss " | xargs | hexdump 你会注意到0a73a是换行符。如果您对sed: 做同样的事情,echo -n "hey thiss " | sed 's/ *$//' | hexdump您将看到0073,没有换行符。

8
小心; 如果xargs的字符串之间包含多余的空格,这将很难解决。就像“这是一个论点”。xargs将分为四个。
bos 2015年

64
这是不好的。1.它会a<space><space>b变成a<space>b。2.甚至更多:它将a"b"c'd'e变成abcde。3.更:它会失败的a"b,等等
萨沙

357

有一个解决方案仅使用Bash内置的通配符

var="    abc    "
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="${var%"${var##*[![:space:]]}"}"   
printf '%s' "===$var==="

这是包装在函数中的相同内容:

trim() {
    local var="$*"
    # remove leading whitespace characters
    var="${var#"${var%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    var="${var%"${var##*[![:space:]]}"}"   
    printf '%s' "$var"
}

您传递要以引号形式修剪的字符串。例如:

trim "   abc   "

关于此解决方案的一件好事是,它将与任何POSIX兼容的外壳一起使用。

参考


17
聪明!这是我最喜欢的解决方案,因为它使用内置的bash功能。感谢您的发布!@San,这是两个嵌套的字符串修剪。例如,s=" 1 2 3 "; echo \""${s%1 2 3 }"\"从头开始修剪所有内容,返回前导" "。Subing 1 2 3 with [![:space:]]*告诉它“找到第一个非空格字符,然后破坏它以及之后的所有内容”。使用%%代替%使从头开始修剪操作贪婪。这嵌套在非贪婪的“从头开始修剪”中,因此实际上" "是从头开始修剪。然后,将%,#和*交换为末尾空格。am!
Mark G.

2
我还没有发现任何有害的副作用,并且主要代码也可以与其他类似POSIX的外壳一起使用。但是,在Solaris 10下,它不能使用/bin/sh(仅用于/usr/xpg4/bin/sh,但这不适用于常规sh脚本)。
vinc17 2016年

9
比使用sed,tr等更好的解决方案,因为它要快得多,避免了任何fork()。在Cygwin上,速度差异是数量级。
Gene Pavlovsky

9
@San起初我很为难,因为我认为这些是正则表达式。他们不是。更确切地说,这是模式匹配语法(gnu.org/software/bash/manual/html_node/Pattern-Matching.htmlwiki.bash-hackers.org/syntax/pattern在子串去除(使用)tldp.org/LDP/abs /html/string-manipulation.html)。因此${var%%[![:space:]]*}说:“从var以非空格字符开头的最长子字符串中删除”。这意味着您只剩下前导空格,随后用删除${var#..。以下行(尾随)是相反的。
Ohad Schneider

8
这绝对是理想的解决方案。分叉的一个或多个外部处理(例如,awksedtrxargs)仅仅从单个串装饰空白是从根本上疯狂-尤其是当最壳(包括bash)的已经提供本地字符串改写(munging)设施外的所述盒。
塞西尔·库里

80

Bash具有称为参数扩展的功能,除其他功能外,它还允许基于所谓的模式替换字符串(模式类似于正则表达式,但是存在根本的区别和局限性)。[flussence的原始行:Bash具有正则表达式,但它们是很好的隐藏物:]

下面演示了如何从变量值中删除所有空白(甚至从内部)。

$ var='abc def'
$ echo "$var"
abc def
# Note: flussence's original expression was "${var/ /}", which only replaced the *first* space char., wherever it appeared.
$ echo -n "${var//[[:space:]]/}"
abcdef

2
或者更确切地说,它适用于var中间的空格,但是当我尝试将其锚定在末尾时无效。
Paul Tomblin

这有帮助吗?在联机帮助页中:“ $ {parameter / pattern / string} [...]如果pattern以%开头,则它必须在参数扩展值的末尾匹配。”

@Ant,所以它们不是真正的正则表达式,而是类似的东西?
Paul Tomblin

3
它们是正则表达式,只是一个奇怪的方言。

13
${var/ /}删除第一个空格字符。${var// /}删除所有空格字符。仅使用这种结构就无法修剪前导和尾随空白。
吉尔(Gilles)“所以,别再邪恶了”

60

为了删除字符串开头和结尾(包括行尾字符)中的所有空格,请执行以下操作:

echo $variable | xargs echo -n

这还将删除重复的空格:

echo "  this string has a lot       of spaces " | xargs echo -n

产生:“此字符串有很多空格”


5
基本上,xargs将从字符串中删除所有定界符。缺省情况下,它使用空格作为定界符(可以通过-d选项更改)。
rkachach'3

4
到目前为止,这是最干净的(简短易读的)解决方案。
Potherca '17

为什么根本需要echo -necho " my string " | xargs具有相同的输出。
bfontaine

echo -n也删除行尾
rkachach

55

去除一个前导空间和一个尾随空间

trim()
{
    local trimmed="$1"

    # Strip leading space.
    trimmed="${trimmed## }"
    # Strip trailing space.
    trimmed="${trimmed%% }"

    echo "$trimmed"
}

例如:

test1="$(trim " one leading")"
test2="$(trim "one trailing ")"
test3="$(trim " one leading and one trailing ")"
echo "'$test1', '$test2', '$test3'"

输出:

'one leading', 'one trailing', 'one leading and one trailing'

去除所有前导和尾随空格

trim()
{
    local trimmed="$1"

    # Strip leading spaces.
    while [[ $trimmed == ' '* ]]; do
       trimmed="${trimmed## }"
    done
    # Strip trailing spaces.
    while [[ $trimmed == *' ' ]]; do
        trimmed="${trimmed%% }"
    done

    echo "$trimmed"
}

例如:

test4="$(trim "  two leading")"
test5="$(trim "two trailing  ")"
test6="$(trim "  two leading and two trailing  ")"
echo "'$test4', '$test5', '$test6'"

输出:

'two leading', 'two trailing', 'two leading and two trailing'

9
这将仅修剪1个空格字符。因此,回声导致'hello world ', 'foo bar', 'both sides '
Joe

@Joe我添加了一个更好的选择。
wjandrea

42

从Bash指南中关于遍历的部分

在参数扩展中使用extglob

 #Turn on extended globbing  
shopt -s extglob  
 #Trim leading and trailing whitespace from a variable  
x=${x##+([[:space:]])}; x=${x%%+([[:space:]])}  
 #Turn off extended globbing  
shopt -u extglob  

这是包装在函数中的相同功能(注意:需要引用传递给函数的输入字符串):

trim() {
    # Determine if 'extglob' is currently on.
    local extglobWasOff=1
    shopt extglob >/dev/null && extglobWasOff=0 
    (( extglobWasOff )) && shopt -s extglob # Turn 'extglob' on, if currently turned off.
    # Trim leading and trailing whitespace
    local var=$1
    var=${var##+([[:space:]])}
    var=${var%%+([[:space:]])}
    (( extglobWasOff )) && shopt -u extglob # If 'extglob' was off before, turn it back off.
    echo -n "$var"  # Output trimmed string.
}

用法:

string="   abc def ghi  ";
#need to quote input-string to preserve internal white-space if any
trimmed=$(trim "$string");  
echo "$trimmed";

如果我们更改要在子shell中执行的功能,则不必担心检查extglob的当前shell选项,我们可以对其进行设置而不会影响当前shell。这极大地简化了功能。我还“就地”更新了位置参数,所以我什至不需要局部变量

trim() {
    shopt -s extglob
    set -- "${1##+([[:space:]])}"
    printf "%s" "${1%%+([[:space:]])}" 
}

所以:

$ s=$'\t\n \r\tfoo  '
$ shopt -u extglob
$ shopt extglob
extglob         off
$ printf ">%q<\n" "$s" "$(trim "$s")"
>$'\t\n \r\tfoo  '<
>foo<
$ shopt extglob
extglob         off

2
如您所见,trim()仅删除前导和尾随空格。
GuruM 2012年

正如mkelement已经指出的那样,您需要将函数参数作为带引号的字符串传递,即$(trim“ $ string”)而不是$(trim $ string)。我已经更新了代码以显示正确的用法。谢谢。
GuruM 2012年

虽然我很欣赏寂寂壳选择,我不认为最终的结果是更优雅比简单地做2个模式替换
sehe

需要注意的是(使用bash的足够新版本?),可以简化机构的恢复选项extglob,通过使用shopt -p:简单地写local restore="$(shopt -p extglob)" ; shopt -s extglob在你的函数的开始,并eval "$restore"在结束(除批,EVAL是邪恶的......)。
马兰(Maëlan)

很好的解决方案!一个潜在的改进:看起来[[:space:]]可以用一个空格代替:${var##+( )}并且也可以${var%%+( )}正常工作,而且它们更易于阅读。
DKroot

40

您可以使用echo以下命令简单地进行修剪:

foo=" qsdqsd qsdqs q qs   "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'

这会将多个连续的空间折叠成一个。
Evgeni Sergeev

7
foo包含通配符时您尝试过吗?例如,foo=" I * have a wild card"...惊喜!而且,这将几个连续的空间折叠成一个。
gniourf_gniourf 2014年

5
如果您遇到以下情况,这是一个极好的解决方案:1.两端不希望有空格。2.每个单词之间只希望有一个空格。3.正在使用没有通配符的受控输入。从本质上来说,它会将格式错误的列表变成了好的列表。
musicin3d

很好地提醒了通配符@gniourf_gniourf +1。Vamp还是一个出色的解决方案。也向您+1。
Beco博士

25

我一直都用sed来做

  var=`hg st -R "$path" | sed -e 's/  *$//'`

如果有更好的解决方案,我希望有人发布它。


你能解释一下语法sed吗?
farid99

2
正则表达式匹配所有尾随空格,并将其替换为空白。
Paul Tomblin

4
前导空格如何?
前千

这将删除所有尾随空白sed -e 's/\s*$//'。说明:'s'表示搜索,'\ s'表示所有空格,'*'表示零或多个,'$'表示直到行尾,'//'表示将所有匹配项替换为空字符串。
Craig

在's / * $ //'中,为什么在星号前有2个空格而不是一个空格?那是错字吗?
布伦特212

24

您可以使用删除换行符tr

var=`hg st -R "$path" | tr -d '\n'`
if [ -n $var ]; then
    echo $var
done

8
我不想从字符串的中间删除“ \ n”,而只是从开头或结尾删除。
太多的PHP

24

启用Bash的扩展模式匹配功能(shopt -s extglob)后,您可以使用以下命令:

{trimmed##*( )}

删除任意数量的前导空格。


了不起!我认为这是最轻巧,最优雅的解决方案。
dubiousjim 2011年

1
有关类似但更通用的解决方案,请参阅@GuruM的帖子,该解决方案(a)处理所有形式的空白,并且(b)还处理尾随空白。
mklement0 2012年

@mkelement +1麻烦您将我的代码片段重写为一个函数。谢谢
GuruM

也可以与OpenBSD的默认/ bin / ksh一起使用。/bin/sh -o posix也可以,但是我很怀疑。
克林特·帕奇

这里不是bash向导;什么trimmed啊 是内置的东西还是正在修剪的变量?
Abhijit Sarkar,

19
# Trim whitespace from both ends of specified parameter

trim () {
    read -rd '' $1 <<<"${!1}"
}

# Unit test for trim()

test_trim () {
    local foo="$1"
    trim foo
    test "$foo" = "$2"
}

test_trim hey hey &&
test_trim '  hey' hey &&
test_trim 'ho  ' ho &&
test_trim 'hey ho' 'hey ho' &&
test_trim '  hey  ho  ' 'hey  ho' &&
test_trim $'\n\n\t hey\n\t ho \t\n' $'hey\n\t ho' &&
test_trim $'\n' '' &&
test_trim '\n' '\n' &&
echo passed

2
惊人!简单有效!显然,我最喜欢的解决方案。谢谢!
xebeche 2012年

1
@CraigMcQueen,它是变量值,read将以其名称$ 1存储其值$ {!1}的修整版本
Aquarius Power

2
trim()函数的参数是一个变量名称:请参见test_trim()中对trim()的调用。在从test_trim()调用的trim()中,$ 1扩展为foo,而$ {!1}扩展为$ foo(即,变量foo的当前内容)。在bash手册中搜索“变量间接”。
flabdablet 2015年

1
为了支持一次调用中的多个var修剪而进行的少量修改又如何呢?trim() { while [[ $# -gt 0 ]]; do read -rd '' $1 <<<"${!1}"; shift; done; }
Gene Pavlovsky

2
@AquariusPower无需为单线版本的子外壳使用echo read -rd '' str <<<"$str"
flabdablet '16

12

有很多答案,但是我仍然相信我刚写的脚本值得一提,因为:

  • 它已在shell bash / dash / busybox shell中成功测试
  • 它很小
  • 它不依赖于外部命令,也不需要派生(->快速和低资源使用率)
  • 它按预期工作:
    • 它从开始到结束剥离了所有空格和制表符,但没有更多
    • 重要:它不会从字符串的中间删除任何内容(许多其他答案都可以),即使换行符也将保留
    • 特殊:"$*"使用一个空格连接多个参数。如果要修剪并仅输出第一个参数,请"$1"改用
    • 如果匹配文件名模式没有任何问题等

剧本:

trim() {
  local s2 s="$*"
  until s2="${s#[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  until s2="${s%[[:space:]]}"; [ "$s2" = "$s" ]; do s="$s2"; done
  echo "$s"
}

用法:

mystring="   here     is
    something    "
mystring=$(trim "$mystring")
echo ">$mystring<"

输出:

>here     is
    something<

用C语言实现Bah会更容易实现!
尼尔斯

当然。不幸的是,这不是C语言,有时您希望避免调用外部工具
Daniel Alder

为了使代码更具可读性且与复制过去兼容,可以将方括号更改为转义字符:[\ \t]
leondepeon

@leondepeon您尝试了吗?我写了本书后就尝试了,然后再试一次,但您的建议在bash,dash,busybox中均不起作用
Daniel Alder

@DanielAlder我做到了,但正如3年前一样,我找不到使用它的代码。但是现在,我可能会[[:space:]]像在其他答案之一中那样使用:stackoverflow.com/a/3352015/3968618
leondepeon

11

您可以使用老式的tr。例如,这将返回git存储库中已修改文件的数量(去除空格)。

MYVAR=`git ls-files -m|wc -l|tr -d ' '`

1
这不会从正面和背面修剪空格,而是从字符串中删除所有空格。
尼克

11

这为我工作:

text="   trim my edges    "

trimmed=$text
trimmed=${trimmed##+( )} #Remove longest matching series of spaces from the front
trimmed=${trimmed%%+( )} #Remove longest matching series of spaces from the back

echo "<$trimmed>" #Adding angle braces just to make it easier to confirm that all spaces are removed

#Result
<trim my edges>

为了得到相同的结果,将其放在更少的行上:

text="    trim my edges    "
trimmed=${${text##+( )}%%+( )}

1
没为我工作。第一个打印未修饰的字符串。第二个丢了糟糕的替补。您能解释一下这是怎么回事吗?
musicin3d

1
@ musicin3d:这是我经常使用的网站,阐明了bash搜索中变量操作的工作原理${var##Pattern}获取更多详细信息。另外,该网站还介绍了bash模式。因此,该##方法从前面%%删除了给定的图案,而从后面删除了给定的图案。该+( )部分是模式,它表示“一个或多个空间的出现”
gMale 2015年

有趣的是,它可以在提示符下工作,但在转换为bash脚本文件后无法工作。
Beco博士

奇怪的。两个实例中的bash版本是否相同?
gMale

11
# Strip leading and trailing white space (new line inclusive).
trim(){
    [[ "$1" =~ [^[:space:]](.*[^[:space:]])? ]]
    printf "%s" "$BASH_REMATCH"
}

要么

# Strip leading white space (new line inclusive).
ltrim(){
    [[ "$1" =~ [^[:space:]].* ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    [[ "$1" =~ .*[^[:space:]] ]]
    printf "%s" "$BASH_REMATCH"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

要么

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

要么

# Strip leading specified characters.  ex: str=$(ltrim "$str" $'\n a')
ltrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^["$trim_chrs"]*(.*[^"$trim_chrs"]) ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip trailing specified characters.  ex: str=$(rtrim "$str" $'\n a')
rtrim(){
    if [ "$2" ]; then
        trim_chrs="$2"
    else
        trim_chrs="[:space:]"
    fi

    [[ "$1" =~ ^(.*[^"$trim_chrs"])["$trim_chrs"]*$ ]]
    printf "%s" "${BASH_REMATCH[1]}"
}

# Strip leading and trailing specified characters.  ex: str=$(trim "$str" $'\n a')
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1" "$2")" "$2")"
}

要么

以moskit的expr解决方案为基础...

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)[[:space:]]*$"`"
}

要么

# Strip leading white space (new line inclusive).
ltrim(){
    printf "%s" "`expr "$1" : "^[[:space:]]*\(.*[^[:space:]]\)"`"
}

# Strip trailing white space (new line inclusive).
rtrim(){
    printf "%s" "`expr "$1" : "^\(.*[^[:space:]]\)[[:space:]]*$"`"
}

# Strip leading and trailing white space (new line inclusive).
trim(){
    printf "%s" "$(rtrim "$(ltrim "$1")")"
}

8

我见过脚本只使用变量赋值来完成这项工作:

$ xyz=`echo -e 'foo \n bar'`
$ echo $xyz
foo bar

空白会自动合并并修剪。必须注意外壳的元字符(潜在的注入风险)。

我还建议在shell条件语句中始终双引号变量替换:

if [ -n "$var" ]; then

因为类似-o或变量中其他内容的内容可能会修改测试参数。


3
这是不带引号的使用$xyzecho该做的空白凝聚,没有赋值的变量。要将修剪后的值存储在示例中的变量中,必须使用xyz=$(echo -n $xyz)。此外,此方法还可能受到潜在有害路径名扩展(globbing)的影响。
mklement0 2012年

这是错误的,未xyz修剪变量中的值。
caesarsol 2015年

7
var='   a b c   '
trimmed=$(echo $var)

1
如果在任何两个单词之间有一个以上的空格,那将是行不通的。尝试:(echo $(echo "1 2 3")在1、2和3之间有两个空格)。
joshlf

7

我只会使用sed:

function trim
{
    echo "$1" | sed -n '1h;1!H;${;g;s/^[ \t]*//g;s/[ \t]*$//g;p;}'
}

a)在单行字符串上的用法示例

string='    wordA wordB  wordC   wordD    '
trimmed=$( trim "$string" )

echo "GIVEN STRING: |$string|"
echo "TRIMMED STRING: |$trimmed|"

输出:

GIVEN STRING: |    wordA wordB  wordC   wordD    |
TRIMMED STRING: |wordA wordB  wordC   wordD|

b)在多行字符串上的用法示例

string='    wordA
   >wordB<
wordC    '
trimmed=$( trim "$string" )

echo -e "GIVEN STRING: |$string|\n"
echo "TRIMMED STRING: |$trimmed|"

输出:

GIVEN STRING: |    wordAA
   >wordB<
wordC    |

TRIMMED STRING: |wordAA
   >wordB<
wordC|

c)最后的注释:
如果您不喜欢使用函数,则对于单行字符串,您可以简单地使用“更容易记住”的命令,例如:

echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

例:

echo "   wordA wordB wordC   " | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

输出:

wordA wordB wordC

多行字符串上使用上面的命令也可以使用,但是请注意,正如GuruM在评论中所指出的,它也将减少任何尾随/前导的内部多个空格。

string='    wordAA
    >four spaces before<
 >one space before<    '
echo "$string" | sed -e 's/^[ \t]*//' | sed -e 's/[ \t]*$//'

输出:

wordAA
>four spaces before<
>one space before<

因此,如果您想保留这些空格,请在我的答案开头使用该功能!

d)在函数修剪内使用的多行字符串上解释 sed语法“查找并替换”:

sed -n '
# If the first line, copy the pattern to the hold buffer
1h
# If not the first line, then append the pattern to the hold buffer
1!H
# If the last line then ...
$ {
    # Copy from the hold to the pattern buffer
    g
    # Do the search and replace
    s/^[ \t]*//g
    s/[ \t]*$//g
    # print
    p
}'

注意:如@mkelement所建议,它不适用于多行字符串,但应适用于单行字符串。
GuruM 2012年

1
您错了:它也适用于多行字符串。只是测试一下!:)
卡·博里昂

使用率+1-使我可以轻松地测试代码。但是,该代码仍不适用于多行字符串。如果仔细查看输出,您会注意到所有前导/后部内部空间也都被删除了,例如,“多行”前面的空间被“多行”代替。只需尝试增加每行的前导/尾随空格数即可。
GuruM 2012年

现在我明白你的意思了!谢谢您的注意,我修改了答案。
卡·博里昂

@“ Luca Borrione”-欢迎:-)您能解释一下trim()中使用的sed语法吗?它也可以帮助您的代码的任何用户将其调整为其他用途。同样,它甚至可以帮助找到正则表达式的边线。
GuruM 2012年

6

这是一个trim()函数,用于修剪和标准化空白

#!/bin/bash
function trim {
    echo $*
}

echo "'$(trim "  one   two    three  ")'"
# 'one two three'

还有另一个使用正则表达式的变体。

#!/bin/bash
function trim {
    local trimmed="$@"
    if [[ "$trimmed" =~ " *([^ ].*[^ ]) *" ]]
    then 
        trimmed=${BASH_REMATCH[1]}
    fi
    echo "$trimmed"
}

echo "'$(trim "  one   two    three  ")'"
# 'one   two    three'

第一种方法很棘手,因为它不仅规范化内部空白(将所有内部空白都替换为一个空格),而且还受到泛化(路径名扩展)的影响,例如,*输入字符串中的字符将扩展到当前工作文件夹中的所有文件和文件夹。最后,如果将$ IFS设置为非默认值,则修整可能不起作用(尽管通过添加可以很容易地进行修整local IFS=$' \t\n')。修剪仅限于以下形式的空格:空格\t\n字符。
mklement0 2012年

1
第二种基于正则表达式的方法很好并且没有副作用,但是以目前的形式存在问题:(a)在bash v3.2 +上,默认情况下,匹配将不起作用,因为正则表达式必须按顺序用联合国引号可以正常工作;(b)正则表达式本身无法处理输入字符串是由空格包围的单个非空格字符的情况。要解决这些问题,请将if行替换为:if [[ "$trimmed" =~ ' '*([^ ]|[^ ].*[^ ])' '* ]]。最后,该方法仅处理空格,而不处理其他形式的空格(请参阅我的下一条评论)。
mklement0 2012年

2
利用正则表达式的函数仅处理空格,而不处理其他形式的空格,但是很容易概括:用以下if行代替该行:[[ "$trimmed" =~ [[:space:]]*([^[:space:]]|[^[:space:]].*[^[:space:]])[[:space:]]* ]]
mklement0 2012年

6

使用AWK:

echo $var | awk '{gsub(/^ +| +$/,"")}1'

甜蜜似乎有效(例如:) $stripped_version=echo $ var | awk'{gsub(/ ^ + | + $ /,“”)} 1'``
rogerdpack 2010年

4
除了awk没做任何事情:回显一个未加引号的变量已经去除了空格
glenn jackman 2011年

6

分配忽略前导和尾随空格,因此可以用来修剪:

$ var=`echo '   hello'`; echo $var
hello

8
这不是真的。删除空格而不是分配的是“回声”。在您的示例中,请echo "$var"务必查看带空格的值。
尼古拉斯·舒什金

2
@NicholasSushkin一个可以做,var=$(echo $var)但我不建议这样做。此处介绍的其他解决方案是首选。
xebeche 2012年

5

这不会出现不必要的浮点问题,而且内部的空白未修改(假设$IFS设置为默认值' \t\n')。

它读取直到第一个换行符(并且不包括它)或字符串的末尾(以先到者为准),并去除前导和尾随空格和\t字符的任何混合。如果要保留多行(并去除开头和结尾的换行符),请read -r -d '' var << eof改用;但是请注意,如果您输入的内容恰好包含\neof,它将在之前被截断。(即使将其他形式的空白,即\r\f\v,也不会删除,即使将它们添加到$ IFS中也是如此。)

read -r var << eof
$var
eof


5

这将从字符串中删除所有空格,

 VAR2="${VAR2//[[:space:]]/}"

/替换//字符串中第一次出现的空格和所有出现的空格。即所有空白都被替换为–什么都没有


4

这是我见过的最简单的方法。它仅使用Bash,只有几行,regexp很简单,并且匹配所有形式的空格:

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

这是一个示例脚本,可用于测试:

test=$(echo -e "\n \t Spaces and tabs and newlines be gone! \t  \n ")

echo "Let's see if this works:"
echo
echo "----------"
echo -e "Testing:${test} :Tested"  # Ugh!
echo "----------"
echo
echo "Ugh!  Let's fix that..."

if [[ "$test" =~ ^[[:space:]]*([^[:space:]].*[^[:space:]])[[:space:]]*$ ]]
then 
    test=${BASH_REMATCH[1]}
fi

echo
echo "----------"
echo -e "Testing:${test}:Tested"  # "Testing:Spaces and tabs and newlines be gone!"
echo "----------"
echo
echo "Ah, much better."

1
当然比(例如,天哪!)更喜欢使用Python。除了我认为正确处理仅包含空格的字符串更简单,更通用。略微简化的表达式为:^[[:space:]]*(.*[^[:space:]])?[[:space:]]*$
Ron Burk

4

Python的功能strip()与PHP的功能相同trim(),因此我们可以做一些内联的Python来制作一个易于理解的实用程序:

alias trim='python -c "import sys; sys.stdout.write(sys.stdin.read().strip())"'

这将修剪开头和结尾的空格(包括换行符)。

$ x=`echo -e "\n\t   \n" | trim`
$ if [ -z "$x" ]; then echo hi; fi
hi

在可行的同时,您可能需要考虑提供一种解决方案,该解决方案不涉及启动完整的python解释器以修剪字符串。真是浪费。
pdwalker 2015年

3
#!/bin/bash

function trim
{
    typeset trimVar
    eval trimVar="\${$1}"
    read trimVar << EOTtrim
    $trimVar
EOTtrim
    eval $1=\$trimVar
}

# Note that the parameter to the function is the NAME of the variable to trim, 
# not the variable contents.  However, the contents are trimmed.


# Example of use:
while read aLine
do
    trim aline
    echo "[${aline}]"
done < info.txt



# File info.txt contents:
# ------------------------------
# ok  hello there    $
#    another  line   here     $
#and yet another   $
#  only at the front$
#$



# Output:
#[ok  hello there]
#[another  line   here]
#[and yet another]
#[only at the front]
#[]

3

我发现我需要从混乱的sdiff输出中添加一些代码以进行清理:

sdiff -s column1.txt column2.txt | grep -F '<' | cut -f1 -d"<" > c12diff.txt 
sed -n 1'p' c12diff.txt | sed 's/ *$//g' | tr -d '\n' | tr -d '\t'

这将删除尾随空格和其他不可见的字符。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.