이 코드는 bash v4.4에서는 실행되지만 bash v3.2에서는 실행되지 않는 이유는 무엇입니까?

이 코드는 bash v4.4에서는 실행되지만 bash v3.2에서는 실행되지 않는 이유는 무엇입니까?

다음 bash 스크립트가 있습니다.

#!/bin/bash

encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
MISSING_DISKS=()
OLDIFS=$IFS
IFS=$'\n'
MISSING_DISKS+=($({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${encl0[@]#0,}"; } | sort | uniq -u))
IFS=$OLDIFS
echo "$({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${encl0[@]#0,}"; } | sort | uniq -u)"
echo "${MISSING_DISKS[@]}"
if ((${#MISSING_DISKS[@]}>1)); then
    echo "Greater than 1"
else
    echo "Success"
fi

bash v4.4를 사용하여 실행하면 예상대로 작동합니다.

$ /usr/local/bin/bash test.sh
0 6
0 6
Success

그러나 bash v3.2를 사용하여 실행하면 다음과 같은 결과가 발생하지 않습니다.

$ /bin/bash test.sh
0 6
0 0 0 0 1 2 3 4 5 7 8 9 10 11 12 13 14 15 0 1 0 10 0 11 0 12 0 13 0 14 0 15 0 2 0 3 0 4 0 5 0 6 0 7 0 8 0 9
Greater than 1

MISSING_DISKS이를 설정하는 명령의 출력과 다른 것으로 설정하는 방법을 이해하지 못합니다 . 이 문제의 원인을 아는 사람이 있나요?

답변1

공백, 탭 또는 줄 바꿈을 사용하는 것은 항상 거의 실패합니다.

여기서 핵심 질문이 발생합니다.

encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 )
IFS=$'\n'
printf '<0 %s>\n' "${encl0[@]#0,}"

Bash 3.2에서 실행하는 경우:

$ b32sh ./script
<0 0 1 2 3 4 5 7 8 9>

확장은 "${encl0[@]#0,}"값 목록이 아닌 문자열로 처리됩니다.

IFS에 공백이 있거나 편집되지 않은 배열의 각 값을 확장하는 경우에는 이 문제가 발생하지 않습니다.

#!/bin/bash
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 )
IFS=$' \n'
printf '<0 %s>\n' "${encl0[@]#0,}"

구현하다:

$ b32sh ./script
<0 0>
<0 1>
<0 2>
<0 3>
<0 4>
<0 5>
<0 7>
<0 8>
<0 9>

또는:

#!/bin/bash
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 )
IFS=$'\n'
printf '<0 %s>\n' "${encl0[@]}"

구현하다:

b32sh ./so
<0 0,0>
<0 0,1>
<0 0,2>
<0 0,3>
<0 0,4>
<0 0,5>
<0 0,7>
<0 0,8>
<0 0,9>

IFS=$OLDIFS 에코 라인을 테스트하기 전에 IFS를 복원하기 때문에 문제는 스크립트에 숨겨져 있습니다 .

이 문제를 피하는 한 가지 방법은 printf에서 공백을 사용하지 않는 것입니다.

#!/bin/bash
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
MISSING_DISKS=()
OLDIFS=$IFS
IFS=$' \n'
MISSING_DISKS+=($({ printf '0x%s\n' {0..15}; printf '0x%s\n' "${encl0[@]#0,}"; } | sort | uniq -u))
echo "test $({ printf '0x%s\n' {0..15}; printf '0x%s\n' "${encl0[@]#0,}"; } | sort | uniq -u)"
echo "var  ${MISSING_DISKS[@]}"
if ((${#MISSING_DISKS[@]}>1)); then
    echo "Greater than 1"
else
    echo "Success"
fi
IFS=$OLDIFS

또 다른 옵션은 대체 배열을 사용하여 IFS를 개행으로 변경한 후 대체 확장을 방지하는 것입니다.

#!/bin/bash
OLDIFS=$IFS
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
IFS=$' \n'; arr=("${encl0[@]#0,}")
MISSING_DISKS=()
IFS=$'\n'
MISSING_DISKS+=($({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${arr[@]}"; } | sort | uniq -u))
echo "test $({ printf '0 %s\n' {0..15}; printf '0 %s\n' "${arr[@]}"; } | sort | uniq -u)"
echo "var  ${MISSING_DISKS[@]}"
if ((${#MISSING_DISKS[@]}>1)); then
    echo "Greater than 1"
else
    echo "Success"
fi
IFS=$OLDIFS

나는 너에게 제안한다:

  • 다음 규칙을 따르십시오. CAPS의 변수는 환경 변수입니다.
  • +=코드의 이 시점에서 비어 있는 배열을 증가시킬 필요가 없습니다 MISSING_DISKS+=.
  • 나중에 IFS에서 공백을 제거하지 않으려면 printf에서 공백이 아닌 문자를 사용하십시오. 이렇게 하면 스크립트가 더욱 강력해집니다.

이러한 변경이 이루어지면 스크립트는 다음과 같습니다.

#!/bin/bash
oldIFS=$IFS
encl0=( 0,0 0,1 0,2 0,3 0,4 0,5 0,7 0,8 0,9 0,10 0,11 0,12 0,13 0,14 0,15 )
unset missing_disks

IFS=' '
           arr=($(printf '0-%s\n' "${encl0[@]#0,}"))
           arr+=($(printf '0-%s\n' {0..15}))

missing_disks=($(printf '%s\n' "${arr[@]}" | sort | uniq -u))

           echo "test $(printf '0-%s\n' "${arr[@]}" | sort | uniq -u)"
           echo "var  ${missing_disks[@]}"

((${#missing_disks[@]}>1)) && echo "Greater than 1" || echo "Success"
IFS=$oldIFS

관련 정보