首页 > 解决方案 > Bash 将命令解析为二维数组

问题描述

我想制作一个脚本,列出所有可用的 midi 设备并提示用户选择一个,然后再为每个音符分配快捷方式。我设法获得了所有使用aseqdump -l. 就我而言,这输出:

 Port    Client name                      Port name
  0:0    System                           Timer
  0:1    System                           Announce
 14:0    Midi Through                     Midi Through Port-0
 20:0    UMC404HD 192k                    UMC404HD 192k MIDI 1
 28:0    Launchpad S                      Launchpad S MIDI 1

并以我的最低技能制作了一个脚本,在每行之前添加了一个数字,所以它看起来像这样:

    Port    Client name                      Port name
 1) 0:0    System                           Timer
 2) 0:1    System                           Announce
 3) 14:0    Midi Through                     Midi Through Port-0
 4) 20:0    UMC404HD 192k                    UMC404HD 192k MIDI 1
 5) 28:0    Launchpad S                      Launchpad S MIDI 1

然后提示用户根据左侧的数字选择设备。所有的乐趣和游戏,但我不知道我怎么能只阅读设备名称。例如,如果用户输入“4”,我希望我的 mDevice 变量等于“UMC404HD 192k”,这样我就可以调用aseqdump -p $(mDevice)并监控它的活动。

我尝试逐字阅读命令输出,但这似乎没用,因为每个字段中的字数从 1 到 5 甚至更多不等。是否可以将此命令的输入解析为二维数组,其中一维将存储设备?例如,我理想情况下有

mDevicesArray[0] = { "0:0", "System, "Timer"} 
mDevicesArray[1] = { "0:1", "System", "Announce"}
...
mDevicesArray[4] = { "28:0", "Launchpad S", "Launchpad S MIDI 1"}

然后使用此数组对设备进行进一步处理。

标签: linuxbashmidi

解决方案


bash 没有二维数组。只要执行速度不是什么大问题,您就可以使用关联数组模拟一个:

$ cat ./tst.sh
#!/bin/env bash

declare -A mDevicesArray

mDevicesSet() {
    local rowNr="$1" colNr
    shift
    for (( colNr=1; colNr<=$#; colNr++ )); do
        mDevicesArray["${rowNr},${colNr}"]="${!colNr}"
    done
}

mDevicesSet 1 '0:0'  'System'        'Timer'
mDevicesSet 2 '0:1'  'System'        'Announce'
mDevicesSet 3 '14:0' 'Midi Through'  'Midi Through Port-0'
mDevicesSet 4 '20:0' 'UMC404HD 192k' 'UMC404HD 192k MIDI 1'
mDevicesSet 5 '28:0' 'Launchpad S'   'Launchpad S MIDI 1'

printf '%s\n' "${mDevicesArray[4,2]}"

$ ./tst.sh
UMC404HD 192k

否则,还有其他各种解决方法,例如eval,我不推荐使用它,或者为了简单、稳健和效率,您可以使用mDevicesArray1[1]="0:0"; mDevicesArray2[1]="System"; mDevicesArray3[1]="Timer"等等,并简单地编写函数来访问数组,就好像它们是 2D 一样,例如:

$ cat tst.sh
#!/bin/bash

mDevicesSet() {
    local rowNr="$1"
    shift
    mDevicesArray1["$rowNr"]="$1"
    mDevicesArray2["$rowNr"]="$2"
    mDevicesArray3["$rowNr"]="$3"
}

mDevicesGet() {
    local rowNr=$1 colNr=$2 val
    case $colNr in
        1 ) val="${mDevicesArray1[$rowNr]}" ;;
        2 ) val="${mDevicesArray2[$rowNr]}" ;;
        3 ) val="${mDevicesArray3[$rowNr]}" ;;
    esac
    printf '%s\n' "$val"
}

mDevicesSet 1 '0:0'  'System'        'Timer'
mDevicesSet 2 '0:1'  'System'        'Announce'
mDevicesSet 3 '14:0' 'Midi Through'  'Midi Through Port-0'
mDevicesSet 4 '20:0' 'UMC404HD 192k' 'UMC404HD 192k MIDI 1'
mDevicesSet 5 '28:0' 'Launchpad S'   'Launchpad S MIDI 1'

printf '%s\n' "$(mDevicesGet 4 2)"

$ ./tst.sh
UMC404HD 192k

与使用关联数组相比,虽然执行速度更快,但索引数组方法的缺点是您拥有硬编码的列数 (3),并且每行必须具有相同的列数,并且更难扩展超过 2 个指数。


推荐阅读