이름에 변수가 있는 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개의 값이 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의 배열에 저장하는 표준 방법은 각 행을 배열의 오프셋에 저장하는 것입니다.

요소는 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)"

추가 자료:

관련 정보