Bash中的多维数组

scp*_*ntm 44 arrays bash shell multidimensional-array

我正在计划一个脚本来管理我的Linux系统的某些部分,我正在决定是否要使用.

我更喜欢将此作为Bash脚本,因为命令更容易,但真正的决定因素是配置.我需要能够在配置文件中存储多维数组,以告诉脚本如何处理自身.使用bash在配置文件中存储简单的key = value对很容易,但我能想到做多维数组的唯一方法是使用两层解析引擎,类似于

array=&d1|v1;v2;v3&d2|v1;v2;v3
Run Code Online (Sandbox Code Playgroud)

但是marshall/unmarshall代码可能会成为一只熊,而且对于下一个必须管理这个问题的可怜的人来说,它远非用户友好.如果我不能轻易地在bash中执行此操作,我只需将配置写入xml文件并在python中编写脚本.

在bash中有一个简单的方法吗?

感谢大家.

Nah*_*eul 43

Bash不支持多维数组,也不支持哈希,似乎你想要一个值为数组的哈希.这个解决方案不是很漂亮,带有xml文件的解决方案应该更好:

array=('d1=(v1 v2 v3)' 'd2=(v1 v2 v3)')
for elt in "${array[@]}";do eval $elt;done
echo "d1 ${#d1[@]} ${d1[@]}"
echo "d2 ${#d2[@]} ${d2[@]}"
Run Code Online (Sandbox Code Playgroud)

  • 只是一张纸条.bash支持从版本4开始的哈希(关联数组).更多信息:http://mywiki.wooledge.org/BashFAQ/006 (29认同)
  • 那就是......邪恶......抱歉,我的意思就是eval (8认同)

Jah*_*hid 19

Bash没有多维数组.但您可以使用关联数组模拟一些类似的效果.以下是假装用作多维数组的关联数组的示例:

declare -A arr
arr[0,0]=0
arr[0,1]=1
arr[1,0]=2
arr[1,1]=3
echo "${arr[0,0]} ${arr[0,1]}" # will print 0 1
Run Code Online (Sandbox Code Playgroud)

如果未将数组声明为关联(with -A),则上述操作无效.例如,如果省略该declare -A arr行,echo则将打印2 3而不是0 1,因为0,0,1,0这将被视为算术表达式并计算为0(逗号运算符右侧的值).

  • 编辑:似乎OS X附带bash 3,此选项需要bash 4.谢谢,Apple. (10认同)
  • 声明-A [arrayname]引发"无效选项",至少在OS X中是这样. (2认同)

Pau*_*ake 14

这对我有用.

# Define each array and then add it to the main one
SUB_0=("name0" "value0")
SUB_1=("name1" "value1")
MAIN_ARRAY=(
  SUB_0[@]
  SUB_1[@]
)

# Loop and print it.  Using offset and length to extract values
COUNT=${#MAIN_ARRAY[@]}
for ((i=0; i<$COUNT; i++))
do
  NAME=${!MAIN_ARRAY[i]:0:1}
  VALUE=${!MAIN_ARRAY[i]:1:1}
  echo "NAME ${NAME}"
  echo "VALUE ${VALUE}"
done
Run Code Online (Sandbox Code Playgroud)

这是基于这个答案

  • 我还没有研究为什么 - 但这在 bash v4.3+ 上运行良好,但在早期版本上有问题。(打印单个字符而不是完整的单词) (3认同)

yac*_*cob 8

独立于所使用的shell(sh,ksh,bash,...),以下方法对于n维数组非常有效(该示例涵盖了二维数组).

在样本中,行分隔符(第一维)是空格字符.为了引入字段分隔符(第二维),使用标准的unix工具tr.可以以相同的方式使用用于附加尺寸的附加分离器.

当然这种方法的表现不是很好,但如果性能不是一个标准,这种方法非常通用,可以解决许多问题:

array2d="1.1:1.2:1.3 2.1:2.2 3.1:3.2:3.3:3.4"

function process2ndDimension {
    for dimension2 in $*
    do
        echo -n $dimension2 "   "
    done
    echo
}

function process1stDimension {
    for dimension1 in $array2d
    do
        process2ndDimension `echo $dimension1 | tr : " "`
    done
}

process1stDimension
Run Code Online (Sandbox Code Playgroud)

该示例的输出如下所示:

1.1     1.2     1.3     
2.1     2.2     
3.1     3.2     3.3     3.4 
Run Code Online (Sandbox Code Playgroud)

  • 因此,这些值不包含空格或冒号。:D (3认同)

小智 7

如果您想使用 bash 脚本并使其易于阅读,建议将数据放入结构化 JSON 中,然后在您的 bash 命令中使用轻量级工具 jq来遍历数组。例如使用以下数据集:

[

    {"specialId":"123",
    "specialName":"First"},

    {"specialId":"456",
    "specialName":"Second"},

    {"specialId":"789",
    "specialName":"Third"}
]
Run Code Online (Sandbox Code Playgroud)

您可以使用 bash 脚本和 jq 遍历这些数据,如下所示:

function loopOverArray(){

    jq -c '.[]' testing.json | while read i; do
        # Do stuff here
        echo "$i"
    done
}

loopOverArray
Run Code Online (Sandbox Code Playgroud)

输出:

{"specialId":"123","specialName":"First"}
{"specialId":"456","specialName":"Second"}
{"specialId":"789","specialName":"Third"}
Run Code Online (Sandbox Code Playgroud)

  • 这次真是万分感谢。绝对是最简单的实用解决方案。我们可以把纯粹性留给纯粹主义者。 (2认同)

n00*_*00p 5

经过大量的反复试验,我实际上发现 bash 上最好、最清晰和最简单的多维数组是使用常规 var。是的。

优点:你不必遍历一个大数组,你可以只回显 "$var" 并使用 grep/awk/sed。它简单明了,您可以拥有任意数量的列。

例子:

$ var=$(echo -e 'kris hansen oslo\nthomas jonson peru\nbibi abu johnsonville\njohnny lipp peru')

$ echo "$var"
kris hansen oslo
thomas johnson peru
bibi abu johnsonville
johnny lipp peru
Run Code Online (Sandbox Code Playgroud)

如果你想找到秘鲁的每个人

$ echo "$var" | grep peru
thomas johnson peru
johnny lipp peru
Run Code Online (Sandbox Code Playgroud)

仅在第三个字段中使用 grep(sed)

$ echo "$var" | sed -n -E '/(.+) (.+) peru/p'
thomas johnson peru
johnny lipp peru
Run Code Online (Sandbox Code Playgroud)

如果你只想要 x 字段

$ echo "$var" | awk '{print $2}'
hansen
johnson
abu
johnny
Run Code Online (Sandbox Code Playgroud)

秘鲁的每个人都叫 thomas 并且只返回他的姓氏

$ echo "$var" |grep peru|grep thomas|awk '{print $2}'
johnson
Run Code Online (Sandbox Code Playgroud)

您能想到的任何查询......超级简单。

要更改项目:

$ var=$(echo "$var"|sed "s/thomas/pete/")
Run Code Online (Sandbox Code Playgroud)

删除包含“x”的行

$ var=$(echo "$var"|sed "/thomas/d")
Run Code Online (Sandbox Code Playgroud)

根据另一个项目的值更改同一行中的另一个字段

$ var=$(echo "$var"|sed -E "s/(thomas) (.+) (.+)/\1 test \3/")
$ echo "$var"
kris hansen oslo                                                                                                                                             
thomas test peru                                                                                                                                          
bibi abu johnsonville
johnny lipp peru
Run Code Online (Sandbox Code Playgroud)

当然,如果您想这样做,循环也可以工作

$ for i in "$var"; do echo "$i"; done
kris hansen oslo
thomas jonson peru
bibi abu johnsonville
johnny lipp peru
Run Code Online (Sandbox Code Playgroud)

我发现的唯一问题是您必须始终引用 var(在示例中;var 和 i),否则事情将如下所示

$ for i in "$var"; do echo $i; done
kris hansen oslo thomas jonson peru bibi abu johnsonville johnny lipp peru
Run Code Online (Sandbox Code Playgroud)

如果您的输入中有空格,那么无疑有人会说它不起作用,但是可以通过在输入中使用另一个分隔符来解决这个问题,例如(现在使用 utf8 字符来强调您可以选择您的输入不会包含,但您可以选择任何 ofc):

$ var=$(echo -e 'field one?field two hello?field three yes moin\nfield 1?field 2?field 3 dsdds aq')

$ for i in "$var"; do echo "$i"; done
field one?field two hello?field three yes moin
field 1?field 2?field 3 dsdds aq

$ echo "$var" | awk -F '?' '{print $3}'
field three yes moin
field 3 dsdds aq

$ var=$(echo "$var"|sed -E "s/(field one)?(.+)?(.+)/\1?test?\3/")
$ echo "$var"
field one?test?field three yes moin
field 1?field 2?field 3 dsdds aq
Run Code Online (Sandbox Code Playgroud)

如果您想在输入中存储换行符,您可以在输入之前将换行符转换为其他内容,并在输出时再次将其转换回(或不使用 bash...)。享受!