关于字符串:如何从bash变量中删除空格?

How to trim whitespace from a Bash variable?

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

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

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

  • 有没有一种简单的方法可以从$var中去除空白(就像PHP中的trim())?

  • 有没有处理这个问题的标准方法?

我可以使用SED或AWK,但我想有一个更优雅的解决方案来解决这个问题。


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

1
2
3
4
5
FOO=' test test test '
echo -e"FOO='${FOO}'"
# > FOO=' test test test '
echo -e"length(FOO)==${#FOO}"
# > length(FOO)==16

如何删除所有空白(用tr中的[:space:]表示):

1
2
3
4
5
6
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

如何仅删除前导空格:

1
2
3
4
5
6
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

如何仅删除尾随空格:

1
2
3
4
5
6
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:

1
2
3
4
5
6
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支持它,您可以用sed ... <<<${FOO}替换echo -e"${FOO}" | sed ...,就像这样(对于尾随空格):

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


一个简单的答案是:

1
echo"   lol " | xargs

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

注意:这不会删除内部空间,因此"foo bar"保持不变。它不会变成"foobar"


有一种解决方案只使用bash内置的通配符:

1
2
3
4
5
6
var="    abc   "
# remove leading whitespace characters
var="${var#"${var%%[![:space:]]*}"}"
# remove trailing whitespace characters
var="
${var%"${var##*[![:space:]]}"}"  
echo"===$var==="

以下是用函数包装的相同内容:

1
2
3
4
5
6
7
8
trim() {
    local var="$*"
    # remove leading whitespace characters
    var="${var#"${var%%[![:space:]]*}"}"
    # remove trailing whitespace characters
    var="
${var%"${var##*[![:space:]]}"}"  
    echo -n"$var"
}

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

1
trim"   abc  "

这个解决方案的一个好处是它可以与任何符合POSIX的shell一起工作。

参考文献

  • 从bash变量(原始源)中删除前导空格和尾随空格


bash有一个称为参数扩展的特性,除其他外,它允许基于所谓的模式(模式类似于正则表达式,但有基本的区别和限制)进行字符串替换。[Flussence的原始行:bash有正则表达式,但它们隐藏得很好:]

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

1
2
3
4
5
6
$ 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


去掉一个前导和一个尾随空格

1
2
3
4
5
6
7
8
9
10
11
trim()
{
    local trimmed="$1"

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

    echo"$trimmed"
}

例如:

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

输出:

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

去掉所有前导和尾随空格

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
trim()
{
    local trimmed="$1"

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

    echo"$trimmed"
}

例如:

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

输出:

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


为了删除字符串开头和结尾的所有空格(包括行尾字符):

1
echo $variable | xargs echo -n

这也将删除重复的空格:

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

生成:"此字符串有很多空格"


您可以使用echo简单地修剪:

1
2
3
4
5
6
7
8
9
10
foo=" qsdqsd qsdqs q qs  "

# Not trimmed
echo \'$foo\'

# Trim
foo=`echo $foo`

# Trimmed
echo \'$foo\'


从bash指南的globbing部分

在参数扩展中使用extglob

1
2
3
4
5
6
 #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

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

1
2
3
4
5
6
7
8
9
10
11
12
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.
}

用途:

1
2
3
4
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的情况下对其进行设置。这大大简化了功能。我还"就地"更新位置参数,这样我甚至不需要局部变量。

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

所以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ s=$'\t
 
\tfoo  '

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


启用了bash的扩展模式匹配功能(shopt -s extglob后,您可以使用此功能:

{trimmed##*( )}

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


您可以使用tr删除新行:

1
2
3
4
5
var=`hg st -R"$path" | tr -d '
'
`
if [ -n $var ]; then
    echo $var
done


我一直和塞德在一起

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

如果有更优雅的解决方案,我希望有人贴出来。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 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 $'

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


你可以用老派的tr。例如,这将返回Git存储库中修改过的文件数,其中的空白区被去除。

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


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

  • 它在shell bash/dash/busybox shell中成功测试
  • 它非常小
  • 它不依赖外部命令,也不需要分叉(->快速和低资源使用率)
  • 按预期工作:
    • 它从开始和结束处删除所有空格和制表符,但不超过
    • 重要提示:它不会从字符串中间删除任何内容(许多其他答案都会删除),即使换行也会保留
    • 特殊:"$*"使用一个空格连接多个参数。如果只想修剪和输出第一个参数,请使用"$1"
    • 如果在匹配文件名模式等方面没有任何问题

剧本:

1
2
3
4
5
6
7
8
trim() {
  local s2 s="$*"
  # note: the brackets in each of the following two lines contain one space
  # and one tab
  until s2="${s#[   ]}"; ["$s2" ="$s" ]; do s="$s2"; done
  until s2="${s%[   ]}"; ["$s2" ="$s" ]; do s="$s2"; done
  echo"$s"
}

用途:

1
2
3
4
mystring="   here     is
    something   "

mystring=$(trim"$mystring")
echo">$mystring<"

输出:

1
2
>here     is
    something<


这对我很有用:

1
2
3
4
5
6
7
8
9
10
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>

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

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


我已经看到脚本只使用变量赋值来完成任务:

1
2
3
4
$ xyz=`echo -e 'foo
 bar'
`
$ echo $xyz
foo bar

空白将自动合并和修剪。必须注意外壳元特征(潜在注入风险)。

我还建议在shell条件中始终重复引用变量替换:

1
if [ -n"$var" ]; then

因为变量中的-o或其他内容可以修改您的测试参数。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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")")"
}

1
2
3
4
5
6
7
8
9
10
11
12
# Strip leading and trailing specified characters.  ex: str=$(trim"$str" $'
 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]}"
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Strip leading specified characters.  ex: str=$(ltrim"$str" $'
 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" $'

 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" $'

 a')
trim(){
    printf"%s""$(rtrim"$(ltrim"$1""$2")""$2")"
}

建立在Moskit的expr解决方案之上…

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 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")")"
}

1
2
var='   a b c   '
trimmed=$(echo $var)


我只需要使用sed:

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

a)单线串使用示例

1
2
3
4
5
string='    wordA wordB  wordC   wordD    '
trimmed=$( trim"$string" )

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

输出:

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

b)多线串使用示例

1
2
3
4
5
6
7
8
string='    wordA
   >wordB<
wordC    '

trimmed=$( trim"$string" )

echo -e"GIVEN STRING: |$string|
"

echo"TRIMMED STRING: |$trimmed|"

输出:

1
2
3
4
5
6
7
GIVEN STRING: |    wordAA
   >wordB<
wordC    |

TRIMMED STRING: |wordAA
   >wordB<
wordC|

c)最后注意事项:如果您不喜欢使用函数,对于单行字符串,您可以简单地使用"易于记忆"命令,例如:

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

例子:

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

输出:

1
wordA wordB wordC

在多行字符串上使用上面的方法也可以,但是请注意,它也会减少任何尾随/前导的内部多空格,正如Gurum在注释中注意到的那样。

1
2
3
4
string='    wordAA
    >four spaces before<
 >one space before<    '

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

输出:

1
2
3
wordAA
>four spaces before<
>one space before<

所以,如果你介意保留这些空间,请使用我答案开头的函数!

d)在函数trim内使用的多行字符串上解释SED语法"查找和替换":

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
}'


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

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


使用AWK:

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


要从左到第一个单词删除空格和制表符,请输入:

1
echo"     This is a test" | sed"s/^[ \t]*//"

cyberciti.biz/tips/delete-leading-spaces-from-front-of-each-word.html


下面是一个trim()函数,用于修剪和规范化空白

1
2
3
4
5
6
7
#!/bin/bash
function trim {
    echo $*
}

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

以及另一个使用正则表达式的变体。

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
function trim {
    local trimmed="$@"
    if [["$trimmed" =~" *([^ ].*[^ ]) *" ]]
    then
        trimmed=${BASH_REMATCH[1]}
    fi
    echo"$trimmed"
}

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


这将删除字符串中的所有空白,

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

/替换了第一个出现点,//替换了字符串中出现的所有空格。也就是说,所有的空白区域都会被-无


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

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

下面是一个用来测试它的示例脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
test=$(echo -e"
 \t Spaces and tabs and newlines be gone! \t  
"
)

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."


这不存在不需要的全局设置问题,同时,内部空白也未被修改(假设$IFS设置为默认值,即' \t
'
)。

它读取到第一个换行符(不包括换行符)或字符串的末尾(以先到者为准),并去掉任何前导和尾随空格以及\t字符的混合。如果要保留多条线(同时删除前导和尾随新行),请使用read -r -d '' var << eof;但是请注意,如果您的输入恰好包含
eof
,它将在前面被切断。(其他形式的空白,即
\f\v,即使加在$ifs中,也不会去掉。)

1
2
3
read -r var << eof
$var
eof

我创建了以下函数。我不确定可移植的printf是如何的,但是这个解决方案的好处是,通过添加更多的字符代码,您可以精确地指定什么是"空白"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    iswhitespace()
    {
        n=`printf"%d
"
"'$1'"`
        if (( $n !="13" )) && (( $n !="10" )) && (( $n !="32" )) && (( $n !="92" )) && (( $n !="110" )) && (( $n !="114" )); then
            return 0
        fi
        return 1
    }

    trim()
    {
        i=0
        str="$1"
        while (( i < ${#1} ))
        do
            char=${1:$i:1}
            iswhitespace"$char"
            if ["$?" -eq"0" ]; then
                str="${str:$i}"
                i=${#1}
            fi
            (( i += 1 ))
        done
        i=${#str}
        while (( i >"0" ))
        do
            (( i -= 1 ))
            char=${str:$i:1}
            iswhitespace"$char"
            if ["$?" -eq"0" ]; then
                (( i += 1 ))
                str="${str:0:$i}"
                i=0
            fi
        done
        echo"$str"
    }

#Call it like so
mystring=`trim"$mystring"`

我发现我需要从杂乱的sdiff输出中添加一些代码,以便清理它:

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

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


trim()删除空格(和制表符,不可打印的字符;为了简单起见,我只考虑使用空格)。我的解决方案版本:

1
2
3
4
5
var="$(hg st -R"$path")" # I often like to enclose shell output in double quotes
var="$(echo"${var}" | sed"s/\(^ *\| *\$\)//g")" # This is my suggestion
if [ -n"
$var" ]; then
 echo"
[${var}]"
fi

"sed"命令只修剪前导空格和尾随空格,但它可以通过管道连接到第一个命令,并导致:

1
2
3
4
var="$(hg st -R"$path" | sed"s/\(^ *\| *\$\)//g")"
if [ -n"
$var" ]; then
 echo"
[${var}]"
fi

python有一个与php的trim()相同的函数strip(),因此我们只需做一点内联python,就可以轻松理解它的实用程序:

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

这将减少前导空格和尾随空格(包括换行符)。

1
2
3
4
5
$ x=`echo -e"
\t  
"
| trim`
$ if [ -z"$x" ]; then echo hi; fi
hi


用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
trim() {
    local orig="$1"
    local trmd=""
    while true;
    do
        trmd="${orig#[[:space:]]}"
        trmd="${trmd%[[:space:]]}"
        test"$trmd" ="$orig" && break
        orig="$trmd"
    done
    printf -- '%s
'
"$trmd"
}
  • 它适用于所有类型的空白,包括换行符,
  • 它不需要修改shopt。
  • 它保留了内部空白,包括换行符。

单元测试(手动审查):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#!/bin/bash

. trim.sh

enum() {
    echo"   a b c"
    echo"a b c  "
    echo"  a b c"
    echo" a b c "
    echo" a  b c "
    echo" a  b  c "
    echo" a      b  c "
    echo"     a      b  c "
    echo"     a  b  c "
    echo" a  b  c     "
    echo" a  b  c     "
    echo" a N b  c "
    echo"N a N b  c "
    echo" Na  b  c "
    echo" a  b  c N"
    echo" a  b  c  N"
}

xcheck() {
    local testln result
    while IFS='' read testln;
    do
        testln=$(tr N '
'
<<<"$testln")
        echo": ~~~~~~~~~~~~~~~~~~~~~~~~~ :">&2
        result="$(trim"$testln")"
        echo"testln='$testln'">&2
        echo"result='$result'">&2
    done
}

enum | xcheck

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/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]
#[]

IFS变量设置为其他变量时,我需要从脚本中删除空白。依靠Perl完成了这个技巧:

1
2
3
4
5
6
7
8
9
10
11
12
13
# trim() { echo $1; } # This doesn't seem to work, as it's affected by IFS

trim() { echo"$1" | perl -p -e 's/^\s+|\s+$//g'; }

strings="after --> , <-- before,  <-- both --> "

OLD_IFS=$IFS
IFS=","
for str in ${strings}; do
  str=$(trim"${str}")
  echo"str= '${str}'"
done
IFS=$OLD_IFS


这将修剪前端和末端的多个空间

whatever=${whatever%% *}

whatever=${whatever#* }


使用这个简单的bash参数扩展:

1
2
3
$ x=" a z     e r ty"
$ echo"START[${x// /}]END"
START[azerty]END


将空间移到一个空间:

1
(text) | fmt -su

1
2
3
4
var="  a b "
echo"$(set -f; echo $var)"

>a b

用途:

1
var=`expr"$var" :"^\ *\(.*[^ ]\)\ *$"`

它删除了前导和尾随空格,我相信这是最基本的解决方案。不是内置的bash,但"expr"是coreutils的一部分,因此至少不需要像sed或awk这样的独立实用程序。


另一种解决方案是单元测试,它从stdin中去掉$IFS,并与任何输入分隔符(甚至$'\0')一起工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
ltrim()
{
    # Left-trim $IFS from stdin as a single line
    # $1: Line separator (default NUL)
    local trimmed
    while IFS= read -r -d"${1-}" -u 9
    do
        if [ -n"${trimmed+defined}" ]
        then
            printf %s"$REPLY"
        else
            printf %s"${REPLY#"${REPLY%%[!$IFS]*}"}"
        fi
        printf"
${1-\x00}"
        trimmed=true
    done 9<&0

    if [[ $REPLY ]]
    then
        # No delimiter at last line
        if [ -n"
${trimmed+defined}" ]
        then
            printf %s"
$REPLY"
        else
            printf %s"
${REPLY#"${REPLY%%[!$IFS]*}"}"
        fi
    fi
}

rtrim()
{
    # Right-trim $IFS from stdin as a single line
    # $1: Line separator (default NUL)
    local previous last
    while IFS= read -r -d"${1-}" -u 9
    do
        if [ -n"${previous+defined}" ]
        then
            printf %s"$previous"
            printf"${1-\x00}"
        fi
        previous="$REPLY"
    done 9<&0

    if [[ $REPLY ]]
    then
        # No delimiter at last line
        last="$REPLY"
        printf %s"$previous"
        if [ -n"${previous+defined}" ]
        then
            printf"${1-\x00}"
        fi
    else
        last="$previous"
    fi

    right_whitespace="${last##*[!$IFS]}"
    printf %s"${last%$right_whitespace}"
}

trim()
{
    # Trim $IFS from individual lines
    # $1: Line separator (default NUL)
    ltrim ${1+"$@"} | rtrim ${1+"$@"}
}

虽然这不是严格的抨击,但它将做你想要做的,以及更多:

1
php -r '$x = trim("  hi there "); echo $x;'

如果您还想将其变为小写,请执行以下操作:

1
php -r '$x = trim("  Hi There "); $x = strtolower($x) ; echo $x;'

1
2
3
4
5
6
#Execute this script with the string argument passed in double quotes !!
#var2 gives the string without spaces.
#$1 is the string passed in double quotes
#!/bin/bash
var2=`echo $1 | sed 's/ \+//g'`
echo $var2