while 루프 내에서 테스트가 실패할 때 "부울" 플래그 변수가 false로 유지되지 않는 이유를 추적하는 데 문제가 있습니다.
스크립트에는 몇 가지 루프가 포함되어 있지만 본질적으로 그 목적은 크론이 실행될 때 매일 아침 두 개의 무작위 앨범을 재생하는 것입니다. 한때 두 개의 앨범을 선택하는 간단한 한 줄 스크립트가 있었지만 재생하고 싶지 않은 앨범(예: 크리스마스 음악 앨범)을 블랙리스트에 추가하고 싶었습니다.
이를 위해 듣고 싶지 않은 앨범 이름이 포함된 wakeUp_blacklist.txt 파일을 읽도록 하여 일치하는 항목을 거부할 수 있도록 했습니다. 블랙리스트에 등록된 앨범을 무작위로 선택한 앨범과 한 줄씩 비교하여 일치 여부를 테스트합니다. 일치하는 항목이 발견되면 해당 일치 항목을 실패( pass = false
)로 표시하고 루프백하여 다른 앨범을 선택해야 합니다. 어떤 이유에서인지 실행이 read | while
루프를 종료하면 플래그가 다시 true로 전환되므로 다른 앨범은 선택되지 않습니다.
내 코드와 샘플 출력은 다음과 같습니다.
#!/bin/bash
wd="/media/External1/albums"
pass="false"
for ((i=0; i<=1; i++)); do
while [ "$pass" == "false" ]; do
album=`ls -d "$wd"/*/*/ | sort -R | tail -n 1`
echo "album selected:"
echo "$album"
pass="true"
echo "pass reset; pass=$pass"
cat "$wd/wakeUp_blacklist.txt" | while read black; do
echo "pass check 2:$pass"
echo "black: $wd/$black"
if [ "$album" == "$wd/$black" ] || [ "$album\n" == "$albums" ]; then
pass="false"
echo "test failed; pass=$pass"
fi
echo "pass check 1:$pass"
done
echo "Blacklist check complete; pass=$pass" #Why is it true again?!
done
pass="false"
albums="$albums$album\n"
echo "album assigned:"
echo "$album"
done
echo
echo
for album in "$albums"; do
# play "$album*"
echo -e "$album" #List album names rather than actually play them
done
따라서 몇 번 실행하면 결국 블랙리스트에 있는 앨범을 선택하고 다음과 같은 출력을 제공합니다.
album selected:
/media/External1/albums/Adele/21/
pass reset; pass=true
pass check 2:true
black: /media/External1/albums/Vince_Guaraldi_Trio/A_Charlie_Brown_Christmas/
pass check 1:true
pass check 2:true
black: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
pass check 1:true
Blacklist check complete; pass=true
album assigned:
/media/External1/albums/Adele/21/
album selected:
/media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
pass reset; pass=true
pass check 2:true
black: /media/External1/albums/Vince_Guaraldi_Trio/A_Charlie_Brown_Christmas/
pass check 1:true
pass check 2:true
black: /media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
test failed; pass=false
pass check 1:false
Blacklist check complete; pass=true
album assigned:
/media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
/media/External1/albums/Adele/21/
/media/External1/albums/Sound_Inventions/Stille_Nacht-_A_German_Christmas/
마지막에 블랙리스트 항목이 선택한 앨범(German_Christmas)과 일치하면 테스트가 실패하고 변수가 pass
있지만 false
루프 외부 값의 첫 번째 출력을 확인하면 다시 true로 표시되는 것을 볼 수 있습니다!pass
read | while
내 스크립트가 충분히 효율적이지 않을 수 있다는 것을 알고 있지만 계속하기 전에 여기서 무슨 일이 일어나고 있는지 이해하고 싶습니다. 어떤 제안이 있으십니까?
답변1
당신은:
cat "$wd/wakeUp_blacklist.txt" | while read black; do
파이프라인의 각 명령은 자체 하위 셸에서 실행됩니다.
ㅏ서브쉘은bash
자체 상태가 있는 의 또 다른 인스턴스입니다 . 이것은 당신이오른쪽개념적으로 |
다른 인스턴스에 "속합니다" bash
- 처음의 기존 값과 선언은 복사되지만 변수는 저장 위치가 다르며 이에 대한 수정 사항은 해당 서브셸 내에서만 볼 수 있습니다(이렇게 작동한다고 생각할 수 있습니다)fork
, 원한다면).
서브셸이 완료되면 변수 복사본이 더 이상 존재하지 않습니다. "외부" 셸은 수정되지 않은 자체 변수 복사본만 볼 수 있으므로 루프 외부에서 수정 사항이 손실되는 것처럼 보입니다.
이 경우 서브쉘 사용을 피할 수 있습니다간단한 리디렉션:
while read black; do
...
pass="false"
...
done < "$wd/wakeUp_blacklist.txt"
이제 while
루프는 기본 셸에서 실행되며 하위 셸은 생성되지 않습니다. 블랙리스트 파일의 내용은 여전히 루프의 표준 입력으로 제공됩니다.< file
리디렉션. 모든 변수는 동일한 환경에 존재합니다. 이 접근 방식은 또한쓸모없는 사용cat
.
일부 다른 쉘(특히 zsh
)에는 처음부터 이 동작이 없으며 파이프 오른쪽에 있는 변수를 자유롭게 수정할 수 있습니다.