명령 출력을 stdout으로 보내고 오류를 stderr로 보내는 동안 명령을 grep으로 파이프하고 종료 상태를 얻으려면 어떻게 해야 합니까?

명령 출력을 stdout으로 보내고 오류를 stderr로 보내는 동안 명령을 grep으로 파이프하고 종료 상태를 얻으려면 어떻게 해야 합니까?

특정 사용 사례:

내가 사용하려는 curlURL은 -v자세한 디버그 정보를 일반처럼 stderr에 출력하고, 응답 본문을 일반처럼 stdout으로 출력하지만, 정규 표현식이 응답에 있는지 확인하고 grep찾을 수 없으면 0이 아닌 값으로 종료합니다. 이 명령의 가장 간단한 형식은 다음과 같습니다.

curl -v "${url}" | grep -q "${pattern}"

그러나 grep응답 본문은 소비됩니다.

내가 시도한 것:

  1. 나는 본 적이 "일치하는 라인뿐만 아니라 모든 라인을 출력하도록 grep을 설득하십시오.”, 이는 다음을 제안합니다.

    curl -v "${url}" | grep --color -E "${pattern}|$"
    

    하지만 이 색상만 일치 항목을 강조 표시하므로 pattern존재하지 않는 경우 0이 아닌 종료 코드를 제공하지 않습니다.

  2. 나도 본 적 있어"grep 종료 코드를 얻지만 모든 줄을 인쇄하는 방법은 무엇입니까?", 이는 다음을 나타냅니다.

    curl -v "${url}" | tee /dev/stderr | grep -q "${pattern}"
    

    종료 코드로 종료되고 응답 본문을 인쇄하지만 응답 본문을 stderr에 인쇄하므로 stderr 및 stdout 스트림을 별도로 grep유지하고 싶습니다 .curl

  3. 다음과 같은"파이프로 직접 출력 및 표준 출력", 나는 노력했다

    curl -v "${url}" | tee >(grep -q "${pattern}")
    

    그러나 이는 모든 것을 인쇄하고 stdout 및 stderr 스트림을 올바르게 분리하지만 grep종료 코드는 삭제됩니다.

현재 해결 방법:

지금까지 제가 생각할 수 있는 유일한 방법은 응답 본문을 임시 파일에 붙여넣는 것입니다.

curl -v "${url}" | tee /tmp/response
grep -q "${pattern}" /tmp/response

그러면 올바른 출력과 올바른 종료 코드가 표시됩니다.

하지만 임시 파일 없이 단일 파이프로 이 작업을 수행할 수 있는 방법이 있을까요?

답변1

두 번째 대답은 올바른 방향으로 가고 있습니다. 노력하다

{ curl -v "${url}" | tee /dev/fd/3 | grep -q "${pattern}";} 3>&1

간략한 설명:

  • 파일 설명자 3>&11(표준 출력)을 파일 설명자 3(표준 사용이 없는 가장 낮은 설명자)에 복사합니다. 이는 전체 파이프라인(즉, 전체 명령줄)의 표준 출력을 나타냅니다. 새 파일 설명자(3)는 전체 파이프라인에 유효합니다.

    어떤 숫자든 사용할 수 있습니다(적어도 최대 9까지). POSIX 쉘 명령 언어사양, 2.7 리디렉션,설명하다

    …모든 구현은 최소한 0부터 9까지 지원해야 합니다…

    POSIX 호환 쉘은 9보다 큰 숫자를 인식하지 못할 수도 있습니다. 그리고 bash(1)설명하다

    9보다 큰 파일 설명자를 사용하는 리디렉션은 주의해서 사용해야 합니다.

  • tee /dev/fd/3tee파일 설명자 3에 쓰라고 지시하므로 tee파이프의 표준 출력에 연결됩니다.

    • 지시문에서 사용한 것과 동일한 번호를 여기에 사용해야 합니다 n>&1.
    • Linux가 아닌 운영 체제에서는 유사한 파일 이름이 작동하지 않을 수 있습니다./dev/fd/n

    이것은 stdout과 아무 관련이 없습니다.tee, 이는 에 대한 파이프라인입니다  grep.

스티븐 차제라스지적:

grep -q패턴이 발견되면 종료합니다. tee이후에 출력이 기록되면 종료됩니다. GNU 구현은  tee최소한  -p SIGPIPE를 무시하고 여전히 출력을 얻는 대상에 계속 쓸 수 있습니다. 또한 일부 셸은 파이프라인의 마지막 구성 요소만 기다리므로 여기에서 grep -q패턴이 발견되면 스크립트의 나머지 부분이 전달됩니다.

형식적으로 첫 번째 사항을 확인했습니다. (패턴이 일치할 경우 파이프가 조기 종료될 수 있음) 1 .

저는 두 가지 문제를 모두 해결하기 위해 다음과 같은 향상된 솔루션을 제공합니다.

{ curl -v "${url}" | tee /dev/fd/3 | { grep -q "${pattern}" && cat > /dev/null;} } 3>&1

노트:

  • 데이터 흐름이라면아니요패턴과 일치하면 grep전체 스트림(즉, 입력)을 읽으므로 문제가 발생하지 않습니다. 그리고 이 경우에는 grep"실패"하므로 는  &&실행되지 않고 복합 명령은 에서 cat종료 코드를 반환합니다 . grep즉, 실패합니다(즉, 일치하지 않음).
  • 데이터 흐름이라면하다패턴과 일치하면 (Stéphane이 지적했듯이) grep패턴이 일치하면 종료되고 전체 입력(즉, 출력 curl | tee)은 읽혀지지 않습니다. 하지만 이 경우에는 grep"성공"할 것이므로  &&cat ~ 할 것이다실행하면 나머지 데이터가 흡수됩니다(EOF까지). 이렇게 하면 tee전체 데이터 스트림이 파이프에 기록될 수 있으며  curl모든 출력이 처리될 때까지 파이프(즉, 전체 명령줄)가 종료되지 않습니다.

기술적으로 이는 여전히 문제를 남깁니다. 데이터 흐름이 패턴과 일치하고 grep"성공"하면 파이프라인의 종료 상태는 의 종료 상태가 됩니다 cat. 왜 실패하는지 모르겠지만 (pipe) | cat > /dev/null이론적으로는 가능합니다. 이를 방지하기 위해 나는 다음을 제안합니다.

{ curl -v "${url}" | tee /dev/fd/3 | if grep -q "${pattern}"; then cat > /dev/null; true; else false; fi; } 3>&1

성공하면 명시적으로 true를 반환 grep하고 실패하면 false를 반환합니다.
______________
1개의  옵션 이 추가 -p되었습니다 .tee버전 8.24 (2015-07-03).


위의 변형 중 원하는 경우 관로다른 곳에서 오는 출력의 경우 curl평소와 같이 명령줄 끝에 파이프를 추가하면 됩니다.

{ curl …; fi; } 3>&1 | lpr

하지만 원한다면리디렉션파일에 저장하려면 출력 리디렉션을 삽입해야 합니다.앞으로이것  3>&1:

{곱슬...;수수료;}>결과물 파일3>&1

"파일로 파이핑"은 잘못된 용어라는 점을 기억하세요.

답변2

여기에 특별한 것이 없으면 grepawk를 사용하여 패턴 일치를 수행할 수도 있습니다.

curl -v "${url}" | awk -v p="$pattern" '$0 ~ p {found=1} {print} END {exit ! found}'
  • -v p="$pattern"awk 변수를 p쉘 변수의 값으로 설정하십시오.pattern
  • $0 ~ p {found=1}found행이 p변수의 정규식과 일치하면 awk 변수가 1로 설정됩니다.
  • {print}- 모든 행을 인쇄합니다(이 블록에는 조건이 제공되지 않으므로).
  • END {exit ! found} - 입력이 끝나면 음수 상태로 종료됩니다 found(따라서 패턴이 일치하면 0, 그렇지 않으면 1).

awk와 grep은 다양한 정규식을 지원하므로 패턴을 변경해야 할 수도 있습니다.

관련 정보