如何在bash中使用引号(例如命令参数)拆分字符串?


8

我有一个像这样的字符串:

"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"

我希望能够像这样拆分它:

aString that may haveSpaces IN IT
bar
foo
bamboo  
bam boo

我怎么做?(最好使用单线)



@DavidPostill问题实际上是完全不同的。
foxneSs 2016年

并非如此,这是相同的普遍问题。
DavidPostill

@DavidPostill-这是一个简单得多的问题:它需要的只是for l in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $l; done
AFH

@AFH哈哈。我刚刚发布了更长的答案。输出的唯一区别是我的保留了"s。我错过了OP在输出中不需要它们的事实。
DavidPostill

Answers:


3

当我看到David Postill的答案时,我以为“必须有一个更简单的解决方案”。经过一些实验,我发现以下作品:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo $string
eval 'for word in '$string'; do echo $word; done'

之所以有效,是因为在执行结果行(即内联答案)之前先eval展开行(除去引号并扩大string):

for word in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $word; done

扩展到同一行的替代方法是:

eval "for word in $string; do echo \$word; done"

此处string在双引号内将其展开,但$必须将其转义,以便word在执行该行之前不进行扩展(另一种形式,使用单引号具有相同的效果)。结果是:-

[~/]$ string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
[~/]$ echo $string
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
[~/]$ eval 'for word in '$string'; do echo $word; done'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo
[~/]$ eval "for word in $string; do echo \$word; done"
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

4

最简单的解决方案是使用带引号的参数数组,然后根据需要循环或直接传递给命令。

eval "array=($string)"

for arg in "${array[@]}"; do echo "$arg"; done   

ps如果您发现没有的更简单方法,请评论eval

编辑:

在@Hubbitus的答案的基础上,我们有一个经过完全消毒和正确引用的版本。注意:这是过大的杀伤力,实际上会在大多数标点符号之前在双引号或单引号部分中留下额外的反斜杠,但不受攻击。

declare -a "array=($( echo "$string" | sed 's/[][`~!@#$%^&*():;<>.,?/\|{}=+-]/\\&/g' ))"

我把它留给感兴趣的读者修改,因为他们认为合适http://ideone.com/FUTHhj


我有一个打破这种情况的测试用例:string="bash -c 'echo \$USER'"它“离开”了反斜杠。例如,您有时需要使用它ssh
Flamefire

2

我怎么做?

$ for l in "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"; do echo $l; done
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

如果我的字符串在bash变量中该怎么办?

使用bash字符串标记器的简单方法将不起作用,因为它会在每个空格上进行拆分,而不仅限于引号之外的空格:

DavidPostill@Hal /f/test
$ cat ./test.sh
#! /bin/bash
string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
for word in $string; do echo "$word"; done

DavidPostill@Hal /f/test
$ ./test.sh
"aString
that
may
haveSpaces
IN
IT"
bar
foo
"bamboo"
"bam
boo"

为了解决这个问题,以下shell脚本(splitstring.sh)显示了一种方法:

#! /bin/bash 
string=$(cat <<'EOF'
"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" 
EOF
)
echo Source String: "$string"
results=()
result=''
inside=''
for (( i=0 ; i<${#string} ; i++ )) ; do
    char=${string:i:1}
    if [[ $inside ]] ; then
        if [[ $char == \\ ]] ; then
            if [[ $inside=='"' && ${string:i+1:1} == '"' ]] ; then
                let i++
                char=$inside
            fi
        elif [[ $char == $inside ]] ; then
            inside=''
        fi
    else
        if [[ $char == ["'"'"'] ]] ; then
            inside=$char
        elif [[ $char == ' ' ]] ; then
            char=''
            results+=("$result")
            result=''
        fi
    fi
    result+=$char
done
if [[ $inside ]] ; then
    echo Error parsing "$result"
    exit 1
fi

echo "Output strings:"
for r in "${results[@]}" ; do
    echo "$r" | sed "s/\"//g"
done

输出:

$ ./splitstring.sh
Source String: "aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"
Output strings:
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

来源:StackOverflow的答案只能由在外面引号空格分割字符串choroba。脚本已进行了调整,以符合问题的要求。


2

您可以用declare代替eval,例如:

代替:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"'
echo "Initial string: $string"
eval 'for word in '$string'; do echo $word; done'

做:

declare -a "array=($string)"
for item in "${array[@]}"; do echo "[$item]"; done

但是请注意,如果输入来自用户,这并不安全!

因此,如果您尝试使用类似以下的字符串:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'

您得到了hostname评估(偏离路线可能像rm -rf /)!

非常非常简单的尝试来保护它,只是替换像反引号`和$这样的字符:

string='"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo" `hostname`'
declare -a "array=( $(echo $string | tr '`$<>' '????') )"
for item in "${array[@]}"; do echo "[$item]"; done

现在您得到如下输出:

[aString that may haveSpaces IN IT]
[bar]
[foo]
[bamboo]
[bam boo]
[?hostname?]

您可能会在一个很好的答案中找到有关方法和优缺点的更多详细信息:https : //stackoverflow.com/questions/17529220/why-should-eval-be-avoided-in-bash-and-what-should-i-使用,而不是/ 17529221#17529221

但是仍然有攻击的载体。 我非常希望在bash的字符串引用方法中像在双引号(“)中一样,但不解释content


0

使用awk

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);print $i} }'
aString that may haveSpaces IN IT
bar
foo
bamboo
bam boo

或将空格转换为“%20”或“ _”,以便可以由下一个命令throw pip处理:

echo '"aString that may haveSpaces IN IT" bar foo "bamboo" "bam boo"' | awk 'BEGIN {FPAT = "([^ ]+)|(\"[^\"]+\")"}{for(i=1;i<=NF;i++){gsub("\"","",$i);gsub(" ","_",$i)} print }'
aString_that_may_haveSpaces_IN_IT bar foo bamboo bam_boo

参考:Awk将双引号字符串视为一个标记,并忽略两者之间的空格

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.