124

我正在寻找一个简单但跨平台的否定进程,它否定进程返回的值。它应该将 0 映射到某个值 != 0 和任何值 != 0 到 0,即以下命令应该返回“是的,不存在的路径不存在”:

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

这 !- 操作员很棒,但不幸的是不是独立于外壳的。

4

7 回答 7

147

以前,答案是以现在的第一部分作为最后一部分给出的。

POSIX Shell 包括一个!运算符

围绕 shell 规范寻找其他问题,我最近(2015 年 9 月)注意到 POSIX shell 支持!运算符。例如,它被列为保留字,可以出现在管道的开头——其中一个简单的命令是“管道”的特例。因此,它也可以在if语句和while/或until循环中使用——在符合 POSIX 的 shell 中。因此,尽管我有所保留,但它可能比我在 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命令

这 '!' 运算符,无论是内置在您的 shell 中还是由 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"提供了简单的错误报告工具——我在任何地方都使用它。应要求提供源代码 - 请参阅我的个人资料页面与我联系。

于 2008-12-14T23:40:01.133 回答
46

在 Bash 中,使用 ! 命令前的运算符。例如:

! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist"
于 2008-12-14T22:25:43.437 回答
18

你可以试试:

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

要不就:

! ls nonexistingpath
于 2008-12-14T22:26:47.740 回答
12

如果您没有以某种方式使用 Bash 作为您的 shell(例如:Git 脚本或Puppet执行测试),您可以运行:

echo '! ls notexisting' | bash

-> 重新编码:0

echo '! ls /' | bash

-> 重新编码:1

于 2013-03-30T15:16:08.047 回答
6

没有!,没有 subshel​​l,没有 if 的解决方案,并且至少应该在 Bash 中工作:

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

# Alternatively without an error message and handling of any error code > 0
ls nonexistingpath 2>/dev/null; test $? -gt 0 && echo "yes, nonexistingpath doesn't exist."
于 2020-11-06T01:58:38.403 回答
4
! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."

或者

ls nonexistingpath || echo "yes, nonexistingpath doesn't exist."
于 2008-12-14T22:27:21.200 回答
3

注意:有时您会看到!(command || other command).
这里! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."就足够了。
不需要子外壳。

Git 2.22(2019 年第二季度)通过以下方式说明了更好的形式:

提交 74ec8cf提交 3fae7ad提交 0e67c32提交 07353d9提交 3bc2702提交 8c3b9f7提交 80a539a提交 c5c39f4(2019 年 3 月 13 日) by SZEDER Gábor ( szeder)
请参阅Johannes Schindelin ( ) 的commit 99e37c2commit 9f82b2acommit 900721e(2019 年 3 月 13 日(由Junio C Hamano 合并 -- --提交 579b75a中,2019 年 4 月 25 日)dscho
gitster

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是子shell命令,则应用程序应确保(开头的运算符与运算符command1分开!一个或多个<blank>字符。紧跟在运算符后面
的保留字的行为是未指定的。!( "

虽然大多数常见的 shell 仍然将此 ' !' 解释为“否定管道中最后一个命令的退出代码”,但 ' mksh/lksh' 不会并将其解释为负文件名模式。
结果,他们尝试运行由当前目录中的路径名组成的命令(它包含一个名为 ' main' 的目录),当然,这会导致测试失败。

!我们可以简单地通过在 ' ' 和 ' '之间添加一个空格来(修复它,但我们可以通过删除不必要的 subshel​​l 来修复它。特别是,提交 74ec8cf

于 2019-04-26T21:19:01.120 回答