我有一个包含多个单词的字符串,每两个单词之间至少有一个空格。如何将该字符串拆分为单独的单词,以便可以循环遍历它们?
这个字符串作为参数传递。例如:${2} == "cat cat file"
。如何循环遍历它?
此外,如何检查字符串是否包含空格?
我有一个包含多个单词的字符串,每两个单词之间至少有一个空格。如何将该字符串拆分为单独的单词,以便可以循环遍历它们?
这个字符串作为参数传递。例如:${2} == "cat cat file"
。如何循环遍历它?
此外,如何检查字符串是否包含空格?
我喜欢把它转换为数组,以便能够访问单个元素:
sentence="this is a story"
stringarray=($sentence)
现在您可以直接访问单个元素(从0开始):
echo ${stringarray[0]}
或者将其转换为字符串以便循环:
for i in "${stringarray[@]}"
do
:
# do whatever on $i
done
当然,直接遍历该字符串的解答之前已经回答过了,但该答案有一个缺点是不能跟踪后续使用的单个元素:
for i in $sentence
do
:
# do whatever on $i
done
另请参阅Bash数组参考。
touch NOPE; var='* a *'; arr=($var); set | grep ^arr=
输出了arr=([0]="NOPE" [1]="a" [2]="NOPE")
而不是预期的arr=([0]="*" [1]="a" [2]="*")
。 - Tino你是否尝试过直接将字符串变量传递给 for
循环?Bash 会自动按空格进行分割。
sentence="This is a sentence."
for word in $sentence
do
echo $word
done
This
is
a
sentence.
A=${A}${word}
。 - Lucas Jonestouch NOPE; var='* a *'; for a in $var; do echo "[$a]"; done
输出的结果是[NOPE] [a] [NOPE]
,而不是预期的[*] [a] [*]
(为了方便阅读,LF被替换为SPC)。 - Tino在BASH 3及以上版本中,最简单且最安全的方法可能是:
var="string to split"
read -ra arr <<<"$var"
(其中arr
是包含字符串分割部分的数组),或者,如果输入中可能有换行符,并且您想获得不止第一行:
var="string to split"
read -ra arr -d '' <<<"$var"
请注意-d ''
中的空格;不可省略,但这可能会导致您从<<<"$var"
获得意外换行符(因为它会在结尾隐式添加LF)。touch NOPE
var="* a *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done
输出预期结果
[*]
[a]
[*]
与所有先前的解决方案不同,此解决方案不容易受到意外和常常无法控制的shell扩展的影响。
而且这也为你提供了完整的IFS功能,正如你所期望的:
示例:
IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done
输出类似于:
[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]
正如您所见,空格也可以用这种方式保留:
IFS=: read -ra arr <<<' split : this '
for a in "${arr[@]}"; do echo "[$a]"; done
输出
[ split ]
[ this ]
请注意,BASH中的IFS
处理是一个独立的主题,因此请进行测试;一些有趣的主题包括:
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
输出
1 [this is]
2 [a test]
当......时
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
输出
1 [this]
2 [is]
3 [a]
4 [test]
顺便说一句:
如果你还不习惯$'ANSI-ESCAPED-STRING'
,那就要适应它吧;这样可以省很多时间。
如果你不加-r
(比如在read -a arr <<<"$var"
中),那么读取时会出现反斜杠转义。这个问题留给读者自己思考。
至于第二个问题:
我通常使用case
来测试字符串中是否包含某些内容,因为它可以同时检查多种情况(注意:case
只会执行第一个匹配的情况,如果需要继续执行,请使用多个case
语句),而且这种需求经常会遇到(题外话:pun intended):
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
因此,您可以将返回值设置为以下内容以检查SPC:
case "$var" in (*' '*) true;; (*) false;; esac
为什么选择case
?因为它通常比正则表达式更易读,并且由于Shell元字符的存在,它可以很好地处理99%的需求。
set -f
或 set -o noglob
来关闭 globbing,以便在此上下文中 shell 元字符不再造成影响。但我并不是真正的支持者,因为这会削弱 shell 的很多功能 / 在切换设置时非常容易出错。 - Tino;&
来实现。不太确定这个功能在哪个版本的 bash 中出现。我是一个4.3的用户。 - Sergiy Kolodyazhnyy;&
类似于C语言中强制执行无需模式检查的 fallthrough。还有;;&
,它只会继续进行其他模式检查。因此,;;
就像是if ..; then ..; else if ..
,而;;&
就像是if ..; then ..; fi; if ..
,其中;&
就像是m=false; if ..; then ..; m=:; fi; if $m || ..; then ..
- 一个人永远都在学习(从别人那里);) - Tino只需使用shell内置的“set”命令。例如:
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]
awk
,但是 set
更容易。现在我成了 set
的粉丝。谢谢 @Idelic! - Yzmir Ramireztouch NOPE; var='* a *'; set -- $var; for a; do echo "[$a]"; done
输出的是[NOPE] [a] [NOPE]
而不是预期的[*] [a] [*]
。 仅在101%确定分割后的字符串中没有SHELL元字符时才使用它!请注意Shell通配符。 - Tinoset -- $var
前使用 set -f
来禁用通配符展开,在执行完后再使用 set +f
来恢复通配符展开。 - Idelicset -f
,您的解决方案也很安全。但是 set +f
是每个 shell 的默认设置,因此这是一个必要的细节,必须注意到,因为其他人可能没有意识到它(就像我一样)。 - Tino$ echo "This is a sentence." | tr -s " " "\012"
This
is
a
sentence.
要检查空格,请使用grep:
$ echo "This is a sentence." | grep " " > /dev/null
$ echo $?
0
$ echo "Thisisasentence." | grep " " > /dev/null
$ echo $?
1
echo "X" |
通常可以被<<<"X"
替换,例如:grep -s " " <<<"This contains SPC"
。如果你做类似于echo X | read var
和read var <<< X
的操作,你会发现它们之间的区别。只有后者将变量var
导入到当前shell中,而要在第一种情况下访问它,你必须像这样分组:echo X | { read var; handle "$var"; }
。 - Tinoecho $WORDS | xargs -n1 echo
这会输出每个单词,您随后可以根据需要处理该列表。
npm install $(echo $NPM_PACKAGES | xargs -n1 echo) --save-dev
- Steve Moretz(A) 将句子按单词(以空格分隔)拆分,您可以直接使用默认的IFS,如下所示:
array=( $string )
示例:运行以下代码段
#!/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
将会输出
words counted: 8
this
is
the
"sentence"
'you'
want
to
split
正如你所看到的,你可以使用单引号或双引号,而不会有任何问题。
注:
-- 这基本上与mob的答案相同,但通过这种方式,您可以为任何进一步需要存储数组。 如果您只需要单个循环,则可以使用他的答案,它比这个短一行 :)
-- 请参考此问题以获取基于定界符拆分字符串的其他方法。
(B) 您还可以使用正则表达式匹配来检查字符串中的某个字符。
例如,要检查空格字符是否存在,您可以使用以下内容:
regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
then
echo "Space here!";
fi
$ echo foo bar baz | sed 's/ /\n/g'
foo
bar
baz
使用bash检查空格:
[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"
grep -oP '\w+' file