명명되지 않은 파이프의 오류에 대한 제어 시퀀스

명명되지 않은 파이프의 오류에 대한 제어 시퀀스
$ 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

그러나 자체 소프트웨어 및/또는 독점 소프트웨어도 포함하는 경우 일반적인 문자열을 제공하기가 어렵습니다. 귀하의 적절한 명령 중 하나가 그러한 문자열을 사용하는 것이 가능합니다(가능성은 낮지만). 오류 메시지의 일반 설정을 살펴보고 사용되지 않을 문자열을 생성해야 합니다.

질문에서 알 수 있듯이 commandmayfailis 인 경우 없이 문자열을 사용하는 것으로 충분할 수 있습니다 .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<fOK. 그렇다면 다음 줄( getline<f다시)은 cmdmayfail확실히 from입니다. OK예상한 라인이 없을 때까지 모든 라인을 구문 분석하는 루프를 반복합니다 . 그 다음은 종료 상태입니다.

cmdmayfail생성되지 않는 한 이것은 잘 작동합니다.불완전한 줄. 예:

$ (printf 'foo\nincomplete line'; exit 22) | sed 's/^/OK\n/'; printf '%s\n' "${PIPESTATUS[0]}"

의 구현에 따라 sed도구는 다음과 같은 작업을 수행할 수 있습니다.

  1. 불완전한 줄을 전혀 무시하거나
  2. 이를 처리하고 누락된 개행 문자를 추가하거나
  3. 그대로 처리하세요.

실제로 당신은

  1. 출력의 일부를 놓치거나
  2. 그 줄이 불완전했는지 모르거나
  3. 종료 상태가 첨부된 행을 가져옵니다.

(3)의 경우 printf '\n%s\n' "${PIPESTATUS[0]}"도움이 될 수 있습니다. 마지막 줄이 cmdmayfail완료 되면 추가 빈 줄이 생성됩니다 . 이렇게 하면 awk코드가 알 수 있습니다.

cmdmayfail미드라인에서 강제 종료된 경우를 생각해 보자 . 그러면 불완전한 줄을 구문 분석하고 싶지 않을 수도 있습니다. 문제는 awk라인 양식이 완전한지 여부를 알기 위해 cmdmayfail다음(상태) 라인을 테스트해야 한다는 것입니다. 이에 대한 유용한 논리를 구현하는 것은 awk적어도 불편할 수 있습니다.


불완전한 라인을 가능한 한 빨리 감지하는 것이 좋으며,readBash에서는 이것을 할 수 있습니다. 단점은 속도 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. 라인은 쌍으로 이동합니다. (프로토콜을 보다 직관적으로 이해하려면 아래 예제를 참조하십시오.)

  1. 쌍( getline<f)의 첫 번째 줄을 읽습니다.
  2. 첫 번째 줄을 변수( first=$0)에 저장합니다.
  3. 쌍( getline<f)에서 두 번째 줄을 읽습니다.
  4. 첫 번째 줄( $first)을 분석합니다.
    • C두 번째 항목(current $0)이 의 완전한 라인 인 경우 cmdmayfail구문 분석할 수 있습니다.
    • I두 번째 줄이 의 불완전한 줄인 경우 cmdmayfail구문 분석을 원할 수도 있고 원하지 않을 수도 있습니다. E다음 쌍을 기대합니다 .
    • 그렇다면 E두 번째는 의 종료 상태입니다 cmdmayfail. 더 이상 쌍을 기 대해서는 안됩니다.
  5. 고리.

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
    

관련 정보