다음을 사용하여 파일 이름을 배열로 구문 분석하는 스크립트가 있습니다.SO에 대한 Q&A:
unset ARGS
ARGID="1"
while IFS= read -r -d $'\0' FILE; do
ARGS[ARGID++]="$FILE"
done < <(find "$@" -type f -name '*.txt' -print0)
이는 매우 효율적이며 모든 유형의 파일 이름 변형을 완벽하게 처리합니다. 그러나 때때로 존재하지 않는 스크립트에 파일을 전달합니다. 예를 들면 다음과 같습니다.
$ findscript.sh existingfolder nonexistingfolder
find: `nonexistingfile': No such file or directory
...
일반적인 상황에서는 스크립트에서 유사한 종료 코드를 캡처하고 RET=$?
이를 사용하여 진행 방법을 결정하게 됩니다. 위의 프로세스 대체에서는 작동하지 않는 것 같습니다.
이 상황에서 올바른 절차는 무엇입니까? 반환 코드를 캡처하는 방법은 무엇입니까? 교체 프로세스 중에 문제가 발생했는지 확인하는 더 적절한 방법이 있습니까?
답변1
표준 출력에 반환 값을 반영하여 하위 쉘 프로세스의 반환 값을 쉽게 얻을 수 있습니다. 프로세스 교체도 마찬가지입니다.
while IFS= read -r -d $'\0' FILE ||
! return=$FILE
do ARGS[ARGID++]="$FILE"
done < <(find . -type f -print0; printf "$?")
내가 실행하면 마지막 줄은 -(또는 \0
적절하게 구분된 부분)반품 상태 는 입니다 find
. read
EOF를 받으면 1을 반환합니다. 따라서 $return
설정되는 유일한 시간은 $FILE
정보의 마지막 비트를 읽을 때입니다.
나는 여분의 줄을 printf
추가하는 것을 피 하곤 했습니다. 이는 NUL로 구분되지 않은 일반 실행 에서도 방금 읽은 데이터가 한 줄로 끝나지 않으면 0이 아닌 값을 반환하기 \n
때문에 중요합니다 . 따라서 마지막 줄이 ewline으로 끝나지 않으면 변수로 읽은 마지막 값이 반환 값이 됩니다.read
\0
\n
\n
위 명령을 실행한 후 다음을 수행하십시오.
echo "$return"
산출
0
공정교환부품을 바꾸면..
...
done < <(! find . -type f -print0; printf "$?")
echo "$return"
산출
1
더 간단한 데모:
printf \\n%s list of lines printed to pipe |
while read v || ! echo "$v"
do :; done
산출
pipe
실제로 반환하려는 내용이 프로세스 교체(또는 그런 식으로 읽은 하위 쉘 프로세스)에서 stdout에 마지막으로 쓴 것인 한, 그런 일이 $FILE
발생하면 항상 원하는 반환 상태가 전달됩니다. . 따라서 이 || ! return=...
섹션은 꼭 필요한 것은 아닙니다. 개념을 설명하는 데에만 사용됩니다.
답변2
사용하다공동 프로세스. coproc
내장 함수를 사용하면 하위 프로세스를 시작하고, 출력을 읽고, 종료 상태를 확인할 수 있습니다.
coproc LS { ls existingdir; }
LS_PID_=$LS_PID
while IFS= read i; do echo "$i"; done <&"$LS"
wait "$LS_PID_"; echo $?
디렉터리가 존재하지 않으면 wait
0이 아닌 상태 코드로 종료합니다.
현재 PID는 $LS_PID
호출되기 전에 설정 해제되므로 다른 변수에 복사해야 합니다. wait
바라보다Bash는 coproc을 기다리기 전에 *_PID 변수를 설정 해제합니다.더 알아보기.
답변3
프로세스 교체의 프로세스는 비동기식입니다. 셸은 해당 프로세스를 시작한 다음 종료 시점을 감지할 수 있는 방법을 제공하지 않습니다. 따라서 종료 상태를 얻을 수 없습니다.
종료 상태를 파일에 쓸 수 있지만 파일이 언제 쓰여졌는지 알 수 없기 때문에 이는 종종 서투른 작업입니다. 여기서 파일은 루프가 끝난 직후에 작성되므로 기다리는 것이 합리적입니다.
… < <(find …; echo $? >find.status.tmp; mv find.status.tmp find.status)
while ! [ -e find.status ]; do sleep 1; done
find_status=$(cat find.status; rm find.status)
또 다른 방법은 명명된 파이프와 백그라운드 프로세스(사용 가능 wait
)를 사용하는 것입니다.
mkfifo find_pipe
find … >find_pipe &
find_pid=$!
… <find_pipe
wait $find_pid
find_status=$?
두 가지 접근 방식 모두 적합하지 않다면 Perl, Python, Ruby와 같은 좀 더 강력한 언어를 사용해야 할 것 같습니다.
답변4
한 가지 방법은 다음과 같습니다.
status=0
token="WzNZY3CjqF3qkasn" # some random string
while read line; do
if [[ "$line" =~ $token:([[:digit:]]+) ]]; then
status="${BASH_REMATCH[1]}"
else
echo "$line"
fi
done < <(command; echo "$token:$?")
echo "Return code: $status"
아이디어는 명령이 완료된 후 임의의 토큰과 함께 종료 상태를 에코한 다음 bash 정규식을 사용하여 종료 상태를 찾아 추출하는 것입니다. 이 태그는 출력에서 찾을 고유 문자열을 만드는 데 사용됩니다.
이것은 일반적인 프로그래밍 측면에서 가장 좋은 방법은 아니지만 아마도 bash에서 처리하는 데 가장 덜 고통스러운 방법일 것입니다.