作为使用 for/while 循环迭代 0 .. ${#string}-1 的替代方法,我还可以考虑使用其他两种方法仅使用 bash:使用 =~ 和使用 @987654324 @。 (还有第三种可能使用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
其工作方式是执行string 的扩展,将(.) 替换为每个单个字符,然后将此生成的正则表达式与分组相匹配,以将每个单独的字符捕获到BASH_REMATCH[] 中。索引 0 设置为整个字符串,因为该特殊数组是只读的,您无法将其删除,如果需要,请注意当数组扩展以跳过索引 0 时的 :1。
对重要字符串(>64 个字符)的一些快速测试表明,此方法大大比使用 bash 字符串和数组操作的方法快。
以上内容适用于包含换行符的字符串,=~ 默认支持POSIX ERE where . matches anything except NUL,即编译正则表达式时没有REG_NEWLINE。 (POSIX文本处理utilities的行为在这方面默认是允许不同的,通常是这样。)
第二个选项,使用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 中捕获打印的字符数。 (这至少可以追溯到 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