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中所示),而不是错误状态,则这将更简单,而使用%n
在xx
捕获打印的字符%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}'
FS
和OFS
是分隔符,用于读入和打印