BASH: awk를 사용하여 고유한 줄을 필터링하면 길이가 0인 배열이 됩니다.

BASH: awk를 사용하여 고유한 줄을 필터링하면 길이가 0인 배열이 됩니다.

참고: Jeff Schaller와 steeldriver에게 감사드립니다. 그러나 답변으로 게시되지 않았으므로 해결된 것으로 표시하는 방법을 잘 모르겠습니다. 이제 파이프/서브쉘에 대해 더 잘 이해하게 되었습니다. 나는 이것을 한때 알고 있었을 것이라고 확신하지만 bash에서 복잡한 것을 시도한 지 오래되었습니다.

awk에서 필터링된 결과를 변수에 할당하고프로세스 대체나를 위해 일했습니다. 정렬되지 않은 고유 행을 읽는 최종 코드는 다음과 같습니다 stdin.

while read -r FILE
do
    ...
done < <(awk '!x[$0]++')

더 읽어보기프로세스 대체유사한 문제에 대한 해결책을 찾고 있는 이 질문을 찾는 사람들을 위한 것입니다.

원래 질문:

사이트를 검색했지만 내 문제에 대한 답변을 찾을 수 없습니다.

stdin에서 배열을 만들고 있는데 고유한 줄을 필터링해야 합니다. 이를 위해 제가 awk '!x[$0]++'읽은 약칭은 다음과 같습니다.

awk 'BEGIN { while (getline s) { if (!seen[s]) print s; seen[s]=1 } }'.

필터는 원하는 대로 작동하지만 문제는 루프의 결과 배열이 while read비어 있다는 것입니다.

예를 들어( $list의 대리자로 사용 stdin):

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
while read -r line; do
    array[count++]=$line
done <<< "$list"
echo "array length = ${#array[@]}"
counter=0
while [  $counter -lt ${#array[@]} ]; do
    echo ${array[counter++]}
done

다음을 생산합니다:

array length = 5
red apple
yellow banana
purple grape
orange orange
yellow banana

하지만 $listawk로 필터링하면 다음과 같습니다.

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
    array[count++]=$line
done
echo "array length = ${#array[@]}"
counter=0
while [  $counter -lt ${#array[@]} ]; do
     echo ${array[counter++]}
done

다음을 생산합니다:

array length = 0

그러나 출력은 awk '!x[$0]++' <<< "$list"괜찮아 보입니다.

red apple
yellow banana
purple grape
orange orange

루프 의 각 줄을 검사해 보았습니다 while read.

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
i=0
awk '!x[$0]++' <<< "$list" | while read -r line; do
    echo "line[$i] = $line"
    let i=i+1
done

괜찮아 보이는데:

line[0] = red apple
line[1] = yellow banana
line[2] = purple grape
line[3] = orange orange

내가 여기서 무엇을 놓치고 있는 걸까요?

중요한 경우에는 bash 3.2.57을 사용하고 있습니다.

GNU bash, 버전 3.2.57(1)-릴리스(x86_64-apple-darwin15) Copyright (C) 2007 Free Software Foundation, Inc.

답변1

awk '!x[$0]++' <<< "$list" |-r 줄을 읽는 동안; 하다
    정렬[개수++]=$라인
완료

그만큼array(이탤릭체) 이 경우에는subshell(용감한).

그리고$line$array 값이 있습니다....하는 동안말하자면, 서브쉘은 살아있습니다.

하위 쉘이 완료되면(즉, 죽으면) 상위(생성자) 환경이 복원됩니다. 여기에는 서브셸에 설정된 모든 변수 삭제가 포함됩니다.

이 경우:

  • $array제거됨,
  • $line제거됨.

이 시도:

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'
awk '!x[$0]++' <<< "$list" | while read -r line; do
    array[count++]=$line
    printf "array[%d] { %s\n" ${#array[@]} # array[num_of_elements] {
    printf "       %s\n" "${array[@]}"     # elements
    printf "}\n"                           # } end of array

done

printf "\n[ %s ]\n\n" "END OF SUBSHELL (PIPE)"

printf "array[%d] {\n" ${#array[@]}
printf "       %s\n" "${array[@]}"
printf "}\n"

수확량:

array[1] {
       red apple
}
array[2] {
       red apple
       yellow banana
}
array[3] {
       red apple
       yellow banana
       purple grape
}
array[4] {
       red apple
       yellow banana
       purple grape
       orange orange
}

[ END OF SUBSHELL (PIPE) ]

array[0] {

}

아니면 설명서대로.

우리는 다음과 같이 시작할 수 있습니다파이프라인

[...] 파이프라인의 각 명령은 자체적으로 실행됩니다.서브쉘(보다명령 실행 환경). […]

그리고명령 실행 환경다음과 같이 모험을 확장합니다.

[...] 여기서 호출되는 명령은별도의 환경 할 수 없다쉘의 실행 환경에 영향을 미칩니다.

명령 대체, 괄호로 그룹화된 명령 및 비동기 명령은 쉘 환경과 중복되는 하위 쉘 환경에서 호출됩니다. 단, 쉘이 포착한 트랩은 호출 시 쉘이 상위로부터 상속받은 값으로 재설정됩니다. 파이프라인의 일부로 호출되는 내장 명령은 서브셸 환경에서도 실행됩니다.서브쉘 환경에 대한 변경사항은 쉘의 실행 환경에 영향을 미칠 수 없습니다.[…]

영향을 미칠 수 없습니다. 따라서 설정할 수 없습니다.

그러나 다음과 같은 방향으로 리디렉션하고 작업을 수행할 수 있습니다.

list=$'red apple\nyellow banana\npurple grape\norange orange\nyellow banana'

while read -r line; do
    arr[count++]=$line
done <<<"$(awk '!x[$0]++' <<< "$list")"

echo "arr length = ${#arr[@]}"
count=0
while [[  $count -lt ${#arr[@]} ]]; do
    echo ${arr[count++]}
done

답변2

문제에 대한 몇 가지 해결책루프 없이

# use bash's mapfile with process substitution 
mapfile -t arr < <( awk '!x[$0]++' <<<"$list" )

# use array assignment syntax (at least bash, ksh, zsh) 
# of a command-substituted value split at newline only
# and (if the data can contain globs) globbing disabled
set -f; IFS='\n' arr=( $( awk '!x[$0]++' <<<"$list" ) ); set +f

관련 정보