我需要用 bash 创建一个多维数组,我读到 bash 中没有多维数组之类的东西。
这是我可能的数据,它们是什么样的以及我需要什么。这不是 bash 数组:
DATACOL = [
"1"=>("Santiago","Barcelona","AWG","6792992","Male"),
"2"=>("Santi","Texas","BGG","6792992","Male"),
"3"=>("Tiago","Rio","GHA","6792992","Female")
]
我怎样才能用一个简单的脚本做类似的事情?我对 bash 完全是个新手。
答案1
你不知道。如果您发现自己需要诸如多维数组之类的东西,则强烈表明您应该使用实际的编程语言而不是 shell。 Shell 并不是真正的编程语言,尽管它们可以(滥用)用作一种语言,但这应该只用于简单的事情。不幸的是,许多人似乎认为 shell 是适用于所有情况的正确工具,这会导致浪费大量精力尝试做 shell 无法完成或可能做得非常糟糕的事情,而不是使用专为特定情况设计的工具。工作。
也就是说,你可以使用以下方法将一些东西组合在一起名称引用:
可以使用声明或本地内置命令(请参阅 Bash 内置命令)的 -n 选项为变量分配 nameref 属性,以创建 nameref 或对另一个变量的引用。这允许间接操纵变量。每当 nameref 变量被引用、赋值、取消设置或修改其属性(除了使用或更改 nameref 属性本身)时,实际上都会对 nameref 变量的值指定的变量执行操作。 nameref 通常在 shell 函数中使用来引用其名称作为参数传递给函数的变量。例如,如果将变量名作为第一个参数传递给 shell 函数,则运行
declare -n ref=$1
函数内部创建一个 nameref 变量 ref,其值是作为第一个参数传递的变量名称。对 ref 的引用和赋值以及对其属性的更改均被视为对名称作为 $1 传递的变量的引用、赋值和属性修改。
例如,像这样:
#!/bin/bash
data1=("Santiago" "Barcelona" "AWG" "6792992" "Male")
data2=("Santi" "Texas" "BGG" "6792992" "Male")
data3=("Tiago" "Rio" "GHA" "6792992" "Female")
datacol=("data1" "data2" "data3")
for arrayName in "${datacol[@]}"; do
declare -n array="$arrayName"
echo "The second element of the array '$arrayName' is: ${array[1]}"
done
其产生:
$ foo.sh
The second element of the array 'data1' is: Barcelona
The second element of the array 'data2' is: Texas
The second element of the array 'data3' is: Rio
它真的很复杂、脆弱,不值得付出努力。请改用真正的脚本语言。
答案2
在 Bash 中你做不到,没有技巧的话。不过,Ksh93 确实具有本机多维数组。
一种常见的技巧是使用关联数组 ( declare -A arr
),并使用 , 之类的键1,2
,多个索引之间用逗号分隔。尽管迭代单个“行”或“列”并不那么简单。这也是 AWK 实现多维数组的方式,参见例如GNU AWK 手册。
以克什为单位:
arr=((a b c) (d e f)) # 3x2
arr[2]=(g h i) # one more row
arr[0][2]=x # change a value
typeset -p arr
echo ---
for i in ${!arr[@]}; do # ${!arr[@]} gives the indexes
for j in ${!arr[i][@]}; do
echo -n "${arr[i][j]} ";
done;
echo;
done
印刷
typeset -a arr=((a b x) (d e f) (g h i) )
---
a b x
d e f
g h i
但实际上,这是您可能应该考虑改用 Python(或 Perl,或...)的情况之一,除非您的用例非常特殊。 shell 语言使启动外部程序变得容易,但处理数据结构却困难得多。
答案3
我发现“滥用”数组可以达到你想要的效果:
#!/bin/bash
AllServers=(
"vmkm13, 172.16.39.71"
"vmkm14, 172.16.39.72"
"vmkm15, 172.16.39.84"
"vmkw51, 172.16.39.73"
"vmkw52, 172.16.39.74"
"vmkw53, 172.16.39.75"
"vmkw54, 172.16.39.76"
"vmkw55, 172.16.39.77"
"vmkw56, 172.16.39.78"
"vmkw57, 172.16.39.79"
"vmkw58, 172.16.39.80"
"vmkw59, 172.16.39.81"
"vmkw60, 172.16.39.82"
"vmkw61, 172.16.39.83"
"vmkw62, 172.16.39.85"
"vmkw63, 172.16.39.86"
"vmkw64, 172.16.39.87"
)
for Servers in "${AllServers[@]}"; do
Servername=$(echo "$Servers" | awk -F',' '{ print $1 }')
ServerIP=$(echo "$Servers" | awk -F',' '{ print $2 }')
done
这绝不是正确的。但是将数据括在双引号中,然后用逗号分隔条目,可以让您使用 AWK 之类的工具将列条目提取到变量中。我确实说过这是一种非常hacky的方法。但它有效。
答案4
declare -p
这很hacky,但是您可以使用(typeset -p
也可以)序列化数组。使用该策略,您可以在 bash 中创建一系列序列化数组,然后创建eval
它们。请注意,如果我们关心空字符串作为值,则必须使用引号:
# multidimensional array serialized with `declare -p`
# red, yellow, green things
declare -a color_table=(
"$(inner_row=(stop caution go); declare -p inner_row)"
"$(inner_row=(rose tulip clover); declare -p inner_row)"
"$(inner_row=(strawberry banana grape); declare -p inner_row)"
)
以下是如何使用该多维数组:
# using eval, we deserialize the inner_row variable as we loop
echo "=== table ==="
printf '%s\n' "${color_table[@]}"
echo "=== rows ==="
for row in "${color_table[@]}"; do
eval $row
echo "red thing: ${inner_row[0]}"
echo "yellow thing: ${inner_row[1]}"
echo "green thing: ${inner_row[2]}"
done
输出是:
=== table ===
declare -a inner_row=([0]="stop" [1]="caution" [2]="go")
declare -a inner_row=([0]="rose" [1]="tulip" [2]="clover")
declare -a inner_row=([0]="strawberry" [1]="banana" [2]="grape")
=== rows ===
red thing: stop
yellow thing: caution
green thing: go
red thing: rose
yellow thing: tulip
green thing: clover
red thing: strawberry
yellow thing: banana
green thing: grape
对于 Zsh 用户,请记住数组从 1 开始,因此此脚本将起作用,但在此示例中不使用 0-2 进行索引,而是使用 1-3。