名前に変数を含むbash配列

名前に変数を含むbash配列

以下の問題についてご協力いただければ幸いです:

配列名の一部として変数を含む配列を設定しようとしています。例: Arr_$COUNTER($COUNTERループ回数に基づいて変更されます)

私が試したあらゆる方法では、「不正な置換」や「予期しないトークンの近くで構文エラー」などのエラーが発生しました。

全体の流れは次のとおりです。

  1. 複数の行を含むファイルが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 つの値が IP (そのうち 4 つ) として使用され、他の 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"

詳細を記載した新しい質問を作成してください。

答え2

変数に名前とインデックスの両方を割り当てる場合は、変数間接参照を使用できます。

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

答え3

多次元配列データを次元 1 の配列に格納する標準的な方法は、各行を配列内のオフセットに格納することです。

要素(i,j)はインデックスに配置されます。i*m + jここではiはゼロベースの行インデックス、jはゼロベースの列インデックス、mは列数です。

これにより、入力ファイルを取得し、すべてのスペースを改行に変更して を使用するだけで済むため、データの読み取りも簡単になりますreadarray

コマンドラインの場合:

$ readarray -t arr < <( tr -s ' ' '\n' <data )
$ printf '%s\n' "${arr[@]}"
10
20
30
40
50
60
100
200
300
400
500
600

データの列数は次のようにして計算できます。

$ m=$( awk '{ print NF; exit }' <data )

行数は次の通りです。

$ n=$( wc -l <data )

次に、通常どおり二重ループで列と行を反復処理します。

for (( i = 0; i < n; ++i )); do
    for (( j = 0; j < m; ++j )); do
        printf '%4d' "${arr[i*m + j]}"
    done
    printf '\n'
done

与えられたデータに対して、これは生成されます

  10  20  30  40  50  60
 100 200 300 400 500 600

理想的には、多次元配列をサポートする Perl、Python、C などの言語を使用します。つまり、実際にデータのセット全体をメモリに格納する必要があり、行ごとに処理できない場合です。

行ごとの処理の場合はawkbashどれでも言語は、あらゆる種類のデータ処理のためのシェルを置き換えるのに適した候補となるでしょう):

awk '{ for (i = 1; i <= NF; ++i) printf("%4d", $i); printf("\n") }' data

答え4

名前を生成するにはeval、例えば、

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

基本的に、評価したいメタ文字を除くすべてのメタ文字を引用符で囲みます。

つまり...$COUNTERが1の場合、スクリプトは

declare -a "arr1=($field)"

参考文献:

関連情報