我在Bash shell脚本中有一个字符串,我希望将其拆分为一个字符数组,而不是基于分隔符,而是每个数组索引只有一个字符。我怎样才能做到这一点? 理想情况下,它不会使用任何外部程序。让我重新说一下。我的目标是可移植性,因此可能在任何POSIX兼容系统上的sed
之类的东西都可以。
答案 0 :(得分:86)
尝试
echo "abcdefg" | fold -w1
编辑:在评论中添加了更优雅的解决方案。
echo "abcdefg" | grep -o .
答案 1 :(得分:28)
您可以单独访问每个字母而无需进行数组转换:
$ 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
或类似的东西,你可以使用上面的第一种技术结合使用原始字符串长度(${#foo}
)的while循环来构建数组。 / p>
警告:如果字符串包含空格,则以下代码不起作用。我认为Vaughn Cato's answer有更好的机会与特殊的角色一起生存。
thing=($(i=0; while [ $i -lt ${#foo} ] ; do echo ${foo:$i:1} ; i=$((i+1)) ; done))
答案 2 :(得分:9)
如果您的字符串存储在变量x中,则会生成一个包含单个字符的数组y:
i=0
while [ $i -lt ${#x} ]; do y[$i]=${x:$i:1}; i=$((i+1));done
答案 3 :(得分:7)
作为使用for / while循环迭代0 .. ${#string}-1
的替代方法,我可以考虑使用仅使用bash 执行此操作的另外两种方法:使用=~
并使用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
这种方法的作用是执行string
的扩展,用(.)
代替每个单个字符,然后将生成的正则表达式与分组相匹配,将每个字符捕获到BASH_REMATCH[]
。索引0设置为整个字符串,因为该特殊数组是只读的,您无法将其删除,请注意:1
当数组扩展为跳过索引0时(如果需要)。
对非平凡字符串(> 64个字符)的一些快速测试表明,该方法基本比使用bash字符串和数组操作的方法快。
以上内容适用于包含换行符的字符串,默认情况下=~
支持POSIX ERE where .
matches anything except NUL,即正则表达式编译时没有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中)而不是错误状态,则会更简单,而是使用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
答案 4 :(得分:2)
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]
。整个数组是[h e l l o 1 2 3 ]
。
答案 5 :(得分:2)
最简单,完整和优雅的解决方案:
$ read -a ARRAY <<< $(echo "abcdefg" | sed 's/./& /g')
并测试
$ echo ${ARRAY[0]}
a
$ echo ${ARRAY[1]}
b
解释:read -a
将stdin作为数组读取,并将其分配给变量ARRAY,将空格视为每个数组项的分隔符。
将字符串回显到sed的评估只是在每个字符之间添加所需的空格。
我们正在使用Here String(&lt;&lt;&lt;&lt;&lt;)来提供读取命令的标准输入。
答案 6 :(得分:2)
我发现以下方法效果最好:
array=( `echo string | grep -o . ` )
(注意反引号)
然后,如果您这样做:echo ${array[@]}
,
您会得到:s t r i n g
或:echo ${array[2]}
,
您会得到:r
答案 7 :(得分:1)
如果文本可以包含空格:
eval a=( $(echo "this is a test" | sed "s/\(.\)/'\1' /g") )
答案 8 :(得分:1)
$ echo hello | awk NF=NF FS=
h e l l o
或者
$ echo hello | awk '$0=RT' RS=[[:alnum:]]
h
e
l
l
o
答案 9 :(得分:0)
如果要将其存储在数组中,可以执行以下操作:
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
最后x
是必要的,以处理在$string
之后添加换行符的事实,如果它不包含换行符。
如果您想使用NUL分隔的字符,可以试试这个:
echo -n "$string" | while read -N 1
do
printf %s "$REPLY"
printf '\0'
done
答案 10 :(得分:0)
AWK非常方便:
a='123'; echo $a | awk 'BEGIN{FS="";OFS=" "} {print $1,$2,$3}'
其中FS
和OFS
是读取和打印输出的分隔符
答案 11 :(得分:0)
对于那些来到这里寻找如何在fish中执行此操作的人:
我们可以使用内置string
命令(自v2.3.0开始)进行字符串操作。
↪ string split '' abc
a
b
c
输出是一个列表,因此数组操作将起作用。
↪ for c in (string split '' abc)
echo char is $c
end
char is a
char is b
char is c
这是一个更复杂的例子,用索引迭代字符串。
↪ set --local chars (string split '' abc)
for i in (seq (count $chars))
echo $i: $chars[$i]
end
1: a
2: b
3: c
答案 12 :(得分:0)
作为对Alexandro de Oliveira的回应,我认为以下内容更优雅或更直观:
while read -r -n1 c ; do arr+=("$c") ; done <<<"hejsan"
答案 13 :(得分:0)
zsh解决方案:将标量string
放入arr
,它将是一个数组:
arr=(${(ps::)string})
答案 14 :(得分:0)
另一个问题:),所述问题只是说“将字符串拆分为字符数组”,并没有说太多关于接收数组的状态,也没有说太多关于特殊字符(如控制字符)的内容。< /p>
我的假设是,如果我想将一个字符串拆分为一个字符数组,我希望接收数组只包含该字符串,并且没有以前运行的剩余内容,但保留任何特殊字符。
例如提议的解决方案系列,如
for (( i=0 ; i < ${#x} ; i++ )); do y[i]=${x:i:1}; done
目标数组中有剩余。
$ y=(1 2 3 4 5 6 7 8)
$ x=abc
$ for (( i=0 ; i < ${#x} ; i++ )); do y[i]=${x:i:1}; done
$ printf '%s ' "${y[@]}"
a b c 4 5 6 7 8
除了每次我们想拆分问题时都写长行之外,为什么不将所有这些隐藏到一个函数中,我们可以保留一个包源文件,带有类似的 API
s2a "Long string" ArrayName
我得到了这个似乎可以胜任这项工作的人。
$ s2a()
> { [ "$2" ] && typeset -n __=$2 && unset $2;
> [ "$1" ] && __+=("${1:0:1}") && s2a "${1:1}"
> }
$ a=(1 2 3 4 5 6 7 8 9 0) ; printf '%s ' "${a[@]}"
1 2 3 4 5 6 7 8 9 0
$ s2a "Split It" a ; printf '%s ' "${a[@]}"
S p l i t I t