Bir sürecin dönüş değerini nasıl reddedebilirim?


101

Bir sürecin döndürdüğü değeri geçersiz kılan basit, ancak platformlar arası bir olumsuzlama süreci arıyorum . 0'ı bir değerle! = 0 ve herhangi bir değeri! = 0 ile 0 arasında eşlemelidir, yani aşağıdaki komut "evet, mevcut olmayan yol mevcut değil" döndürmelidir:

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

! - operatör harika ama maalesef kabuktan bağımsız değil.


Merak ettiğim için, aklınızda varsayılan olarak bash içermeyen belirli bir sistem var mıydı? "Çapraz platform" derken sadece * nix tabanlı demek istediğinizi varsayıyorum, çünkü yalnızca bir * nix sisteminde işe yarayacak bir yanıtı kabul etmişsinizdir.
Parthian Shot

Yanıtlar:


110

Daha önce, yanıt şu anda ilk bölüm olan son bölüm olarak sunuluyordu.

POSIX Kabuğu bir !operatör içerir

Diğer sorunlar için kabuk özelliklerini araştırırken, geçenlerde (Eylül 2015) POSIX kabuğunun bir !operatörü desteklediğini fark ettim . Örneğin, ayrılmış bir sözcük olarak listelenir ve bir boru hattının başlangıcında görünebilir - burada basit bir komut özel bir 'boru hattı' durumudur. Bu nedenle POSIX uyumlu kabuklarda ifdeyimlerde ve / whileveya untildöngülerde de kullanılabilir . Sonuç olarak, çekincelerime rağmen, muhtemelen 2008'de fark ettiğimden daha yaygın olarak mevcut. POSIX 2004 ve SUS / POSIX 1997'nin hızlı bir kontrolü, !her iki sürümde de mevcut olduğunu gösteriyor .

O Not !operatörü de yer almalıdır başından boru hattının ve tüm boru hattının durum kodunu (yani olumsuzlar son komutu). İşte bazı örnekler.

# 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

Taşınabilir cevap - antika kabuklarla çalışır

Bir Bourne (Korn, POSIX, Bash) komut dosyasında şunları kullanıyorum:

if ...command and arguments...
then : it succeeded
else : it failed
fi

Bu olabildiğince taşınabilir. 'Komut ve argümanlar' bir ardışık düzen veya başka bir bileşik komut dizisi olabilir.

Bir notkomut

'!' Operatör, ister kabuğunuzda yerleşik olsun ister o / s tarafından sağlanan, evrensel olarak mevcut değildir. Yine de yazmak çok zor değil - aşağıdaki kodun geçmişi en az 1991 yılına dayanıyor (yine de bir önceki sürümü daha uzun süre önce yazdığımı düşünüyorum). Yine de bunu komut dosyalarımda kullanma eğiliminde değilim çünkü güvenilir bir şekilde mevcut değil.

/*
@(#)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);
}

Bu, komutu yürütemediğinde başarısızlığın tersi olan 'başarı'yı ​​döndürür. 'Hiçbir şeyi başarılı bir şekilde yapma' seçeneğinin doğru olup olmadığını tartışabiliriz; belki de herhangi bir şey yapması istenmediğinde bir hata bildirmesi gerekir. ' "stderr.h"' İçindeki kod basit hata raporlama olanakları sağlar - her yerde kullanırım. Talep üzerine kaynak kodu - benimle iletişime geçmek için profil sayfama bakın.


+1 'komut ve bağımsız değişkenler' bir ardışık düzen veya başka bir bileşik komut dizisi olabilir. Diğer çözümler ardışık
düzen

3
Bash ortam değişkenleri !operatörü takip etmelidir . Bu benim için çalıştı:! MY_ENV=value my_command
Daniel Böhmer

1
Yapamayacağı böylece olumsuzluk, tüm boru üzerinde çalışır ldd foo.exe | ! grep badlibfakat yapabileceğin ! ldd foo.exe | grep badlibsen badlib ise çıkış durumu 0 olmasını istiyorsanız değil Foo.exe bulundu. Anlamsal olarak grepdurumu tersine çevirmek istersiniz , ancak tüm boruyu ters çevirmek aynı sonucu verir.
Mark Lakata

@MarkLakata: haklısın: POSIX bkz kabuk sözdizimi ve ilgili bölümleri (gidin POSIX iyi look'n'feel için). Ancak, kullanmak mümkündür ldd foo.exe | { ! grep badlib; }(o, özellikle noktalı virgül bir sıkıntı olsa, siz tam bir alt kabuk kullanabilirsiniz (ve )ve noktalı virgül olmadan). OTOH, amaç boru hattının çıkış durumunu tersine çevirmektir ve bu, son komutun çıkış durumudur (bazen Bash dışında).
Jonathan Leffler

3
Göre bu cevap! komutu ile istenmeyen bir etkileşim vardır set -eo komutu yerine sadece sıfırdan farklı çıkış kodu ile başarı, bir 0 ya da sıfır olmayan çıkış kodu ile başarılı olur, yani. Yalnızca sıfır olmayan bir çıkış koduyla başarılı olmanın kısa bir yolu var mı ?
Elliott Slaughter

40

Bash'de! komuttan önce operatör. Örneğin:

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

17

Deneyebilirsin:

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

ya da sadece:

! ls nonexistingpath

5
Ne yazık ki !kullanılamaz git bisect runveya en azından nasıl olduğunu bilmiyorum.
Attila O.

1
Her zaman bir kabuk betiğine bir şeyler koyabilirsiniz; git bisect run, !bu durumda görmez .
toolforger

10

Bir şekilde kabuğunuz olarak Bash'iniz yoksa (örneğin: git komut dosyaları veya kukla yürütme testleri) çalıştırabilirsiniz:

echo '! ls notexisting' | bash

-> yeniden kodlama: 0

echo '! ls /' | bash

-> yeniden kodu: 1


1
Buraya başka bir içerik haritası koymak için, bu, travis-ci'nin kod bölümündeki satırlar için gereklidir.
kgraney

Bu, BitBucket ardışık düzenleri için de gerekliydi.
KumZ

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

veya

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

Orijinal sorudan kopyaladım, bunun bir boru hattının parçası olduğunu düşündüm, bazen biraz yavaşım;)
Robert Gamble

Bu iki ifade hemen hemen eşdeğerdir, ANCAK içeride kalan dönüş değeri $?farklı olacaktır. Birincisi, $?=1eğer nonexistingpathgerçek varsa, gidebilir . İkincisi her zaman gider $?=0. Bunu bir Makefile'da kullanıyorsanız ve dönüş değerine güvenmeniz gerekiyorsa, dikkatli olmalısınız.
Mark Lakata

1

Not: bazen göreceksiniz !(command || other command).
İşte ! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist."yeter.
Alt kabuğa gerek yok.

Git 2.22 (2019'un 2. Çeyreği) şunlarla daha iyi bir form göstermektedir:

74ec8cf , commit 3fae7ad , commit 0e67c32 , commit 07353d9 , commit 3bc2702 , commit 8c3b9f7 , commit 80a539a , commit c5c39f4 (13 Mart 2019) by SZEDER Gábor ( szeder) .
Bkz 99e37c2 işlemek , 9f82b2a işlemek , 900721e taahhüt tarafından (13 Mar 2019) Johannes Schindelin ( dscho) .
(Göre Birleştirilmiş Junio Cı Hamano - gitster- içinde işlemek 579b75a , 25 Nisan 2019)

t9811-git-p4-label-import: ardışık düzen olumsuzlamasını düzelt

' t9811-git-p4-label-import.sh' İçinde test ' tag that cannot be exported' çalışır:

!(p4 labels | grep GIT_TAG_ON_A_BRANCH)

verilen dizenin ' p4 labels' tarafından basılmadığını kontrol etmek için .
Bu sorunlu çünkü POSIX'e göre :

"Boru hattı ayrılmış sözcükle başlıyorsa !ve command1bir alt kabuk komutuysa, uygulama, (başlangıcındaki operatörün bir veya daha fazla karakterle command1ayrılmasını sağlamalıdır . Ayrılmış sözcüğün hemen ardından operatör tarafından izlenen davranışı belirtilmez. "!<blank>
!(

En yaygın kabuklar hala bunu " !ardışık mksh/lkshdüzenindeki son komutun çıkış kodunu olumsuzla" olarak yorumlasa da, yapmayın ve bunun yerine bir negatif dosya adı modeli olarak yorumlayın.
Sonuç olarak, geçerli dizindeki yol adlarından oluşan bir komutu çalıştırmaya çalışırlar (' main' adlı tek bir dizin içerir ) ve bu da elbette testi geçemez.

Bunu basitçe ' !' ve ' (' arasına bir boşluk ekleyerek düzeltebiliriz , ancak bunun yerine gereksiz alt kabuğu kaldırarak düzeltelim. Özellikle, 74ec8cf Commit


1

Çözüm! Olmadan, alt kabuksuz, if olmadan ve en azından bash'da çalışmalıdır:

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."
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.