string Bash:将字符串拆分为字符数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7578930/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 01:13:45  来源:igfitidea点击:

Bash: Split string into character array

stringbash

提问by extropic-engine

I have a string in a Bash shell script that I want to split into an array of characters, not based on a delimiter but just one character per array index. How can I do this? Ideally it would not use any external programs.Let me rephrase that. My goal is portability, so things like sedthat are likely to be on any POSIX compatible system are fine.

我在 Bash shell 脚本中有一个字符串,我想将其拆分为一个字符数组,不是基于分隔符,而是每个数组索引只有一个字符。我怎样才能做到这一点?理想情况下,它不会使用任何外部程序。让我重新表述一下。我的目标是可移植性,因此sed在任何兼容 POSIX 的系统上都没有问题。

回答by xdazz

Try

尝试

echo "abcdefg" | fold -w1

Edit: Added a more elegant solution suggested in comments.

编辑:在评论中添加了一个更优雅的解决方案。

echo "abcdefg" | grep -o .

回答by Mat

You can access each letter individually already without an array conversion:

您可以在没有数组转换的情况下单独访问每个字母:

$ foo="bar"
$ echo ${foo:0:1}
b
$ echo ${foo:1:1}
a
$ echo ${foo:2:1}
r

If that's not enough, you could use something like this:

如果这还不够,你可以使用这样的东西:

$ bar=($(echo $foo|sed  's/\(.\)/ /g'))
$ echo ${bar[1]}
a

If you can't even use sedor something like that, you can use the first technique above combined with a while loop using the original string's length (${#foo}) to build the array.

如果您甚至不能使用sed或类似的东西,您可以使用上面的第一种技术结合使用原始字符串长度 ( ${#foo})的 while 循环来构建数组。

Warning:the code below does not work if the string contains whitespace. I think Vaughn Cato's answerhas a better chance at surviving with special chars.

警告:如果字符串包含空格,下面的代码将不起作用。我认为Vaughn Cato 的回答有更好的机会使用特殊字符生存。

thing=($(i=0; while [ $i -lt ${#foo} ] ; do echo ${foo:$i:1} ; i=$((i+1)) ; done))

回答by Vaughn Cato

If your string is stored in variable x, this produces an array y with the individual characters:

如果您的字符串存储在变量 x 中,则会生成一个包含单个字符的数组 y:

i=0
while [ $i -lt ${#x} ]; do y[$i]=${x:$i:1};  i=$((i+1));done

回答by mr.spuratic

As an alternative to iterating over 0 .. ${#string}-1with a for/while loop, there are two other ways I can think of to do this with only bash: using =~and using printf. (There's a third possibility using evaland a {..}sequence expression, but this lacks clarity.)

作为0 .. ${#string}-1使用 for/while 循环进行迭代的替代方法,我可以想到另外两种方法来使用bash执行此操作: using=~和 using printf。(使用eval{..}序列表达式还有第三种可能性,但这不够清晰。)

With the correct environment and NLS enabled in bash these will work with non-ASCII as hoped, removing potential sources of failure with older system tools such as sed, if that's a concern. These will work from bash-3.0 (released 2005).

在 bash 中启用正确的环境和 NLS 后,这些将按预期使用非 ASCII,消除使用旧系统工具(如 )的潜在故障源sed,如果这是一个问题。这些将从 bash-3.0(2005 年发布)开始工作。

Using =~and regular expressions, converting a string to an array in a single expression:

使用=~正则表达式,在单个表达式中将字符串转换为数组:

string="wonkabars"
[[ "$string" =~ ${string//?/(.)} ]]       # splits into array
printf "%s\n" "${BASH_REMATCH[@]:1}"      # loop free: reuse fmtstr
declare -a arr=( "${BASH_REMATCH[@]:1}" ) # copy array for later

The way this works is to perform an expansion of stringwhich substitutes each single character for (.), then match this generated regular expression with grouping to capture each individual character into BASH_REMATCH[]. Index 0 is set to the entire string, since that special array is read-only you cannot remove it, note the :1when the array is expanded to skip over index 0, if needed. Some quick testing for non-trivial strings (>64 chars) shows this method is substantiallyfaster than one using bash string and array operations.

其工作方式是执行扩展,string将每个单个字符替换为(.),然后将此生成的正则表达式与分组匹配以将每个单独的字符捕获到BASH_REMATCH[]. 索引 0 设置为整个字符串,因为该特殊数组是只读的,您无法删除它,请注意:1扩展数组以跳过索引 0(如果需要)的时间。对于非平凡字符串(> 64个字符)的一些快速测试表明,该方法是基本上快于一个使用bash串和阵列操作。

The above will work with strings containing newlines, =~supports POSIX ERE where .matches anything except NULby default, i.e. the regex is compiled without REG_NEWLINE. (The behaviour of POSIX text processing utilitiesis allowed to be different by default in this respect, and usually is.)

以上将适用于包含换行符的字符串,=~支持POSIX ERE,.默认情况下匹配除 NUL 之外的任何内容,即正则表达式编译时没有REG_NEWLINE. (在这方面,默认情况下允许POSIX 文本处理实用程序的行为不同,通常是。)

Second option, using printf:

第二种选择,使用printf

string="wonkabars"
ii=0
while printf "%s%n" "${string:ii++:1}" xx; do 
  ((xx)) && printf "\n" || break
done 

This loop increments index iito print one character at a time, and breaks out when there are no characters left. This would be even simpler if the bash printfreturned the number of character printed (as in C) rather than an error status, instead the number of characters printed is captured in xxusing %n. (This works at least back as far as bash-2.05b.)

此循环增加 indexii以一次打印一个字符,并在没有剩余字符时中断。如果 bashprintf返回打印的字符数(如在 C 中)而不是错误状态,而不是xx使用%n. (这至少可以追溯到 bash-2.05b。)

With bash-3.1 and printf -v varyou have slightly more flexibility, and can avoid falling off the end of the string should you be doing something other than printing the characters, e.g. to create an array:

使用 bash-3.1,printf -v var你有更多的灵活性,并且可以避免从字符串的末尾掉下来,如果你做的不是打印字符,比如创建一个数组:

declare -a arr
ii=0
while printf -v cc "%s%n" "${string:(ii++):1}" xx; do 
    ((xx)) && arr+=("$cc") || break
done

回答by Alexandro de Oliveira

The most simple, complete and elegant solution:

最简单、完整、优雅的解决方案:

$ read -a ARRAY <<< $(echo "abcdefg" | sed 's/./& /g')  

and test

并测试

$ echo ${ARRAY[0]}
  a

$ echo ${ARRAY[1]}
  b

Explanation: read -areads the stdin as an array and assigns it to the variable ARRAY treating spaces as delimiter for each array item.

说明read -a将 stdin 作为数组读取并将其分配给变量 ARRAY,将空格作为每个数组项的分隔符。

The evaluation of echoing the string to sed just add needed spaces between each character.

将字符串回显到 sed 的评估只是在每个字符之间添加所需的空格。

We are using Here String(<<<) to feed the stdin of the read command.

我们使用Here String(<<<) 来提供读取命令的标准输入。

回答by 0x00

string=hello123

for i in $(seq 0 ${#string})
    do array[$i]=${string:$i:1}
done

echo "zero element of array is [${array[0]}]"
echo "entire array is [${array[@]}]"

The zero element of array is [h]. The entire array is [h e l l o 1 2 3 ].

数组的零元素是[h]。整个数组是[h e l l o 1 2 3 ]

回答by Karoly Horvath

If the text can contain spaces:

如果文本可以包含空格:

eval a=( $(echo "this is a test" | sed "s/\(.\)/'' /g") )

回答by Steven Penny

$ echo hello | awk NF=NF FS=
h e l l o

Or

或者

$ echo hello | awk '
string=foo
unset chars
declare -a chars
while read -N 1
do
    chars[${#chars[@]}]="$REPLY"
done <<<"$string"x
unset chars[$((${#chars[@]} - 1))]
unset chars[$((${#chars[@]} - 1))]

echo "Array: ${chars[@]}"
Array: f o o
echo "Array length: ${#chars[@]}"
Array length: 3
=RT' RS=[[:alnum:]] h e l l o

回答by l0b0

If you want to store this in an array, you can do this:

如果要将其存储在数组中,可以执行以下操作:

echo -n "$string" | while read -N 1
do
    printf %s "$REPLY"
    printf '
a='123'; echo $a | awk 'BEGIN{FS="";OFS=" "} {print ,,}'
' done

The final xis necessary to handle the fact that a newline is appended after $stringif it doesn't contain one.

finalx是处理这样一个事实的必要条件,即$string如果它不包含一个换行符,则在它之后追加一个换行符。

If you want to use NUL-separated characters, you can try this:

如果你想使用 NUL 分隔的字符,你可以试试这个:

##代码##

回答by Tony Xu

AWK is quite convenient:

AWK 相当方便:

##代码##

where FSand OFSis delimiter for read-in and print-out

其中FSandOFS是读入和打印的分隔符