String Bash:将字符串拆分为字符数组
我在bashshell脚本中有一个字符串,我希望将其拆分为一个字符数组,而不是基于分隔符,而是每个数组索引只有一个字符。我该怎么做?理想情况下,它不会使用任何外部程序。让我换一种说法。我的目标是可移植性,因此任何POSIX兼容系统上都可以使用的String Bash:将字符串拆分为字符数组,string,bash,String,Bash,我在bashshell脚本中有一个字符串,我希望将其拆分为一个字符数组,而不是基于分隔符,而是每个数组索引只有一个字符。我该怎么做?理想情况下,它不会使用任何外部程序。让我换一种说法。我的目标是可移植性,因此任何POSIX兼容系统上都可以使用的sed。您可以单独访问每个字母,而无需进行数组转换: $ foo="bar" $ echo ${foo:0:1} b $ echo ${foo:1:1} a $ echo ${foo:2:1} r 如果这还不够,您可以使用以下内容: $ bar=($(e
sed
。您可以单独访问每个字母,而无需进行数组转换:
$ 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循环来构建数组
警告:如果字符串包含空格,则下面的代码无效。我认为有了特殊的角色,生存的机会会更大
thing=($(i=0; while [ $i -lt ${#foo} ] ; do echo ${foo:$i:1} ; i=$((i+1)) ; done))
若字符串存储在变量x中,则会生成一个包含单个字符的数组y:
i=0
while [ $i -lt ${#x} ]; do y[$i]=${x:$i:1}; i=$((i+1));done
试一试
编辑:添加了评论中建议的更优雅的解决方案
如果要将其存储在阵列中,可以执行以下操作:
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
AWK非常方便:
a='123'; echo $a | awk 'BEGIN{FS="";OFS=" "} {print $1,$2,$3}'
其中
FS
和OFS
是读入和打印的分隔符,如果文本可以包含空格:
eval a=( $(echo "this is a test" | sed "s/\(.\)/'\1' /g") )
或
作为迭代
0${#string}-1
对于for/while循环,我可以想到另外两种方法来实现这一点:使用=~
和使用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
其工作方式是执行字符串
的扩展,将每个字符替换为()
,然后将生成的正则表达式与分组匹配,以将每个字符捕获到BASH\u REMATCH[]
。索引0设置为整个字符串,因为该特殊数组是只读的,您无法将其删除,请注意,如果需要,当数组展开以跳过索引0时,会出现:1
。
对非平凡字符串(>64个字符)的一些快速测试表明,该方法比使用bash字符串和数组操作的方法要快得多
以上内容将用于包含换行符的字符串,默认情况下,=~
支持,即在编译正则表达式时不使用REG\u换行符
。(默认情况下,POSIX文本处理实用程序在这方面的行为是不同的,并且通常是不同的。)
第二个选项,使用printf
:
string="wonkabars"
ii=0
while printf "%s%n" "${string:ii++:1}" xx; do
((xx)) && printf "\n" || break
done
此循环增加索引ii
,一次打印一个字符,并在没有剩余字符时中断。如果bashprintf
返回打印的字符数(如在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
arr=(${(ps::)string})
数组的零元素是
[h]
。整个阵列是最简单、完整和优雅的解决方案:
$ read -a ARRAY <<< $(echo "abcdefg" | sed 's/./& /g')
说明:read-a
将stdin作为数组读取,并将其分配给变量数组,将空格作为每个数组项的分隔符
将字符串回显到sed的计算只是在每个字符之间添加所需的空格
我们正在使用()为那些登陆这里的人搜索如何在: 我们可以使用内置的
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
如果还需要支持带换行符的字符串,可以执行以下操作:
str2arr(){ local string="$1"; mapfile -d $'\0' Chars < <(for i in $(seq 0 $((${#string}-1))); do printf '%s\u0000' "${string:$i:1}"; done); printf '%s' "(${Chars[*]@Q})" ;}
string=$(printf '%b' "apa\nbepa")
declare -a MyString=$(str2arr "$string")
declare -p MyString
# prints declare -a MyString=([0]="a" [1]="p" [2]="a" [3]=$'\n' [4]="b" [5]="e" [6]="p" [7]="a")
str2arr(){local string=“$1”;映射文件-d$'\0'字符<解决方案:将标量string
变量放入arr
,该变量将是一个数组:
declare -a arr
ii=0
while printf -v cc "%s%n" "${string:(ii++):1}" xx; do
((xx)) && arr+=("$cc") || break
done
arr=(${(ps::)string})
我发现以下方法效果最好:
array=( `echo string | grep -o . ` )
(请注意背面标记)
如果您这样做:echo${array[@]}
,
您将获得:s t r i n g
或者:echo${array[2]}
,
你会得到:r
还有一个关于:),上面提到的问题只是说“将字符串拆分为字符数组”,没有说太多关于接收数组的状态,也没有说太多关于特殊字符,比如和控制字符
我的假设是,如果我想将一个字符串拆分为一个字符数组,我希望接收数组只包含该字符串,而不包含以前运行的剩余字符,同时保留任何特殊字符
thing=($(i=0; while [ $i -lt ${#foo} ] ; do echo ${foo:$i:1} ; i=$((i+1)) ; done))
例如,建议的解决方案族
for (( i=0 ; i < ${#x} ; i++ )); do y[i]=${x:i:1}; done
我找到了一个似乎能胜任这项工作的
$ 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
尽管使用了一个外部命令,+1因为简洁。有相当优雅的建议echo“abcdefg”grep-o.
@xdazz它在Unicode上不起作用。试试这个echo“fold”w1
它可以打印空格和问号。然而@tripleee的解决方案echo“fold”w1| grep-o.
确实很好用。有趣的是,小程序没有通过:)。无论如何,谢谢你优雅的回答。@OmarIthawi谢谢,把它添加到了答案中。@OmarIthawi:这两种变体都适用于我,在Mac OS X和Linux CentOS 6.5上,所以看起来不像“折叠解决方案不适用于u”那么简单
while read -r -n1 c ; do arr+=("$c") ; done <<<"hejsan"
arr=(${(ps::)string})
array=( `echo string | grep -o . ` )
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
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