在 Bash 数组上使用参数替换

Jon*_*Red 9 bash array parameter shell-script variable-substitution

我有 file.txt 需要读入 Bash 数组。然后我需要删除每个条目中的空格、双引号和除第一个逗号之外的所有内容。这是我已经走了多远:

$ cat file.txt
10,this
2 0 , i s
30,"all"
40,I
50,n,e,e,d,2
60",s e,e"

$ cat script.sh
#!/bin/bash
readarray -t ARRAY<$1
ARRAY=( "${ARRAY[@]// /}" )
ARRAY=( "${ARRAY[@]//\"/}" )
for ELEMENT in "${ARRAY[@]}";do
    echo "|ELEMENT|$ELEMENT|"
done

$ ./script.sh file.txt
|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,n,e,e,d,2|
|ELEMENT|60,se,e|
Run Code Online (Sandbox Code Playgroud)

除了逗号情况外,这很好用。我知道有多种方法可以给这只猫剥皮,但由于脚本较大,这是其中的一部分,我真的很想使用参数替换来到达这里:

|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,need2|
|ELEMENT|60,see|
Run Code Online (Sandbox Code Playgroud)

这可以通过参数替换吗?

Kus*_*nda 10

据我所知,无需将其读入bash数组即可创建该输出:

$ sed 's/[ "]//g; s/,/ /; s/,//g; s/ /,/; s/.*/|ELEMENT|&|/' <file
|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,need2|
|ELEMENT|60,see|
Run Code Online (Sandbox Code Playgroud)

sed表达式删除空格和双引号,用空格替换第一个逗号(此时字符串中没有其他空格),删除所有其他逗号,恢复第一个逗号,并在前面加上并附加额外的数据。

或者,使用 GNU sed

sed 's/[ "]//g; s/,//2g; s/.*/|ELEMENT|&|/' <file
Run Code Online (Sandbox Code Playgroud)

(标准sed不支持组合2g作为s命令的标志)。

  • 并且,最后 2 个 s/// 命令可以是 `s/.*/|ELEMENT|&amp;|/` 但这对于 sed 来说可能更费力。 (2认同)

ter*_*don 9

sed 加载到数组之前,我会删除您需要删除的内容(还要注意小写的变量名称,通常最好避免在 shell 脚本中使用大写的变量):

#!/bin/bash
readarray -t array< <(sed 's/"//g; s/  *//g; s/,/"/; s/,//g; s/"/,/' "$1")
for element in "${array[@]}";do
    echo "|ELEMENT|$element|"
done
Run Code Online (Sandbox Code Playgroud)

这会在您的示例文件中生成以下输出:

$ foo.sh file 
|ELEMENT|10,this|
|ELEMENT|20,is|
|ELEMENT|30,all|
|ELEMENT|40,I|
|ELEMENT|50,need2|
|ELEMENT|60,see|
Run Code Online (Sandbox Code Playgroud)

如果您确实必须使用参数替换,请尝试以下操作:

#!/bin/bash
readarray -t array< "$1"
array=( "${array[@]// /}" )
array=( "${array[@]//\"/}" )
array=( "${array[@]/,/\"}" )
array=( "${array[@]//,/}" )
array=( "${array[@]/\"/,}" )

for element in "${array[@]}"; do
    echo "|ELEMENT|$element|"
done
Run Code Online (Sandbox Code Playgroud)


gle*_*man 9

ELEMENT='50,n,e,e,d,2'
IFS=, read -r first rest <<<"$ELEMENT"
printf "%s,%s\n" "$first" "${rest//,/}"
Run Code Online (Sandbox Code Playgroud)
50,need2
Run Code Online (Sandbox Code Playgroud)

改掉使用 ALLCAPS 变量名的习惯。您最终会与关键的“系统”变量(如 PATH)发生冲突并破坏您的代码。


ste*_*ver 8

[这本质上是格伦杰克曼答案的更完善的版本]

从剥离的键和值构建关联数组,使用第一个逗号作为分隔符:

declare -A arr
while IFS=, read -r k v; do arr["${k//[ \"]}"]="${v//[ ,\"]}"; done < file.txt
for k in "${!arr[@]}"; do 
  printf '|ELEMENT|%s,%s|\n' "$k" "${arr[$k]}"
done
|ELEMENT|20,is|
|ELEMENT|10,this|
|ELEMENT|50,need2|
|ELEMENT|40,I|
|ELEMENT|60,see|
|ELEMENT|30,all|
Run Code Online (Sandbox Code Playgroud)


Jef*_*ler 6

您可以遍历数组并使用中间变量:

for((i=0; i < "${#ARRAY[@]}"; i++))
do
  rest="${ARRAY[i]#*,}"
  ARRAY[i]="${ARRAY[i]%%,*}","${rest//,/}"
done
Run Code Online (Sandbox Code Playgroud)

这分配给rest第一个逗号之后的部分;然后我们将三个部分连接回原始变量:

  • 第一个逗号之前的部分
  • 逗号
  • 用空替换rest每个逗号