$ awk -v f=<(cmdmayfail) -e 'BEGIN { r = getline < f ; print r }'
-bash: cmdmayfail: command not found
0
위의 이름이 지정되지 않은 파이프 예에서는 awk
이름이 지정되지 않은 파이프의 오류를 알 수 없습니다.
$ awk -v f=<(cmdmayfail || echo "control sequence" ) -e 'BEGIN { r = getline < f ; print r }'
이 오류를 인식 하기 위해 awk
제어 시퀀스와 일부 오류 정보를 보내 위의 코드를 사용할 수 있습니다.
이미 많은 파일 형식을 알고 있는 경우 오류 제어 시퀀스 를 합법적인 파일의 오류 제어 시퀀스로 잘못 취급하지 않도록 file
이 응용 프로그램에 사용할 수 있는 합리적인 제어 시퀀스가 있습니까 ? awk
감사해요.
답변1
표준 Unix 명령 인 경우 commandmayfail
제어 순서 앞에 복잡한 텍스트(예: )를 붙이면 차이점을 __ERROR__CMDFAIL__:
충분히 이해할 수 있습니다 .awk
그러나 자체 소프트웨어 및/또는 독점 소프트웨어도 포함하는 경우 일반적인 문자열을 제공하기가 어렵습니다. 귀하의 적절한 명령 중 하나가 그러한 문자열을 사용하는 것이 가능합니다(가능성은 낮지만). 오류 메시지의 일반 설정을 살펴보고 사용되지 않을 문자열을 생성해야 합니다.
질문에서 알 수 있듯이 commandmayfail
is 인 경우 없이 문자열을 사용하는 것으로 충분할 수 있습니다 .file
:
답변2
출력이 cmdmayfail
상대적으로 작고 명령 자체가 코드의 다른 부분과 독립적으로 종료되는 경우 해당 출력을 변수에 저장하고 종료 상태를 첫 번째 줄로 전달할 수 있습니다. 코드는 다음 <()
과 같습니다.
out="$(cmdmayfail)"; printf '%s\n' "$?" "$out"
종료 상태를 확인 해야 awk
합니다 . getline<f
연속적으로 getline<f
의 실제 출력을 읽습니다 cmdmayfail
.
제한사항:
- Bash의 변수는 널 문자를 저장할 수 없습니다.
$()
모든 후행 개행 문자를 제거합니다. 그런 다음printf
정확히 하나만 추가합니다. 이를 방지하기 위한 번거로운 방법은 다음과 같습니다.out="$(cmdmayfail; s="$?"; printf X; exit "$s")"; printf '%s\n%s' "$?" "${out%X}"
cmdmayfail
블록 에서 출력을 처리한다는 사실은 조기 종료가 BEGIN
예상된다는 것을 나타낼 수 있습니다 cmdmayfail
. 아마도 이 솔루션으로 충분할 것입니다.
일반적으로 cmdmayfail
"끝없이"(즉, 종료할 때까지) 실행될 수도 있으며 의 ("끝없는") stdin을 처리하는 동안 해당 출력에서 읽고 싶을 수도 있습니다 awk
. 이러한 경우 위의 해결 방법은 작동하지 않습니다.
출력의 각 줄 앞에 cmdmayfail
고정된 상태 줄(예: OK
)을 추가하고 마지막으로 종료 상태가 있는 줄을 추가할 수 있습니다 cmdmayfail
. 코드는 다음 <()
과 같습니다.
cmdmayfail | sed 's/^/OK\n/'; printf '%s\n' "${PIPESTATUS[0]}"
예:
$ (printf '%s\n' foo "bar baz"; exit 7) | sed 's/^/OK\n/'; printf '%s\n' "${PIPESTATUS[0]}"
OK
foo
OK
bar baz
7
그런 다음 awk
코드 getline<f
가 OK
. 그렇다면 다음 줄( getline<f
다시)은 cmdmayfail
확실히 from입니다. OK
예상한 라인이 없을 때까지 모든 라인을 구문 분석하는 루프를 반복합니다 . 그 다음은 종료 상태입니다.
cmdmayfail
생성되지 않는 한 이것은 잘 작동합니다.불완전한 줄. 예:
$ (printf 'foo\nincomplete line'; exit 22) | sed 's/^/OK\n/'; printf '%s\n' "${PIPESTATUS[0]}"
의 구현에 따라 sed
도구는 다음과 같은 작업을 수행할 수 있습니다.
- 불완전한 줄을 전혀 무시하거나
- 이를 처리하고 누락된 개행 문자를 추가하거나
- 그대로 처리하세요.
실제로 당신은
- 출력의 일부를 놓치거나
- 그 줄이 불완전했는지 모르거나
- 종료 상태가 첨부된 행을 가져옵니다.
(3)의 경우 printf '\n%s\n' "${PIPESTATUS[0]}"
도움이 될 수 있습니다. 마지막 줄이 cmdmayfail
완료 되면 추가 빈 줄이 생성됩니다 . 이렇게 하면 awk
코드가 알 수 있습니다.
cmdmayfail
미드라인에서 강제 종료된 경우를 생각해 보자 . 그러면 불완전한 줄을 구문 분석하고 싶지 않을 수도 있습니다. 문제는 awk
라인 양식이 완전한지 여부를 알기 위해 cmdmayfail
다음(상태) 라인을 테스트해야 한다는 것입니다. 이에 대한 유용한 논리를 구현하는 것은 awk
적어도 불편할 수 있습니다.
불완전한 라인을 가능한 한 빨리 감지하는 것이 좋으며,read
Bash에서는 이것을 할 수 있습니다. 단점은 속도 read
가 느리다는 것입니다(그리고 Bash 변수는 널 문자를 저장할 수 없다는 점을 기억하십시오). 솔루션 예시:
# define this helper function in the main shell
encerr () { ( eval "$@" ) | (while IFS= read -r line; do printf 'C\n%s\n' "$line"; done; [ -n "$line" ] && printf 'I\n%s\n' "$line") ; printf 'E\n%s\n' "${PIPESTATUS[0]}"; }
# this part you want to put in <()
encerr cmdmayfail
그런 다음 내부의 사용자 정의 프로토콜을 디코딩해야 합니다 awk
. 라인은 쌍으로 이동합니다. (프로토콜을 보다 직관적으로 이해하려면 아래 예제를 참조하십시오.)
- 쌍(
getline<f
)의 첫 번째 줄을 읽습니다. - 첫 번째 줄을 변수(
first=$0
)에 저장합니다. - 쌍(
getline<f
)에서 두 번째 줄을 읽습니다. - 첫 번째 줄(
$first
)을 분석합니다.C
두 번째 항목(current$0
)이 의 완전한 라인 인 경우cmdmayfail
구문 분석할 수 있습니다.I
두 번째 줄이 의 불완전한 줄인 경우cmdmayfail
구문 분석을 원할 수도 있고 원하지 않을 수도 있습니다.E
다음 쌍을 기대합니다 .- 그렇다면
E
두 번째는 의 종료 상태입니다cmdmayfail
. 더 이상 쌍을 기 대해서는 안됩니다.
- 고리.
eval "$@"
참고 함수 내부에서 사용했습니다 . 이후에 작성한 내용은 encerr
두 번째로 평가되므로 일반적으로 다음과 같은 작업을 실행하고 싶습니다.
encerr 'cmd1 -opt foo'
또는
encerr "cmd1 -opt foo"
또는
encerr 'cmd1 -opt foo | cmd2'
기본적으로 이는 를 사용하여 원격 명령을 실행하는 데 사용하는 형식입니다 ssh
. 비교하다:
ssh a@b 'cmd1 -opt foo | cmd2'
또는 다음과 같이 함수를 작성할 수 있습니다.
encerr () { "$@" | …
다음과 같이 호출하세요.
encerr cmd1 -opt foo
다음과 비교 sudo
:
sudo cmd1 -opt foo
예( 와 함께 원래 함수 사용 eval
):
빈 출력으로 성공
$ encerr true E 0
빈 출력으로 인한 실패
$ # I'm redirecting stderr so "command not found" doesn't obfuscate the example $ encerr nonexisting-command-foo1231234 2>/dev/null E 127
라인 완성 후 성공
$ encerr 'date; sleep 1; date' C Mon Sep 30 09:07:40 CEST 2019 C Mon Sep 30 09:07:41 CEST 2019 E 0
라인 완성 후 실패
$ encerr 'printf "foo\nbar\n"; false' C foo C bar E 1
불완전한 줄 이후의 성공
$ encerr 'printf "foo bar\n89 baz"' C foo bar I 89 baz E 0
불완전한 줄 이후의 실패
$ encerr 'printf "\nThe first line was empty and this one was interru"; exit 33' C I The first line was empty and this one was interru E 33