고려하다:
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 logfile
211로 올바르게 인쇄되지만 그대로 두면 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"
그 동안 스크립트에서 몇 가지 사항을 변경했습니다.
- 변수 대체를 생략해야 하는 이유를 모르는 경우에는 항상 큰따옴표를 사용하십시오.
&&
당신이 그것을 의미할 때 그것을 사용하지 마십시오if
. 명령의 반환 상태를 사용하는 경우 읽기가 어렵고 동일한 동작을 하지 않습니다.- 나는 bash 특정 구성을 사용하고 있으므로 다음을 사용할 수도 있습니다.산술 용어.
파이프된 솔루션과 하위 셸 솔루션 간에는 약간의 차이가 있습니다. 파이프된 명령은 두 명령이 모두 종료될 때 완료되는 반면, 프로세스 대체가 포함된 명령은 프로세스 대체의 프로세스를 기다리지 않고 기본 명령이 종료될 때 완료됩니다. 이는 스크립트가 완료될 때 로그 파일이 완전히 기록되지 않았을 수 있음을 의미합니다.
또 다른 접근 방식은 다른 채널을 사용하여 하위 쉘에서 원래 쉘 프로세스로 통신하는 것입니다. 당신은 할 수임시 파일 사용(유연하지만 수행하기가 더 어렵습니다. 임시 파일을 적절한 디렉터리에 쓰고, 이름 충돌을 방지하고( 사용 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"