Bash에서의 Bang(!) 사용법

Bash에서의 Bang(!) 사용법

bash 소스 코드를 읽고 있는데BNF 구문Bash의 경우 다음과 같습니다.

<pipeline_command> ::= <pipeline>
                    |  '!' <pipeline>
                    |  <timespec> <pipeline>
                    |  <timespec> '!' <pipeline>
                    |  '!' <timespec> <pipeline>

<pipeline> ::=
          <pipeline> '|' <newline_list> <pipeline>
       |  <command>

!이것은 명령도 일종의 파이프라는 것을 의미합니까 ?

! ls작동하지만 와 동일합니다 ls.

! time ls또한 작동합니다.

이것은 |파이프와 완전히 다릅니다.

!Bash에서 어떻게 사용하나요? 파이프인가요?

답변1

Bash 매뉴얼에서: "예약어 !가 파이프 앞에 오면 해당 파이프의 종료 상태는 종료 상태의 논리적 부정입니다."

문법을 잘못 읽으셨군요. 구문에서 말하는 것은 |를 앞에 파이프와 함께 넣을 수 있다는 것입니다.

답변2

느낌표는 명령/파이프의 반환 코드를 논리적으로 반전시킵니다(예:배쉬 매뉴얼):

if true ;    then echo 'this prints' ; fi
if ! false ; then echo 'this also prints' ; fi
if ! true ;  then echo 'this does not print' ; fi

파이프의 반환 코드는 (보통) 마지막 명령의 반환 코드이므로 폭발이 반전됩니다.

if ! true | false ; then echo 'again, this also prints' ; fi

공교롭게도 Bash 소스 배포판에서 해당 BNF 파일을 볼 수 없으며 Bash는 Bash 4.2(2011년 출시) 이후 여러 느낌표를 허용하기 때문에 인용된 구문이 완전히 정확하지 않습니다.

if ! ! true ; then echo 'this prints too' ; fi

그러나 이는 표준이 아닙니다. 예를 들어 zsh와 Dash는 이와 관련하여 잘 수행되지 않습니다.

답변3

파이프를 다음과 같이 정의하십시오.하나또는 여러 명령은 실제로 파이프가 포함되지 않더라도 단일 명령이 파이프이기도 함을 의미합니다. 장점은 !부정 연산자로서 명령과 파이프에 대해 별도로 정의할 필요가 없고 파이프에 적용하기 위해서만 정의하면 된다는 것입니다.

에서는 개별 명령뿐만 아니라 전체 파이프라인의 종료 상태를 취소 ! cmd1 | cmd2합니다 . 기본적으로 파이프의 종료 상태는 가장 오른쪽 명령의 종료 상태입니다.!cmd1


마찬가지로,목록;, &, 또는 로 &&연결된 또 다른 파이프 입니다 ||. 따라서 단일 파이프도 목록이고 단일 명령도 목록입니다. 그런 다음 이와 같은 명령이 take 와 키워드 if사이의 목록으로 정의 되면 명령 정의의 일부로 단일 명령과 단일 파이프가 자동으로 포함됩니다.ifthen

  • 두 개의 파이프로 구성된 목록(그 중 하나에는 하나의 명령만 포함됨):

    if IFS= read -r response && echo "$response" | grep foo; then
    
  • 단일 파이프로 구성된 목록:

    if echo "$foo" | grep foo; then
    
  • 단일 파이프로 구성된 목록(그 자체에는 단일 명령만 포함됨):

    if true; then
    

답변4

다른 답변을 기반으로 한 몇 가지 추가 사항:

  • (간접적으로) 지적했듯이체프너의 답변대신 연산자 는 !구문 요소에 대한 선택적 접두사로 정의됩니다 . 이것은 당신이 말할 수 없는 결과를 가져올 것이다<pipeline_command><command>

      cmd1 | ! cmd2
    

    또는

    ! cmd1 | ! cmd2
    

    "전체 파이프라인"의 종료 상태만 무효화할 수 있습니다. Cheppner가 지적했듯이 "파이프라인"은 단일 명령이 될 수 있으므로 다음을 수행할 수 있습니다.

    ! cmd1 && ! cmd2; ! cmd3 || ! cmd4
    

    그러나 이것은 어리석은 일입니다. before는 아무 작업도 !수행하지 cmd2않습니다. before는 명령 끝의 값 !에만 영향을 미치며 AND와 OR를 교환하여 나머지 두 개를 제거할 수 있습니다.cmd4$?

      cmd1  ||  cmd2;   cmd3  &&  cmd4
    

    마찬가지로 while ! cmd로 대체할 수 있습니다 until cmd.

  • <pipeline>a가 앞에 오는 A는 !- <pipeline_command>다른 구문 요소가 됩니다. 그러므로 말하다

    ! ! cmd1
    

    와 같은 것이 유효한 산술 확장과는 다릅니다.$((! ! value))$((! ! ! value))

  • 참고하세요POSIX동일한 구문을 정의하지만 다른 요소 이름을 사용합니다. 문제의 BNF는 POSIX에서 다음과 같이 나타납니다.

    pipeline         :      pipe_sequence
                     | Bang pipe_sequence
    
    pipe_sequence    :                             command
                     | pipe_sequence '|' linebreak command
    

    여기서 Banga의 %token값은 입니다 '!'.

관련 정보