Bash:将string拆分为字符数组

我有一个Bash shell脚本中的string,我想分割成一个字符数组,而不是基于分隔符,但每个数组索引只有一个字符。 我该怎么做? 理想情况下,它不会使用任何外部程序。 让我重述一下。 我的目标是可移植性,因此可能在任何POSIX兼容系统上的sed等都可以。

尝试

 echo "abcdefg" | fold -w1 

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

 echo "abcdefg" | grep -o . 

您可以单独访问每个字母而不进行数组转换:

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

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

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

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

警告:如果string包含空格,则下面的代码不起作用。 我认为Vaughn Cato的答案在特殊字符存活下有更好的机会。

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

如果您的string存储在variablesx中,则会生成包含单个字符的数组y:

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

作为使用for / while循环遍历0 .. ${#string}-1的替代方法,我可以考虑另外两种方法来使用bash :using =~printf来执行此操作。 (使用eval{..}序列expression式还有第三种可能性,但是这个缺乏清晰度。)

在正确的环境和NLS启用bash的情况下,这些将可以使用非ASCII的希望,消除潜在的故障来源,如旧的系统工具,如sed ,如果这是一个问题。 这些将从bash-3.0(2005年发布)工作。

使用=~和正则expression式,将string转换为单个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 

这样做的方式是执行一个string的扩展,将每个单个字符replace为(.) ,然后将生成的正则expression式与分组匹配,以将每个单独的字符捕获到BASH_REMATCH[] 。 索引0被设置为整个string,因为该特殊数组是只读的,所以不能删除它,当数组被扩展为跳过索引0时(如果需要),请注意:1 。 一些非平凡的string(> 64个字符)的快速testing显示,这种方法比使用bashstring和数组操作的方法快得多。

以上将使用包含换行符的string, =~支持POSIX ERE其中. 匹配除NUL之外的任何内容 ,即正则expression式编译时没有REG_NEWLINE 。 (POSIX文本处理实用程序的行为在这方面默认是不同的,通常是这样。)

第二个选项,使用printf

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

这个循环递增索引ii ,一次打印一个字符,当没有字符时,打印出来。 如果bash printf返回打印的字符数(如C中所示),而不是错误状态,则这将更简单,而使用%nxx捕获打印的字符%n 。 (至less回到bash-2.05b。)

使用bash-3.1和printf -v var你可以有更多的灵活性,并且可以避免从string末尾掉下来,除非要打印字符,例如创build一个数组:

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

最简单,完整和优雅的解决scheme:

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

并testing

 $ echo ${ARRAY[0]} a $ echo ${ARRAY[1]} b 

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

将string回显给sed的评估只是在每个字符之间添加了需要的空格。

我们使用Here String (<<<)来提供读命令的stdin。

 $ echo hello | awk NF=NF FS= hello 

要么

 $ echo hello | awk '$0=RT' RS=[[:alnum:]] h e l l o 
 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[@]}]" 

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

如果你想存储这个数组,你可以这样做:

 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: foo echo "Array length: ${#chars[@]}" Array length: 3 

最后的x是处理这样一个事实,即在$string后面追加一个换行符(如果它不包含换行符)。

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

 echo -n "$string" | while read -N 1 do printf %s "$REPLY" printf '\0' done 

如果文本可以包含空格:

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

AWK相当方便:

 a='123'; echo $a | awk 'BEGIN{FS="";OFS=" "} {print $1,$2,$3}' 

FSOFS是分隔符,用于读入和打印