Как я могу отрицать возвращаемое значение процесса?

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

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

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

14.12.2008 22:20:36
Из любопытства, вы имели в виду конкретную систему, которая по умолчанию не включает bash? Я предполагаю, что под «кроссплатформенностью» вы имели в виду * nix-based, поскольку вы приняли ответ, который будет работать только в * nix-системе.
Parthian Shot 14.07.2014 15:36:17
6 ОТВЕТОВ
РЕШЕНИЕ

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

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

Обращаясь к спецификации оболочки для других проблем, я недавно (сентябрь 2015 года) заметил, что оболочка POSIX поддерживает !оператор. Например, оно указано в качестве зарезервированного слова и может появляться в начале конвейера, где простая команда - это особый случай «конвейера». Поэтому его можно использовать в ifоператорах и / whileили untilциклах - в POSIX-совместимых оболочках. Следовательно, несмотря на мои оговорки, он, вероятно, более широк, чем я предполагал в 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команда

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

93
29.11.2017 17:38:48
+1 для «команда и аргументы» может быть конвейером или другой составной последовательностью команд. Другие решения не работают с конвейером
HVNSweeting 23.06.2014 07:40:54
Переменные среды Bash должны следовать за !оператором. Это сработало для меня:! MY_ENV=value my_command
Daniel Böhmer 22.03.2016 14:49:46
Отрицание работает на всем канале, поэтому вы не можете этого сделать, ldd foo.exe | ! grep badlibно вы можете сделать это, ! ldd foo.exe | grep badlibесли хотите, чтобы состояние выхода было 0, если badlib не найден в foo.exe. Семантически вы хотите инвертировать grepстатус, но инвертирование всей трубы дает тот же результат.
Mark Lakata 9.05.2016 23:08:07
@MarkLakata: Вы правы: посмотрите синтаксис оболочки POSIX и связанные с ней разделы ( для лучшего внешнего вида обратитесь к POSIX ). Тем не менее, целесообразно использовать ldd foo.exe | { ! grep badlib; }(хотя это неудобство, особенно точка с запятой, вы можете использовать полную подоболочку с (и )без точки с запятой). OTOH, цель состоит в том, чтобы инвертировать состояние выхода конвейера, и это состояние выхода последней команды (иногда, кроме Bash).
Jonathan Leffler 9.05.2016 23:22:50
В соответствии с этим ответом! команда имеет нежелательное взаимодействие с set -e, т.е. вызывает команду , чтобы добиться успеха с кодом 0 или ненулевая выхода, а сменив только с кодом ненулевым. Есть ли краткий способ добиться успеха только с ненулевым кодом выхода?
Elliott Slaughter 16.06.2018 06:35:14

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

! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist"
40
14.12.2008 22:25:43

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

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

или просто:

! ls nonexistingpath
17
14.12.2008 22:26:47
К сожалению, !не может быть использован с git bisect run, или, по крайней мере, я не знаю как.
Attila O. 5.07.2012 16:30:14
Вы всегда можете поместить материал в скрипт оболочки; git bisect run не видит !в этом случае.
toolforger 17.04.2019 20:10:15
! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."

или

ls nonexistingpath || echo "yes, nonexistingpath doesn't exist."
3
14.12.2008 23:40:09
Я скопировал, если из исходного вопроса, я думал, что это было частью конвейера, я иногда немного медленный;)
Robert Gamble 14.12.2008 23:42:32
Эти два утверждения почти эквивалентны, НО возвращаемое значение $?будет другим. Первый может уйти, $?=1если nonexistingpathон действительно существует. Второй всегда уходит $?=0. Если вы используете это в Makefile и вам нужно полагаться на возвращаемое значение, вы должны быть осторожны.
Mark Lakata 9.05.2016 22:47:07

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

echo '! ls notexisting' | bash

-> Реткод: 0

echo '! ls /' | bash

-> Реткод: 1

10
30.03.2013 15:16:08
Чтобы поместить сюда еще одну крошку, это необходимо для строк в разделе сценариев travis-ci.
kgraney 2.07.2016 05:24:34
Это также было необходимо для конвейеров BitBucket.
KumZ 23.11.2016 19:41:48

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

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

Зафиксируйте 74ec8cf , зафиксируйте 3fae7ad , зафиксируйте 0e67c32 , зафиксируйте 07353d9 , зафиксируйте 3bc2702 , зафиксируйте 8c3b9f7 , зафиксируйте 80a539a , зафиксируйте c5c39f4 (13 марта 2019 г.) от SZEDER Gábor ( 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является командой подоболочки, приложение должно гарантировать, что (оператор в начале command1отделен от !одного или нескольких <blank>символов.
Поведение зарезервированного слова, !за которым сразу следует (оператор, не определено. "

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

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

1
26.04.2019 21:19:01