Bash: tee를 사용하는 for 루프의 변수 범위

Bash: tee를 사용하는 for 루프의 변수 범위

고려하다:

numbers="1 111 5 23 56 211 63"
max=0

for num in ${numbers[@]}; do

      [ $num -gt $max ]\
           && echo "old -> new max = $max -> $num"\
           && max=$num

done | tee logfile

echo "Max= $max"

max 변수를 제거하면 | tee logfile211로 올바르게 인쇄되지만 그대로 두면 Max= 0.

무슨 일이야?

답변1

파이프의 각 측면은 서브쉘에서 실행됩니다. 서브셸은 동일한 상태에서 시작한 다음 독립적으로 발전하는 원본 셸 프로세스의 복사본입니다. 하위 쉘에 설정된 변수는 상위 쉘로 다시 이스케이프될 수 없습니다.

Bash에서는 파이프를 사용하는 대신 다음을 사용할 수 있습니다.프로세스 교체. 루프가 원래 셸과 tee하위 셸에서만 실행된다는 점을 제외하면 이는 파이프와 거의 동일하게 동작합니다 .

for num in "${numbers[@]}"; do
  if ((num > max)); then
    echo "old -> new max = $max -> $num"
    max=$num
  fi
done > >(tee logfile)
echo "Max= $max"

그 동안 스크립트에서 몇 가지 사항을 변경했습니다.

파이프된 솔루션과 하위 셸 솔루션 간에는 약간의 차이가 있습니다. 파이프된 명령은 두 명령이 모두 종료될 때 완료되는 반면, 프로세스 대체가 포함된 명령은 프로세스 대체의 프로세스를 기다리지 않고 기본 명령이 종료될 때 완료됩니다. 이는 스크립트가 완료될 때 로그 파일이 완전히 기록되지 않았을 수 있음을 의미합니다.

또 다른 접근 방식은 다른 채널을 사용하여 하위 쉘에서 원래 쉘 프로세스로 통신하는 것입니다. 당신은 할 수임시 파일 사용(유연하지만 수행하기가 더 어렵습니다. 임시 파일을 적절한 디렉터리에 쓰고, 이름 충돌을 방지하고( 사용 mktemp), 오류가 발생하더라도 삭제합니다. 아니면 할 수 있어다른 파이프를 사용하세요결과를 전달하고 명령 대체를 통해 결과를 얻습니다.

max=$({ { for … done;
          echo "$max" >&4
        } | tee logfile >&3; } 4>&1) 3&>1

출력은 tee파일 설명자 3으로 이동하며 이는 스크립트의 표준 출력으로 리디렉션됩니다. 의 출력은 echo "$max"파일 설명자 4로 이동하며 이는 명령 대체로 리디렉션됩니다.

답변2

for 루프를 파이프에 넣으면 하위 쉘에서 실행되며 해당 변수는 슈퍼쉘에 전달되지 않습니다.

답변3

파이프에서 살아남지는 못하지만 다음과 같이 작동합니다.

numbers="1 111 5 23 56 211 63"  
max=0  
echo $max>maxfile  

for num in ${numbers[@]}; do  

      [ $num -gt $max ]\  
           && echo "old -> new max = $max -> $num"\  
           && max=$num\  
           && echo $max>maxfile  

done | tee logfile  

read max<maxfile  
echo "Max= $max"  

관련 정보