名稱中帶有變數的 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)"
    

    應該分為兩行,並使用 eval:

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

    再次強調,我不建議使用 eval(在本例中)。

  3. 當您將整個檔案讀入 shell 的記憶體時,我們不妨使用更簡單的方法將所有行放入陣列中:

    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 個),另外兩個是數字和紀元時間。

只是一個非常簡單的 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。也就是說,如果您實際上需要將整組資料儲存在記憶體中並且無法逐行處理它。

對於逐行處理,awk將是替換bash(任何語言將是替換任何類型資料處理的 shell 的良好候選者):

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)"

延伸閱讀:

相關內容