プロセスの戻り値を無効にするにはどうすればよいですか?


103

プロセスが返す値を否定する、単純ですがクロスプラットフォームのnegate - processを探しています。0をある値!= 0および任意の値!= 0から0にマップする必要があります。つまり、次のコマンドは「はい、存在しないパスは存在しません」を返す必要があります。

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

!!-演算子は素晴らしいですが、残念ながらシェルに依存しません。


好奇心から、デフォルトでbashを含まない特定のシステムを念頭に置いていましたか?「クロスプラットフォーム」とは、* nixシステムでのみ機能する回答を受け入れたため、* nixベースを意味していると思います。
パルティアショット

回答:


113

以前は、答えは現在の最初のセクションが最後のセクションとして提示されていました。

POSIXシェルには!演算子が含まれています

他の問題についてシェルの仕様を調べてみると、最近(2015年9月)、POSIXシェルが!演算子をサポートしていることに気づきました。たとえば、予約語としてリストされ、パイプラインの先頭に表示される場合があります。ここで、単純なコマンドは「パイプライン」の特殊なケースです。したがって、POSIX準拠のシェルで、ifステートメントwhileuntilループでも使用できます。その結果、私の予約にもかかわらず、おそらく2008年に私が気付いたよりも広く利用可能です。POSIX2004と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"'のコードは、簡単なエラー報告機能を提供します-私はどこでもそれを使用しています。リクエストに応じてソースコード-私のプロフィールページを参照して私に連絡してください。


'コマンドと引数'の+1は、パイプラインまたはその他のコマンドの複合シーケンスにすることができます。他のソリューションはパイプラインでは機能しません
HVNSweeting 2014年

3
Bash環境変数は!演算子の後に続く必要があります。これが私の仕事:! MY_ENV=value my_command
ダニエル・ベーマー

1
否定はパイプ全体で機能するため、実行できませんldd foo.exe | ! grep badlib! ldd foo.exe | grep badlib、foo.exeでbadlibが見つからない場合に、終了ステータスを0にしたい場合は実行できます。意味的にはgrepステータスを反転したいのですが、パイプ全体を反転しても同じ結果になります。
マークラカタ2016年

@MarkLakata:その通りです。POSIXシェルの構文と関連セクションを参照してください(最高のルックアンドフィールについては、POSIXにアクセスしてください)。しかし、それを使用することが可能であるldd foo.exe | { ! grep badlib; }(それは、特にセミコロン迷惑ですが、あなたが完全なサブシェルを使用することができます(し、)そしてセミコロンなし)。OTOH、目的はパイプラインの終了ステータスを反転することであり、それは最後のコマンドの終了ステータスです(Bashを除く場合もあります)。
ジョナサンレフラー2016年

3
この回答によると、!コマンドはとの望ましくない相互作用を持っていますset -e。つまり、コマンドは、ゼロ以外の終了コードだけで成功するのではなく、0またはゼロ以外の終了コードで成功します。ゼロ以外の終了コードでのみ成功させるための簡潔な方法はありますか?
エリオットスローター

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 bisectrunはそれを認識しません。
toolforger

10

どういうわけか、シェルとしてBashがない場合(例:gitスクリプト、またはpuppet execテスト)、次のコマンドを実行できます。

echo '! ls notexisting' | bash

-> retcode:0

echo '! ls /' | bash

-> retcode:1


1
ここに別のブレッドクラムを配置するには、travis-ciのスクリプトセクションの行にこれが必要です。
kgraney 2016

これは、BitBucketパイプラインにも必要でした。
kumZ 2016年

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

または

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

元の質問から、パイプラインの一部だと思った場合はコピーしました。時々少し遅いです;)
Robert Gamble

これらの2つのステートメントはほぼ同等ですが、残された戻り値は$?異なります。実際に存在する$?=1場合、最初のものは去るかもしれませんnonexistingpath。2番目のものは常に去り$?=0ます。これをMakefileで使用していて、戻り値に依存する必要がある場合は、注意する必要があります。
マークラカタ2016年

1

注:時々表示されます!(command || other command)
これで! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."十分です。
サブシェルは必要ありません。

Git 2.22(2019年第2四半期)は、次のようなより良いフォームを示しています。

コミット74ec8cfコミット3fae7adコミット0e67c32コミット07353d9コミット3bc2702コミット8c3b9f7コミット80a539aコミットc5c39f4(2019年3月13日)bySZEDERGábor(szederJohannes Schindelin()によるcommit 99e37c2commit 9f82b2acommit 900721e(2019年3月13日)を
参照してください。(合併によりJunio C浜野- -コミット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サブシェルコマンドで、アプリケーションがあることを確認しなければならない(の初めにオペレータcommand1から分離された!1つの以上<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.