繁体   English   中英

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

[英]How to split one string into multiple strings separated by at least one space in bash shell?

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

字符串作为参数传递。 例如${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 数组参考

您是否尝试将字符串变量传递给for循环? 一方面,Bash 会自动在空白处拆分。

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

This
is
a
sentence.

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 globbing。

这也为您提供了您可能想要的 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 "$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 $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]
$ 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

(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 $WORDS | xargs -n1 echo

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

仅使用 bash 检查空格:

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

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

foo
bar
baz

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

grep -oP '\w+' file

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

对此的另一种看法(使用 Perl):

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM