我正在寻找一个简单且跨平台的否定过程,用于否定进程返回的值。它应该将0映射为某个不等于0的值,将任何不等于0的值映射为0,即下面的命令应该返回"yes, nonexistingpath不存在":
ls nonexistingpath | negate && echo "yes, nonexistingpath doesn't exist."
! - 运算符很棒,但不幸的是不与shell独立。
我正在寻找一个简单且跨平台的否定过程,用于否定进程返回的值。它应该将0映射为某个不等于0的值,将任何不等于0的值映射为0,即下面的命令应该返回"yes, nonexistingpath不存在":
ls nonexistingpath | negate && echo "yes, nonexistingpath doesn't exist."
! - 运算符很棒,但不幸的是不与shell独立。
以前,答案是将现在的第一部分放在最后一部分呈现出来。
!
运算符在查找其他问题的shell规范时,我最近(2015年9月)注意到POSIX shell支持一个!
运算符。例如,它被列为保留字,可以出现在pipeline的开头——其中简单命令是“pipeline”的特殊情况。因此,在符合POSIX标准的shell中,它也可以用于if
语句和while
或until
循环中。因此,尽管我有所保留,但它可能比我2008年意识到的更普遍。快速查看POSIX 2004和SUS/POSIX 1997表明,!
存在于这两个版本中。
请注意,!
运算符必须出现在pipeline的开头,并否定整个pipeline(即最后一个命令)的状态代码。以下是一些示例。
# 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 还是由操作系统提供的,都不是普遍可用的。但它并不难写 - 下面的代码至少可以追溯到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);
}
当命令执行失败时,该代码返回“success”(成功)的相反结果。我们可以讨论“成功不执行任何操作”选项是否正确;也许当它没有被要求执行任何操作时,它应该报告一个错误。在"stderr.h"
中的代码提供了简单的错误报告工具-我在任何地方都使用它。如需源代码,请联系我个人资料页面。
!
运算符。以下命令对我有效:! MY_ENV=value my_command
。 - Daniel Böhmerldd foo.exe | ! grep badlib
,但是如果您希望在 foo.exe 中未找到 badlib 时退出状态为0,则可以执行 ! ldd foo.exe | grep badlib
。从语义上讲,您想要反转 grep
状态,但是反转整个管道会产生相同的结果。 - Mark Lakataldd foo.exe | { ! grep badlib; }
(虽然这很麻烦,特别是那个分号;您可以使用带有(
和)
但没有分号的完整子shell)。另一方面,目标是反转管道的退出状态,这是最后一个命令的退出状态(除了在Bash中) 。 - Jonathan Leffler!
命令与set -e
存在一种不理想的交互方式,即它会导致命令以0或非0的退出码成功执行,而不是仅以非0退出码成功执行。有没有一种简洁的方法可以使其仅在具有非0退出码时成功执行? - Elliott Slaughterecho "hey" && ! echo "ho" ; echo $?
执行我们想要的操作,但 echo "hey" && !echo "ho" ; echo $?
将展开为依赖于先前命令的某些内容,例如 echo "hey" && echo "hey" && ! echo "ho" ; echo $? "ho" ; echo $?
:) - qbolec! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist"
ls nonexistingpath || echo "yes, nonexistingpath doesn't exist."
! ls nonexistingpath
git bisect run
中无法使用!
,或者至少我不知道如何使用。 - Attila O.!
。 - toolforgerecho "! ls nonexistingpath" > /tmp/bisect.sh; chmod u+x /tmp/bisect.sh; git bisect run /tmp/bisect.sh
- M. Justin如果出现以下情况,您的shell不是Bash(例如Git脚本或Puppet exec测试),您可以运行:
echo'!ls notexisting' | bash
-> 返回码:0
echo'!ls /' | bash
-> 返回码:1
无需使用 !,无需子Shell,无需 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."
test $? -ne 0
。 - kelvintest
来否定/反转结果代码? - Peter Mortensen! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."
或者
ls nonexistingpath || echo "yes, nonexistingpath doesn't exist."
$?
中的返回值将不同。如果 nonexistingpath
确实存在,则第一个语句可能会留下 $?=1
。而第二个语句始终留下 $?=0
。如果您在 Makefile 中使用它并需要依赖于返回值,则必须小心。 - Mark Lakata!(命令||其他命令)
。!ls nonexistingpath && echo“是的,nonexistingpath不存在。”
就足够了。提交 74ec8cf, 提交 3fae7ad, 提交 0e67c32, 提交 07353d9, 提交 3bc2702, 提交 8c3b9f7, 提交 80a539a, 提交 c5c39f4 (2019年3月13日) 作者为SZEDER Gábor (szeder
)。
请查看提交 99e37c2, 提交 9f82b2a, 提交 900721e (2019年3月13日) 作者为Johannes Schindelin (dscho
)。
(由Junio C Hamano -- gitster
--合并于提交 579b75a,2019年4月25日)
t9811-git-p4-label-import
: fix pipeline negationIn '
t9811-git-p4-label-import.sh
', the test 'tag that cannot be exported
' runs:!(p4 labels | grep GIT_TAG_ON_A_BRANCH)
to check that the given string is not printed by '
p4 labels
'.
This is problematic, because according to POSIX:"If the pipeline begins with the reserved word
!
andcommand1
is a subshell command, the application shall ensure that the(
operator at the beginning ofcommand1
is separated from the!
by one or more<blank>
characters.
The behavior of the reserved word!
immediately followed by the(
operator is unspecified."While most common shells still interpret this '
!
' as "negate the exit code of the last command in the pipeline", 'mksh/lksh
' don't and interpret it as a negative file name pattern instead.
As a result, they attempt to run a command made up of the pathnames in the current directory (it contains a single directory called 'main
'), which, of course, fails the test.We could fix it simply by adding a space between the '
!
' and '(
', but instead let's fix it by removing the unnecessary subshell. In particular, Commit 74ec8cf
ls
is not a good tool for testing the existence of a path. If you want to test that a path exists, a perfectly good tool exists for that; it's conveniently calledtest
(often spelled[
).if ! test -e nonextantpath; then echo "does not exist" >&2; fi
- William Pursell