Как я могу отрицать возвращаемое значение процесса? - PullRequest
77 голосов
/ 15 декабря 2008

Я ищу простой, но кроссплатформенный отрицательный -процесс, который сводит на нет значение, возвращаемое процессом. Он должен отображать 0 на какое-то значение! = 0 и любое значение! = 0 на 0, то есть следующая команда должна возвращать «да, несуществующий путь не существует»:

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

The! - оператор отличный, но, к сожалению, не зависит от оболочки.

Ответы [ 6 ]

74 голосов
/ 15 декабря 2008

Ранее ответ был представлен с тем, что является первым разделом в качестве последнего раздела.

POSIX Shell включает ! оператор

Обращаясь к спецификации оболочки по другим вопросам, я недавно (сентябрь 2015 г.) заметил, что оболочка POSIX поддерживает оператор !. Например, оно указано в качестве зарезервированного слова и может появляться в начале pipe - где простая команда является частным случаем 'pipe'. Поэтому его можно использовать в операторах if и циклах while или until - в POSIX-совместимых оболочках. Следовательно, несмотря на мои оговорки, он, вероятно, более широк, чем я предполагал в 2008 году. Быстрая проверка POSIX 2004 и SUS / POSIX 1997 показывает, что ! присутствовал в обеих этих версиях.

Обратите внимание, что оператор ! должен появляться в начале конвейера и отменяет код состояния всего конвейера (т. Е. Команда last ). Вот несколько примеров.

# 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

Это настолько портативно, насколько это возможно. «Команда и аргументы» могут быть конвейером или другой составной последовательностью команд.

A not команда

'!' Оператор, встроенный в вашу оболочку или предоставляемый o / s, не всегда доступен. Это не так сложно писать, хотя приведенный ниже код восходит по крайней мере к 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" предоставляет простые средства сообщения об ошибках - я использую его везде. Исходный код по запросу - смотрите страницу моего профиля, чтобы связаться со мной.

39 голосов
/ 15 декабря 2008

В Bash используйте! оператор перед командой. Например:

! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist"
17 голосов
/ 15 декабря 2008

Вы можете попробовать:

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

или просто:

! ls nonexistingpath
9 голосов
/ 30 марта 2013

Если как-то случится, что у вас нет Bash в качестве оболочки (например, git-скрипты или кукольные тесты), вы можете запустить:

echo '! ls notexisting' | bash

-> retcode: 0

echo '! ls /' | bash

-> retcode: 1

2 голосов
/ 15 декабря 2008
! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."

или

ls nonexistingpath || echo "yes, nonexistingpath doesn't exist."
0 голосов
/ 27 апреля 2019

Примечание: иногда вы увидите !(command || other command).
Здесь ! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist." достаточно.
Нет необходимости в субоболочке.

Git 2.22 (Q2 2019) иллюстрирует эту лучшую форму с:

Commit 74ec8cf , commit 3fae7ad , commit 0e67c32 , commit 07353d9 , commit 3bc2702 , коммит 8c3b9f7 , коммит 80a539a , коммит c5c39f4 (13 марта 2019 г.) от СЕДЕР Габор (szeder) .
См. коммит 99e37c2 , коммит 9f82b2a , коммит 900721e (13 марта 2019) от Йоханнес Шинделин (dscho) .
(Объединено с Junio ​​C Hamano - gitster - в коммит 579b75a , 25 апреля 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 является командой subshell, приложение должно убедиться, что оператор ( в начале command1 отделен от ! одним или более <blank> символов.
Поведение зарезервированного слова !, за которым сразу следует оператор (, не определено."

В то время как большинство обычных оболочек все еще интерпретируют это ! как «отрицание выхода» код последней команды в конвейере ", 'mksh/lksh' не и Вместо этого интерпретируйте его как шаблон с отрицательным именем файла.
В результате они пытаются выполнить команду, составленную из путей в текущем каталог (он содержит один каталог с именем 'main'), который, из Конечно, не проходит тест.

Мы могли бы это исправить, просто добавив пробел между '!' и '(', но вместо этого давайте исправим это, удалив ненужный подоболочек. В частности, Commit 74ec8cf

...