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
Bash: Split string into character array
提问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 sed
that 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 sed
or 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}-1
with 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 eval
and 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 string
which 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 :1
when 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 ii
to print one character at a time, and breaks out when there are no characters left. This would be even simpler if the bash printf
returned the number of character printed (as in C) rather than an error status, instead the number of characters printed is captured in xx
using %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 var
you 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 -a
reads 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 x
is necessary to handle the fact that a newline is appended after $string
if 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 FS
and OFS
is delimiter for read-in and print-out
其中FS
andOFS
是读入和打印的分隔符