프로세스의 반환 값을 어떻게 부정 할 수 있습니까?


103

프로세스가 반환하는 값을 부정 하는 단순하지만 크로스 플랫폼 부정 프로세스를 찾고 있습니다. 0을 어떤 값으로 매핑해야합니다! = 0 및 모든 값을! = 0에서 0으로 매핑해야합니다. 즉, 다음 명령은 "예, 존재하지 않는 경로가 존재하지 않습니다"를 반환해야합니다.

 ls nonexistingpath | negate && echo "yes, nonexistingpath doesn't exist."

! -연산자는 훌륭하지만 안타깝게도 셸에 독립적이지 않습니다.


호기심에서 기본적으로 bash를 포함하지 않는 특정 시스템을 염두에 두셨습니까? "크로스 플랫폼"은 * nix 시스템에서만 작동하는 답변을 수락했기 때문에 * nix 기반을 의미한다고 가정합니다.
Parthian Shot

답변:


115

이전에는 마지막 섹션으로 현재 첫 번째 섹션이 무엇인지 대답이 제시되었습니다.

POSIX Shell에는 !연산자가 포함되어 있습니다.

다른 문제에 대한 셸 사양을 살펴보면서 최근 (2015 년 9 월) POSIX 셸이 !연산자를 지원한다는 것을 알았습니다 . 예를 들어, 예약어 로 나열되고 파이프 라인 의 시작 부분에 나타날 수 있습니다. 여기서 간단한 명령은 'pipeline'의 특별한 경우입니다. 따라서 POSIX 호환 셸 에서 if문 및 while/ 또는 until루프에서도 사용할 수 있습니다 . 결과적으로 내 예약에도 불구하고 2008 년에 깨달은 것보다 더 널리 사용 가능할 것입니다. POSIX 2004 및 SUS / POSIX 1997을 빠르게 확인하면 !두 버전 모두에 존재 하는 것으로 나타났습니다 .

점을 유의 !운영자가에 나타나야 시작 파이프 라인의 전체 파이프 라인의 상태 코드 (즉, 부정 마지막 명령). 여기 예시들이 있습니다.

# Simple commands, pipes, and redirects work fine.
$ ! some-command succeed; echo $?
1
$ ! some-command fail | some-other-command fail; echo $?
0
$ ! some-command < succeed.txt; echo $?
1

# Environment variables also work, but must come after the !.
$ ! RESULT=fail some-command; echo $?
0

# A more complex example.
$ if ! some-command < input.txt | grep Success > /dev/null; then echo 'Failure!'; recover-command; mv input.txt input-failed.txt; fi
Failure!
$ ls *.txt
input-failed.txt

휴대용 대답-골동품 조개와 함께 작동

Bourne (Korn, POSIX, Bash) 스크립트에서 다음을 사용합니다.

if ...command and arguments...
then : it succeeded
else : it failed
fi

이것은 휴대 성이 뛰어납니다. '명령 및 인수'는 파이프 라인 또는 기타 복합 명령 시퀀스 일 수 있습니다.

not명령

'!' 연산자는 쉘에 내장되어 있든 운영 체제에서 제공하든 보편적으로 사용할 수 없습니다. 하지만 작성하기가 끔찍하게 어렵지는 않습니다. 아래 코드는 최소한 1991 년으로 거슬러 올라갑니다 (나는 이전 버전을 더 오래 전에 작성했다고 생각하지만). 나는 안정적으로 사용할 수 없기 때문에 스크립트에서 이것을 사용하지 않는 경향이 있습니다.

/*
@(#)File:           $RCSfile: not.c,v $
@(#)Version:        $Revision: 4.2 $
@(#)Last changed:   $Date: 2005/06/22 19:44:07 $
@(#)Purpose:        Invert success/failure status of command
@(#)Author:         J Leffler
@(#)Copyright:      (C) JLSS 1991,1997,2005
*/

#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "stderr.h"

#ifndef lint
static const char sccs[] = "@(#)$Id: not.c,v 4.2 2005/06/22 19:44:07 jleffler Exp $";
#endif

int main(int argc, char **argv)
{
    int             pid;
    int             corpse;
    int             status;

    err_setarg0(argv[0]);

    if (argc <= 1)
    {
            /* Nothing to execute. Nothing executed successfully. */
            /* Inverted exit condition is non-zero */
            exit(1);
    }

    if ((pid = fork()) < 0)
            err_syserr("failed to fork\n");

    if (pid == 0)
    {
            /* Child: execute command using PATH etc. */
            execvp(argv[1], &argv[1]);
            err_syserr("failed to execute command %s\n", argv[1]);
            /* NOTREACHED */
    }

    /* Parent */
    while ((corpse = wait(&status)) > 0)
    {
            if (corpse == pid)
            {
                    /* Status contains exit status of child. */
                    /* If exit status of child is zero, it succeeded, and we should
                       exit with a non-zero status */
                    /* If exit status of child is non-zero, if failed and we should
                       exit with zero status */
                    exit(status == 0);
                    /* NOTREACHED */
            }
    }

    /* Failed to receive notification of child's death -- assume it failed */
    return (0);
}

명령 실행에 실패하면 실패의 반대 인 '성공'을 반환합니다. 우리는 '아무것도 성공적으로하지 않음'옵션이 옳았는지 토론 할 수 있습니다. 아무것도 요청하지 않으면 오류를보고해야 할 수도 있습니다. ' "stderr.h"' 의 코드 는 간단한 오류보고 기능을 제공합니다. 저는 모든 곳에서 사용합니다. 요청시 소스 코드-저에게 연락하려면 내 프로필 페이지를 참조하십시오.


+1 for '명령 및 인수'는 파이프 라인 또는 기타 복합 명령 시퀀스 일 수 있습니다. 다른 솔루션은 파이프 라인에서 작동하지 않습니다
HVNSweeting

3
Bash 환경 변수는 !연산자 뒤에 와야합니다 . 이것은 나를 위해 일했습니다 :! MY_ENV=value my_command
Daniel Böhmer

1
부정은 전체 파이프에서 작동하므로 할 수는 ldd foo.exe | ! grep badlib없지만 ! ldd foo.exe | grep badlibfoo.exe 에서 badlib를 찾을 수 없는 경우 종료 상태가 0이되도록하려면 할 수 있습니다 . 의미 적으로 grep상태 를 반전하고 싶지만 전체 파이프를 반전하면 동일한 결과가 나타납니다.
Mark Lakata

@MarkLakata : 맞습니다 : POSIX 쉘 구문 및 관련 섹션을 참조하십시오 ( 최상의 룩앤필은 POSIX 로 이동 ). 그러나, 사용이 가능하다 ldd foo.exe | { ! grep badlib; }(이것은, 특히 세미콜론 귀찮은하지만, 당신이 전체 서브 쉘을 사용할 수 ()세미콜론없이). OTOH, 객체는 파이프 라인의 종료 상태를 반전시키는 것이며, 이것이 마지막 명령의 종료 상태입니다 (때로는 Bash 제외).
Jonathan Leffler

3
에 따르면 이 답변! 명령에 바람직하지 않은 상호 작용을 가지고 set -e는 명령 대신에만 0이 아닌 종료 코드로 성공의, 0 또는 0이 아닌 종료 코드와 성공의 원인 즉,. 0이 아닌 종료 코드 로만 성공할 수있는 간결한 방법이 있습니까?
Elliott Slaughter

40

Bash에서는! 명령 전에 연산자. 예를 들면 :

! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist"

17

시도해 볼 수 있습니다.

ls nonexistingpath || echo "yes, nonexistingpath doesn't exist."

또는 그냥 :

! ls nonexistingpath

5
불행히도 !와 함께 사용할 수 없거나 git bisect run적어도 방법을 모르겠습니다.
Attila O.

1
언제든지 쉘 스크립트에 항목을 넣을 수 있습니다. git bisect run은이 경우를 보지 못합니다 !.
toolforger

10

셸로 Bash가없는 경우 (예 : git 스크립트 또는 puppet exec 테스트) 다음을 실행할 수 있습니다.

echo '! ls notexisting' | bash

-> retcode : 0

echo '! ls /' | bash

-> retcode : 1


1
여기에 다른 이동 경로를 넣으려면 travis-ci의 스크립트 섹션에있는 행에 필요합니다.
kgraney

이것은 BitBucket 파이프 라인에도 필요했습니다.
KumZ

3
! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."

또는

ls nonexistingpath || echo "yes, nonexistingpath doesn't exist."

원래 질문에서 복사 한 경우 파이프 라인의 일부라고 생각했습니다. 가끔 조금 느립니다.)
Robert Gamble

이 두 문은 거의 동일하지만 남은 반환 값 $?은 다릅니다. 첫 번째 는 실제 존재하는 $?=1경우 떠날 수 nonexistingpath있습니다. 두 번째는 항상 떠납니다 $?=0. Makefile에서 이것을 사용하고 있고 반환 값에 의존해야하는 경우주의해야합니다.
Mark Lakata 2016 년

1

참고 : 때때로 !(command || other command).
여기 ! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."로 충분합니다.
서브 쉘이 필요 없습니다.

Git 2.22 (2019 년 2 분기)는 다음과 같은 더 나은 형태를 보여줍니다.

커밋 74ec8cf , commit 3fae7ad , commit 0e67c32 , commit 07353d9 , commit 3bc2702 , commit 8c3b9f7 , commit 80a539a , commit c5c39f4 (13 Mar 2019) by SZEDER Gábor ( szeder) .
참조 99e37c2 커밋 , 9f82b2a 커밋 , 900721e 커밋 에 의해 (2019년 3월 13일) 요하네스 Schindelin을 ( dscho) .
(Merged by Junio ​​C gitsterHamano -- in commit 579b75a , 25 Apr 2019)

t9811-git-p4-label-import: 파이프 라인 부정 수정

' t9811-git-p4-label-import.sh'에서 ' '테스트 tag that cannot be exported가 실행됩니다.

!(p4 labels | grep GIT_TAG_ON_A_BRANCH)

주어진 문자열이 ' p4 labels'로 인쇄되지 않았는지 확인합니다 . POSIX 에 따르면
이것은 문제가 됩니다 .

파이프 라인 예약어로 시작하는 경우 " !command1하부 쉘 명령이며, 애플리케이션이되도록한다 (시작에서 오퍼레이터 command1로부터 분리되어 !하나 개 이상의 <blank>캐릭터.
예약어의 동작은 !바로 다음에 (오퍼레이터가 지정한다. "

대부분의 일반적인 셸은 여전히이 ' !'를 "파이프 라인에서 마지막 명령의 종료 코드를 부정"하는 것으로 해석하지만 ' ' mksh/lksh대신 부정적인 파일 이름 패턴으로 해석하지 않습니다.
결과적으로 현재 디렉터리 ( ' main' 라는 단일 디렉터리 포함)의 경로 이름으로 구성된 명령을 실행하려고 시도합니다 . 물론 테스트에 실패합니다.

' !'와 ' (' 사이에 공백을 추가하여 간단히 수정할 수 있지만 대신 불필요한 하위 쉘을 제거하여 수정하겠습니다. 특히 커밋 74ec8cf


1

!없이, 서브 쉘없이, if없이, 최소한 bash에서 작동해야합니다.

ls nonexistingpath; test $? -eq 2 && echo "yes, nonexistingpath doesn't exist."

# alternatively without error message and handling of any error code > 0
ls nonexistingpath 2>/dev/null; test $? -gt 0 && echo "yes, nonexistingpath doesn't exist."
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.