Bash:将字符串拆分为字符数组

77
我有一个 Bash shell 脚本中的字符串,我想将它分成一个字符的数组,不基于分隔符而是每个数组索引只有一个字符。我该如何做?最好不使用任何外部程序。让我重新表达一下,我的目标是可移植性,因此像 sed 这样的可能在任何 POSIX 兼容系统上都存在的东西都是可以接受的。

2
如果您的平台是POSIX,那么“bash”不是默认提供的。 - tripleee
3
@tripleee 这两个东西都不是数组。 - lhunath
当然。我正在努力理解这个问题。也许OP的意思是在一个其他POSIX系统上针对Bash? - tripleee
1
最初的意图是创建一个可以在不了解用户平台的情况下在线共享的shell脚本。因此,我希望尽可能地具有跨OS X、Ubuntu等多个操作系统的兼容性。我不需要与Unix的奇异变体100%兼容。 - n s
在bash中,您可以使用${string:1:1}提取单个字符,但如果您需要支持POSIX sh,则不能假设您拥有它,也不能假设您根本具有数组。 - Charles Duffy
显示剩余2条评论
20个回答

1
$ echo hello | awk NF=NF FS=
h e l l o

或者

$ echo hello | awk '$0=RT' RS=[[:alnum:]]
h
e
l
l
o

1
警告:使用空FS的结果会随awk实现而改变。 POSIX明确避免了这种情况:“1.-如果FS是空字符串,则行为未指定。” 更具体地说:这将失败:echo hello | original-awk NF=NF FS= - user2350426

1
如果文本可能包含空格:

eval a=( $(echo "this is a test" | sed "s/\(.\)/'\1' /g") )

2
使用 https://dev59.com/mmsz5IYBdhLWcg3w6MNS#7581114 中提供的信息来显示存储在数组“a”中的字符。像这样:eval a=( $(echo "this is a test" | sed "s/\(.\)/'\1' /g") );v=0; echo Array: "${a[@]}"; while [[ $v -lt ${#a[@]} ]];do echo -ne "$v:\t" ; echo ${a[$v]}; let v=v+1;done - Menachem

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

0

如果您还需要支持带有换行符的字符串,可以这样做:

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")

作为对Alexandro de Oliveira的回应,我认为以下代码更加优雅或者至少更加直观:
while read -r -n1 c ; do arr+=("$c") ; done <<<"hejsan"

0

我知道这是一个“bash”问题,但请允许我向您展示zsh的完美解决方案,这是一种非常流行的shell:

string='this is a string'
string_array=(${(s::)string})  #Parameter expansion. And that's it!

print ${(t)string_array}  -> type array
print $#string_array -> 16 items

0

zsh 解决方案:将标量string变量放入arr中,该变量将成为一个数组:

arr=(${(ps::)string})

0

AWK非常方便:

a='123'; echo $a | awk 'BEGIN{FS="";OFS=" "} {print $1,$2,$3}'

其中FSOFS是读入和输出的分隔符


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

0
declare -r some_string='abcdefghijklmnopqrstuvwxyz'
declare -a some_array
declare -i idx

for ((idx = 0; idx < ${#some_string}; ++idx)); do
  some_array+=("${some_string:idx:1}")
done

for idx in "${!some_array[@]}"; do
  echo "$((idx)): ${some_array[idx]}"
done

0

纯bash,无循环。

另一个解决方案,类似于/改编自 Léa Gris的解决方案,但使用read -a代替readarray/mapfile

#!/usr/bin/env bash

str='azerty'

# Need extglob for the replacement pattern
shopt -s extglob

# Split string characters into array
# ${str//?()/$'\x1F'} replace each character "c" with "^_c".
# ^_ (Control-_, 0x1f) is Unit Separator (US), you can choose another
# character.
IFS=$'\x1F' read -ra array <<< "${str//?()/$'\x1F'}"

# now, array[0] contains an empty string and the rest of array (starting
# from index 1) contains the original string characters :
declare -p array

# Or, if you prefer to keep the array "clean", you can delete
# the first element and pack the array :
unset array[0]
array=("${array[@]}")
declare -p array

然而,我更喜欢更短的方式(对我来说更容易理解),即在分配数组之前删除初始的0x1f

#!/usr/bin/env bash

str='azerty'
shopt -s extglob

tmp="${str//?()/$'\x1F'}"              # same as code above
tmp=${tmp#$'\x1F'}                     # remove initial 0x1f
IFS=$'\x1F' read -ra array <<< "$tmp"  # assign array

declare -p array                       # verification

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接