名前に変数を含むbash配列

名前に変数を含むbash配列

次の質問にご協力いただきありがとうございます。

次のように、配列名の一部として変数を含む配列を設定しようとしています。 (ループ数によって変わるArr_$COUNTER位置)$COUNTER

私が試したすべての可能なアプローチでは、「誤った置換」や「予期しないトークンの近くの構文エラー」などのエラーが発生します。

全体のプロセスは次のとおりです。

  1. 複数行を含むファイルがあります。各行には、スペースで区切られた6つの値があります。

    10 20 30 40 50 60  
    100 200 300 400 500 600
    
  2. スクリプトは、ファイルの各行を読み取り、配列として宣言するように設計されています(行番号は変数です)。

  3. テストで各値を印刷する必要があり、最終的には各値に対して異なる機能が実行されます。

    #!/bin/bash
    COUNTER=1
    LINES=`wc -l VALUES_FILE.txt | awk '{print $1}'`
    echo "Total number of lines "$LINES
    echo
    while [ $COUNTER -le $LINES ]
    do
    echo "Counter value is $COUNTER"
    field=`awk "NR == $COUNTER" VALUES_FILE.txt`
    echo "Field = $field"
    declare -a "arr$COUNTER=($field)"
    echo "arr$COUNTER[0] = ${arr$COUNTER[0]}"
    echo "arr$COUNTER[1] = ${arr$COUNTER[1]}"
    echo "arr$COUNTER[2] = ${arr$COUNTER[2]}"
    echo "arr$COUNTER[3] = ${arr$COUNTER[3]}"
    echo "arr$COUNTER[4] = ${arr$COUNTER[4]}"
    echo "arr$COUNTER[5] = ${arr$COUNTER[5]}"
    let COUNTER=COUNTER+1
    echo
    done
    echo "The End"
    echo
    

結果は次のとおりです。

総行数 2

カウンタ値は1です。
フィールド = 10 20 30 40 50 60
./sort.sh: 行 12: arr$COUNTER[0] = ${arr$COUNTER[0]}: 無効な置換
終わる

機能するには何を変更/変更する必要がありますか?

ありがとうございます!

ベストアンサー1

いくつかの考え:

  1. 変数値の「パラメータ拡張」(${...}部分):

    echo "arr$COUNTER[0] = ${arr$COUNTER[0]}"
    

    動作しません。 evalを使用して問題を解決できます(ただし推奨しません)。

    eval echo "arr$COUNTER[0] = \${arr$COUNTER[0]}"
    

    この行は次のように書くことができます。

    i="arr$COUNTER[0]"; echo "$i = ${!i}"
    

    Bashではこれを間接指定(!)といいます。

  2. 次の行でも同様の問題が発生します。

    declare -a "arr$COUNTER=($field)"
    

    2行に分けてevalを使用する必要があります。

    declare -a "arr$COUNTER"
    eval arr$COUNTER\=\( \$field \)
    

    繰り返しますが、eval(この場合)を使用しないことをお勧めします。

  3. また、ファイル全体をシェルのメモリに読み込んでいる間に、すべての行を配列に配置するより簡単な方法を使用することもできます。

    readarray -t lines <"VALUES_FILE.txt"
    

    これは、各行に対して awk を呼び出すよりも高速です。

上記のすべてを含むスクリプトは次のとおりです。

#!/bin/bash
valfile="VALUES_FILE.txt"

readarray -t lines <"$valfile"             ### read all lines in.

line_count="${#lines[@]}"
echo "Total number of lines $line_count"

for ((l=0;l<$line_count;l++)); do
    echo "Counter value is $l"             ### In which line are we?
    echo "Field = ${lines[l]}"             ### kepth only to help understanding.
    k="arr$l"                              ### Build the variable arr$COUNTER
    IFS=" " read -ra $k <<<"${lines[l]}"   ### Split into an array a line.
    eval max=\${#$k[@]}                    ### How many elements arr$COUNTER has?
    #echo "field $field and k=$k max=$max" ### Un-quote to "see" inside.
    for ((j=0;j<$max;j++)); do             ### for each element in the line.
        i="$k[$j]"; echo "$i = ${!i}"      ### echo it's value.
    done
done
echo "The End"
echo

ただし、AWKで必要な操作を実行できる場合は、AWKが高速になる可能性があります。


awkでも同様の処理が可能です。 6つの値(そのうち4つ)がIPとして使用され、残りの2つが数字とエポック時間であるとします。

非常に単純なAWKスクリプトの例:

#!/bin/sh
valfile="VALUES_FILE.txt"
awk '
NF==6 { printf ( "IP: %s.%s.%s.%s\t",$1,$2,$3,$4)
        printf ( "number: %s\t",$5+2)
        printf ( "epoch: %s\t",$6)
        printf ( "\n" )
    }
' "$valfile"

詳細を含め、新しい質問をしてください。

おすすめ記事