如何将一个字符串分割成多个字符串,由至少一个空间在bash shell分开?

我有一个包含许多单词的字符串,每个单词之间至少有一个空格。我如何把字符串分割成单独的单词,这样我就可以循环遍历它们?

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

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

536216 次浏览

你试过直接将字符串变量传递给for循环吗?例如,Bash将自动对空白进行分割。

sentence="This is   a sentence."
for word in $sentence
do
echo $word
done

,

This
is
a
sentence.
$ 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

使用bash检查空格:

[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"

只要使用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]

要将一个句子分成它的单词(空格分隔),你可以简单地使用默认的IFS

array=( $string )
< p > < br > 使用实例运行以下代码段

#!/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
如你所见,你也可以使用单引号或双引号,没有任何问题 < br > < br > 注: < br > ——这基本上与暴徒的答案相同,但以这种方式存储数组以供进一步需要。如果你只需要一个循环,你可以使用他的答案,这是一个短一行:)
——请参考这个问题获取基于分隔符分割字符串的替代方法。 < br > < br > < br > (B)要检查字符串中的某个字符,你也可以使用正则表达式匹配 示例检查空格字符的存在,您可以使用:

regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
then
echo "Space here!";
fi

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

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数组参考

在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]
[*]

因为这个解决方案(与前面的所有解决方案相反)不容易出现意外的、通常不可控的外壳通配符。

同时,这也为你提供了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"中),那么read执行反斜杠转义。这是留给读者的练习。


关于第二个问题:

为了测试字符串中的某些内容,我通常坚持使用case,因为这可以一次检查多个case(注意:case只执行第一个匹配,如果你需要使用多个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

所以你可以像这样设置返回值来检查SPC:

case "$var" in (*' '*) true;; (*) false;; esac

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

echo $WORDS | xargs -n1 echo

这将输出每个单词,您可以处理该列表,因为您认为以后合适。

对于我的用例,最好的选择是:

grep -oP '\w+' file

基本上,这是一个匹配连续非空格字符的正则表达式。这意味着任何类型和任何数量的空白都不匹配。-o参数在不同的行上输出每个匹配的单词。

另一个例子(使用Perl):

$ echo foo bar baz | perl -nE 'say for split /\s/'
foo
bar
baz

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

foo
bar
baz