如何将一个字符串拆分为由bash shell中至少一个空格分隔的多个字符串?

der*_*dji 204 string bash shell split

我有一个包含许多单词的字符串,每两个单词之间至少有一个空格.如何将字符串拆分为单个单词,以便循环使用它们?

该字符串作为参数传递.例如${2} == "cat cat file".我怎么能循环呢?

另外,如何检查字符串是否包含空格?

小智 273

我喜欢转换为数组,以便能够访问单个元素:

sentence="this is a story"
stringarray=($sentence)
Run Code Online (Sandbox Code Playgroud)

现在你可以直接访问各个元素(从0开始):

echo ${stringarray[0]}
Run Code Online (Sandbox Code Playgroud)

或转换回字符串以循环:

for i in "${stringarray[@]}"
do
  :
  # do whatever on $i
done
Run Code Online (Sandbox Code Playgroud)

当然,之前直接循环遍历字符串,但是这个答案的缺点是不能跟踪单个元素供以后使用:

for i in $sentence
do
  :
  # do whatever on $i
done
Run Code Online (Sandbox Code Playgroud)

另请参见Bash阵列参考.

  • 可悲的是不太完美,因为贝壳泡沫:`触摸NOPE; var ='*a*'; ARR =($变种); 设置| grep ^ arr =`outputs`arr =([0] ="NOPE"[1] ="a"[2] ="NOPE")`而不是预期的`arr =([0] ="*"[1 ] ="a"[2] ="*")` (22认同)
  • @Tino:如果你不想让通配符干扰,那么只需将其关闭即可。该解决方案对于通配符也可以正常工作。我认为这是最好的方法。 (3认同)
  • @Alexandros我的方法是仅使用模式,这些模式默认情况下是安全的,并且可以在每个上下文中完美地工作。更改外壳程序球以获得安全解决方案的要求不仅是非常危险的道路,而且已经是阴暗的一面。所以我的建议是永远不要习惯在这里使用这种模式,因为迟早您会忘记一些细节,然后有人会利用您的错误。您可以在媒体上找到此类漏洞的证据。每一个 单。天。 (2认同)

mob*_*mob 259

您是否尝试将字符串变量传递给for循环?例如,Bash将自动拆分空白.

sentence="This is   a sentence."
for word in $sentence
do
    echo $word
done
Run Code Online (Sandbox Code Playgroud)

 

This
is
a
sentence.
Run Code Online (Sandbox Code Playgroud)

  • 诀窍是不在for命令中引用变量. (85认同)
  • 实际上这个技巧不仅是一个错误的解决方案,而且由于shell globbing,它也是**非常危险的**.`触摸NOPE; var ='*a*'; for $ var; 回声"[$ a]"; 完成`输出`[NOPE] [a] [NOPE]`而不是预期的`[*] [a] [*]`(为了便于阅读,LF被SPC取代). (27认同)
  • 你可以将它附加到一个变量:`A = $ {A} $ {word})`. (4认同)

Ide*_*lic 84

只需使用内置的"set"shell.例如,

set $text

之后,$ text中的单个单词将分别为1美元,2美元,3美元等.对于稳健性,通常会这样做

set -- junk $text
shift

处理$ text为空或以破折号开头的情况.例如:

text="This is          a              test"
set -- junk $text
shift
for word; do
  echo "[$word]"
done

这打印

[This]
[is]
[a]
[test]

  • 如果你做这些事情,请注意shell globbing:`touch NOPE; var ='*a*'; set - $ var; 为一个; 回声"[$ a]"; 完成`输出`[NOPE] [a] [NOPE]`而不是预期的`[*] [a] [*]`.**只有在101%确定分裂字符串中没有SHELL元字符时才使用它!** (19认同)
  • 这是拆分var的一种很好的方法,可以直接访问各个部分.+1; 解决了我的问题 (5认同)
  • @Tino:这个问题适用于所有地方,不仅仅是在这里,但在这种情况下,您可以在`set - $ var`之前设置-f`,然后再设置`set + f`来禁用globbing. (4认同)
  • @Idelic:好抓.使用`set -f`,您的解决方案也是安全的.但是`set + f`是每个shell的默认值,所以它是必不可少的细节,必须注意,因为其他人可能都不知道它(就像我一样). (3认同)

Tin*_*ino 65

BASH 3及以上版本中最简单,最安全的方法是:

var="string    to  split"
read -ra arr <<<"$var"
Run Code Online (Sandbox Code Playgroud)

(arr获取字符串的拆分部分的数组在哪里),或者,如果输入中可能有换行符,并且您想要的不仅仅是第一行:

var="string    to  split"
read -ra arr -d '' <<<"$var"
Run Code Online (Sandbox Code Playgroud)

(请注意空格-d '',它不能被遗漏),但这可能会给你一个意想不到的换行符<<<"$var"(因为这会隐含地在最后添加一个LF).

例:

touch NOPE
var="* a  *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done
Run Code Online (Sandbox Code Playgroud)

输出预期的

[*]
[a]
[*]
Run Code Online (Sandbox Code Playgroud)

因为这个解决方案(与此前的所有解决方案相比)不容易出现意外且通常无法控制的shell globbing.

这也为您提供了您可能想要的IFS的全部功能:

例:

IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done
Run Code Online (Sandbox Code Playgroud)

输出类似于:

[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]
Run Code Online (Sandbox Code Playgroud)

如您所见,空间也可以通过这种方式保留:

IFS=: read -ra arr <<<' split  :   this    '
for a in "${arr[@]}"; do echo "[$a]"; done
Run Code Online (Sandbox Code Playgroud)

输出

[ split  ]
[   this    ]
Run Code Online (Sandbox Code Playgroud)

请注意,IFSBASH 中的处理是一个独立的主题,所以你的测试,一些有趣的主题:

  • unset IFS:忽略SPC,TAB,NL的运行并在线开始和结束
  • IFS='':没有字段分离,只读取所有内容
  • IFS=' ':SPC运行(仅限SPC)

最后一个例子

var=$'\n\nthis is\n\n\na test\n\n'
IFS=$'\n' read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done
Run Code Online (Sandbox Code Playgroud)

输出

1 [this is]
2 [a test]
Run Code Online (Sandbox Code Playgroud)

unset IFS
var=$'\n\nthis is\n\n\na test\n\n'
read -ra arr -d '' <<<"$var"
i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done
Run Code Online (Sandbox Code Playgroud)

输出

1 [this]
2 [is]
3 [a]
4 [test]
Run Code Online (Sandbox Code Playgroud)

BTW:

  • 如果你不习惯$'ANSI-ESCAPED-STRING'习惯,那就省时了.

  • 如果你不包括-r(比如in read -a arr <<<"$var")那么read会做反斜杠转义.这留给读者练习.


对于第二个问题:

为了测试字符串中的某些东西,我通常会坚持case,因为这可以同时检查多个案例(注意:case只执行第一个匹配,如果你需要使用falllce case语句),这种需要经常是这样的(双关语)意):

case "$var" in
'')                empty_var;;                # variable is empty
*' '*)             have_space "$var";;        # have SPC
*[[:space:]]*)     have_whitespace "$var";;   # have whitespaces like TAB
*[^-+.,A-Za-z0-9]*) have_nonalnum "$var";;    # non-alphanum-chars found
*[-+.,]*)          have_punctuation "$var";;  # some punctuation chars found
*)                 default_case "$var";;      # if all above does not match
esac
Run Code Online (Sandbox Code Playgroud)

所以你可以设置返回值来检查SPC,如下所示:

case "$var" in (*' '*) true;; (*) false;; esac
Run Code Online (Sandbox Code Playgroud)

为什么case?因为它通常比正则表达式序列更具可读性,并且由于Shell元字符,它可以很好地处理所有需求的99%.

  • 由于突出显示的问题及其全面性,因此该答案值得更多投票 (2认同)
  • 美妙的答案,的确值得更多的赞扬。附带说明的情况-您可以使用`;&`实现。不太确定出现在哪个版本的bash中。我是4.3位使用者 (2认同)
  • @Serg感谢您的注意,因为我还不知道!所以我查了一下,它出现在[Bash4](http://www.tldp.org/LDP/abs/html/bashver4.html)中。像C中一样,`;&'是没有模式检查的强制失败。此外,还有;;;&`继续进行进一步的模式检查。所以`;;`就像`if ..; 然后 ..; else ..`和`;;&`就像`if ..; 然后 ..; fi; 如果..`,则`;&`类似于`m = false;。如果..; 然后 ..; m = :; fi; 如果$ m || ..; 然后..`-一个人永远不会停止向他人学习;) (2认同)
  • 对于不熟悉 bash 数组变量的人来说,如果您回显数组变量并希望看到数组的内容,您将只能看到第一个元素,因此这可能看起来无法正常工作。使用 echo "${ARRAY[*]}" 查看内容。 (2认同)

DVK*_*DVK 42

$ echo "This is   a sentence." | tr -s " " "\012"
This
is
a
sentence.
Run Code Online (Sandbox Code Playgroud)

要检查空格,请使用grep:

$ echo "This is   a sentence." | grep " " > /dev/null
$ echo $?
0
$ echo "Thisisasentence." | grep " " > /dev/null     
$ echo $?
1
Run Code Online (Sandbox Code Playgroud)

  • 这似乎是一种更不通常的做事方式 (5认同)

Luc*_*one 16

(A)要将句子分成单词(空格分隔),您只需使用默认的IFS即可

array=( $string )
Run Code Online (Sandbox Code Playgroud)


运行以下代码段的示例

#!/bin/bash

sentence="this is the \"sentence\"   'you' want to split"
words=( $sentence )

len="${#words[@]}"
echo "words counted: $len"

printf "%s\n" "${words[@]}" ## print array
Run Code Online (Sandbox Code Playgroud)

将输出

words counted: 8
this
is
the
"sentence"
'you'
want
to
split
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,您也可以使用单引号或双引号而没有任何问题

注意:
- 这与mob的答案基本相同,但是通过这种方式您可以存储数组以满足任何进一步的需求.如果你只需要一个循环,你可以使用他的答案,这是一行更短:)
- 请参考这个问题,以获得基于分隔符拆分字符串的替代方法.


(B)要检查字符串中的字符,您还可以使用正则表达式匹配.
检查是否存在可以使用的空格字符的示例:

regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
    then
        echo "Space here!";
fi
Run Code Online (Sandbox Code Playgroud)


Ále*_*lex 12

echo $WORDS | xargs -n1 echo
Run Code Online (Sandbox Code Playgroud)

这会输出每个单词,之后您可以根据需要处理该列表。


小智 10

$echo foo bar baz | sed 's/ /\n/g'

foo
bar
baz
Run Code Online (Sandbox Code Playgroud)


gle*_*man 6

仅使用bash检查空格:

[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"
Run Code Online (Sandbox Code Playgroud)