이름에 변수가 포함된 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. 전체 파일을 셸의 메모리로 읽는 동안 모든 줄을 배열에 넣는 더 간단한 방법을 사용할 수도 있습니다.

    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"

자세한 내용을 포함하여 새로운 질문을 해보세요.

답변2

변수에 이름과 인덱스를 모두 할당하는 경우 변수 간접 참조를 사용할 수 있습니다.

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

답변3

다차원 배열 데이터를 1D 배열에 저장하는 표준 방법은 각 행을 배열의 오프셋에 저장하는 것입니다.

요소는 0부터 시작하는 행 인덱스, 가 0 부터 시작하는 열 인덱스, 가 열 개수인 (i,j)인덱스에 있습니다 .i*m + jijm

또한 입력 파일을 가져오고 모든 공백을 줄 바꿈으로 변경하고 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(어느이 언어는 모든 종류의 데이터 처리를 위해 셸을 대체할 수 있는 좋은 후보입니다.

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

답변4

eval다음과 같이 생성된 이름을 사용할 수 있습니다 .

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

기본적으로 평가하려는 메타 문자를 제외한 모든 메타 문자를 인용합니다.

따라서... $COUNTER1이면 스크립트가 작동합니다.

declare -a "arr1=($field)"

추가 자료:

관련 정보