みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

Bashスクリプトで複数のテーブルを組み合わせて使用する

はじめに

タイトルいったい何のことか伝わらないと思うので。

いわゆる2次元配列っぽいことをしたいというか、要素数の異なる複数のテーブルを多重ループで回す感じ。

2次元配列が近いと思って調べたけど、自分のやりたいこととうまく合致しないかな。と。

やりたいこと

例えば、複数の環境と対象としてテストプログラムを実行したいが、環境毎に実行するプログラムが異なる。

環境と環境毎のテストプログラムのテーブルをそれぞれもってて、それを組み合わせてループさせたい。

(C++らしなく)C++で書くとこんな感じのやつ。

#include <cstdio>
#include <vector>

int main() {
    enum {
        Env1,
        Env2,
        Env3,
        Env_Max,
    };
    std::vector<const char*> env1_test {"testA", "testB", "testC"};
    std::vector<const char*> env2_test {"testD"};
    std::vector<const char*> env3_test {"testA", "testB", "testC", "testD"};
    std::vector<std::vector<const char*>> table {
            {env1_test}, {env2_test}, {env3_test},
    };
    for (auto i = 0; i < Env_Max; ++i) {
        for (auto elem : table[i]) {
            std::printf("Env[%d]:%s\n", i, elem);
        }
    }
    return 0;
}

実行結果

Env[0]:testA
Env[0]:testB
Env[0]:testC
Env[1]:testD
Env[2]:testA
Env[2]:testB
Env[2]:testC
Env[2]:testD

環境毎のテストプログラムの数が異なるのがポイント。

やったこと

bashスクリプトでは多重配列が無い(はず?)なので、このようなケースは意外と難しい。

ほとんど型の概念がなく、文字列処理としての側面が強いので、実行時に変数名を合成して複数の配列を組み合わせることにした。

#!/bin/bash

readonly -a Env1_test=(
    testA
    testB
    testC
)

readonly -a Env2_test=(
    testD
)

readonly -a Env3_test=(
    testA
    testB
    testC
    testD
)

readonly -a Environments=(
    Env1
    Env2
    Env3
)

execute() {
    local env=$1
    local test=$2
    echo ${env} ${test}
}

execute_all() {
    for env in ${Environments[@]} ; do
        local -n tests=${env}_test
        for t in ${tests[@]} ; do
            execute ${env} ${t}
        done
    done
}

execute_all

実行結果

$ ./test.sh
Env1 testA
Env1 testB
Env1 testC
Env2 testD
Env3 testA
Env3 testB
Env3 testC
Env3 testD

execute_allの「local -n tests=${env}_test」の行で変数名を合成している。 ここではEnvironments配列の内容であるEnvXと固定の文字列_testを合成してEnvX_testという変数名を作成して、 その中の要素にループでアクセスしている。-nがポイントで、変数を名前参照とすることで、 展開後の変数と文字列を組み合わせて、別に実体のある変数の参照をローカル変数のtestsに格納している。

これをしないと、実体が空のEnvX_testがtestsに格納されるので意図した動きにならない。

まとめ

名前参照を使うと意外と便利なことができる。 ただしやりすぎると破綻する。