我在Bash shell脚本中有一个字符串,我希望将其拆分为一个字符数组,而不是基于分隔符,而是每个数组索引只有一个字符.我怎样才能做到这一点?理想情况下,它不会使用任何外部程序.让我重新说一下.我的目标是可移植性,所以类似的东西sed
很可能在任何POSIX兼容系统上都可以.
xda*_*azz 97
尝试
echo "abcdefg" | fold -w1
Run Code Online (Sandbox Code Playgroud)
编辑:在评论中添加了更优雅的解决方案.
echo "abcdefg" | grep -o .
Run Code Online (Sandbox Code Playgroud)
Mat*_*Mat 28
您可以单独访问每个字母而无需进行数组转换:
$ foo="bar"
$ echo ${foo:0:1}
b
$ echo ${foo:1:1}
a
$ echo ${foo:2:1}
r
Run Code Online (Sandbox Code Playgroud)
如果这还不够,你可以使用这样的东西:
$ bar=($(echo $foo|sed 's/\(.\)/\1 /g'))
$ echo ${bar[1]}
a
Run Code Online (Sandbox Code Playgroud)
如果你甚至不能使用sed
或类似的东西,你可以使用上面的第一个技术结合while循环使用原始字符串的长度(${#foo}
)来构建数组.
警告:如果字符串包含空格,则以下代码不起作用.我认为Vaughn Cato的回答有更好的机会与特殊的角色生存.
thing=($(i=0; while [ $i -lt ${#foo} ] ; do echo ${foo:$i:1} ; i=$((i+1)) ; done))
Run Code Online (Sandbox Code Playgroud)
Vau*_*ato 10
如果您的字符串存储在变量x中,则会生成一个包含单个字符的数组y:
i=0
while [ $i -lt ${#x} ]; do y[$i]=${x:$i:1}; i=$((i+1));done
Run Code Online (Sandbox Code Playgroud)
作为0 .. ${#string}-1
使用for/while循环迭代的替代方法,我还可以考虑使用bash执行此操作的另外两种方法:using =~
和using printf
.(第三种可能性使用eval
和{..}
序列表达式,但这个缺乏清晰度.)
使用正确的环境并在bash中启用NLS,这些将按照希望使用非ASCII,使用较旧的系统工具(例如sed
,如果这是一个问题)消除潜在的故障源.这些将适用于bash-3.0(2005年发布).
使用=~
和正则表达式,将字符串转换为单个表达式中的数组:
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
Run Code Online (Sandbox Code Playgroud)
其工作方式是执行扩展string
,替换每个单个字符(.)
,然后将此生成的正则表达式与分组相匹配,以捕获每个单独的字符BASH_REMATCH[]
.索引0设置为整个字符串,因为该特殊数组是只读的,您无法将其删除,请注意:1
扩展数组何时跳过索引0(如果需要).对于非平凡字符串(> 64个字符)的一些快速测试表明,该方法是基本上快于一个使用bash串和阵列操作.
以上将使用包含换行符的字符串,=~
支持POSIX ERE.
默认匹配NUL以外的任何内容,即正则表达式编译时没有REG_NEWLINE
.(默认情况下,POSIX文本处理实用程序的行为在这方面是允许的,通常是.)
第二种选择,使用printf
:
string="wonkabars"
ii=0
while printf "%s%n" "${string:ii++:1}" xx; do
((xx)) && printf "\n" || break
done
Run Code Online (Sandbox Code Playgroud)
此循环增加索引ii
以一次打印一个字符,并在没有字符时分解.如果bash printf
返回打印的字符数(如C中)而不是错误状态,则更简单,而是在xx
使用时捕获打印的字符数%n
.(这至少可以回到bash-2.05b.)
使用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
Run Code Online (Sandbox Code Playgroud)
The most simple, complete and elegant solution:
$ read -a ARRAY <<< $(echo "abcdefg" | sed 's/./& /g')
Run Code Online (Sandbox Code Playgroud)
and test
$ echo ${ARRAY[0]}
a
$ echo ${ARRAY[1]}
b
Run Code Online (Sandbox Code Playgroud)
Explanation: read -a
reads the stdin as an array and assigns it to the variable ARRAY treating spaces as delimiter for each array item.
The evaluation of echoing the string to sed just add needed spaces between each character.
We are using Here String (<<<) to feed the stdin of the read command.
我发现以下方法效果最好:
array=( `echo string | grep -o . ` )
Run Code Online (Sandbox Code Playgroud)
(注意反引号)
那么如果你这样做:echo ${array[@]}
,你会得到:s t r i n g
或:echo ${array[2]}
,你得到:r
无循环的纯 Bash 解决方案:
#!/usr/bin/env bash
str='The quick brown fox jumps over a lazy dog.'
# Need extglob for the replacement pattern
shopt -s extglob
# Split string characters into array (skip first record)
# Character 037 is the octal representation of ASCII Record Separator
# so it can capture all other characters in the string, including spaces.
IFS= mapfile -s1 -t -d $'\37' array <<<"${str//?()/$'\37'}"
# Strip out captured trailing newline of here-string in last record
array[-1]="${array[-1]%?}"
# Debug print array
declare -p array
Run Code Online (Sandbox Code Playgroud)