मैं किसी प्रक्रिया के रिटर्न-वैल्यू को कैसे नकार सकता हूं?


103

मैं एक सरल, लेकिन क्रॉस-प्लेटफ़ॉर्म निगेट -प्रॉसेस की तलाश कर रहा हूं, जो उस मूल्य को नकारता है जो एक प्रक्रिया रिटर्न करता है। इसे 0 से कुछ मान पर जाना चाहिए! = 0 और किसी भी मूल्य! = 0 से 0 तक, अर्थात निम्नलिखित कमांड को "हाँ, nonexistingpath मौजूद नहीं है" लौटना चाहिए:

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

! - ऑपरेटर महान है लेकिन दुर्भाग्य से शेल-स्वतंत्र नहीं है।


जिज्ञासा से बाहर, क्या आपके पास एक विशिष्ट प्रणाली है जो डिफ़ॉल्ट रूप से बैश को शामिल नहीं करती है? मैं "क्रॉस-प्लेटफ़ॉर्म" से आपको सिर्फ निक्स-आधारित मानता हूं, क्योंकि आपने एक उत्तर स्वीकार किया है जो केवल एक * निक्स प्रणाली पर काम करेगा।
पार्थियन ने

जवाबों:


115

पहले, उत्तर को अंतिम अनुभाग के रूप में अब पहले खंड के साथ प्रस्तुत किया गया था।

POSIX Shell में एक !ऑपरेटर शामिल होता है

अन्य मुद्दों के लिए शेल विनिर्देश के आसपास, मैंने हाल ही में (सितंबर 2015) देखा कि POSIX शेल एक !ऑपरेटर का समर्थन करता है । उदाहरण के लिए, इसे एक आरक्षित शब्द के रूप में सूचीबद्ध किया गया है और एक पाइपलाइन की शुरुआत में दिखाई दे सकता है - जहां एक साधारण कमांड 'पाइपलाइन' का एक विशेष मामला है। इसलिए यह प्रयोग किया जा सकता ifबयानों और whileया untilPOSIX अनुरूप गोले में - बहुत छोरों। नतीजतन, मेरे आरक्षण के बावजूद, यह संभवत: 2008 में वापस महसूस किए जाने की तुलना में अधिक व्यापक रूप से उपलब्ध है। पोसिक्स 2004 और एसयूएस / पोसिक्स 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

पोर्टेबल उत्तर - प्राचीन गोले के साथ काम करता है

एक बॉर्न (कोर्न, पोसिक्स, बैश) स्क्रिप्ट में, मैं उपयोग करता हूं:

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

यह जितना मिलता है उतना ही पोर्टेबल होता है। 'कमांड और दलीलें' एक पाइपलाइन या कमांड के अन्य यौगिक अनुक्रम हो सकते हैं।

एक notआज्ञा

'!' ऑपरेटर, चाहे आपके शेल में अंतर्निहित हो या ओ / एस द्वारा प्रदान किया गया हो, सार्वभौमिक रूप से उपलब्ध नहीं है। यह लिखना बहुत कठिन नहीं है, हालांकि - नीचे दिए गए कोड कम से कम 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 के लिए 'कमांड और दलीलें' एक पाइपलाइन या कमांड के अन्य यौगिक अनुक्रम हो सकते हैं। अन्य समाधान पाइपलाइन के साथ काम नहीं करते हैं
एचवीएनएस ट्विटिंग

3
बैश वातावरण चर को !ऑपरेटर का पालन करना चाहिए । इसने मेरे लिए काम किया:! MY_ENV=value my_command
डैनियल बॉमर ने

1
नकारात्मकता पूरे पाइप पर काम करती है, इसलिए आप ऐसा नहीं कर सकते हैं, ldd foo.exe | ! grep badlibलेकिन ! ldd foo.exe | grep badlibयदि आप चाहते हैं कि बाहर निकलने की स्थिति 0 हो तो आप कर सकते हैं यदि badlib foo.exe में नहीं पाया जाता है। शब्दशः आप grepस्थिति को उलटना चाहते हैं , लेकिन पूरे पाइप को निष्क्रिय करने से एक ही परिणाम मिलता है।
निशान लता

@MarkLakata: आप सही हैं: POSIX शेल सिंटैक्स और संबंधित अनुभाग देखें ( सबसे अच्छे लुक के लिए POSIX पर जाएं)। हालांकि, यह उपयोग करने के लिए संभव है ldd foo.exe | { ! grep badlib; }(हालांकि यह एक उपद्रव, विशेष रूप से अर्धविराम है, आप के साथ एक पूर्ण उप खोल इस्तेमाल कर सकते हैं (और )और अर्धविराम के बिना)। OTOH, वस्तु पाइपलाइन के निकास की स्थिति को उल्टा करने के लिए है, और यह अंतिम कमांड की निकास स्थिति है (बाश, कभी-कभी छोड़कर)।
जोनाथन लेफ्लर

3
इस उत्तर के अनुसार !कमांड के साथ एक अवांछनीय बातचीत होती है set -e, अर्थात यह केवल 0-शून्य-शून्य निकास कोड के साथ आदेश को सफल करने का कारण बनता है, बजाय केवल गैर-शून्य निकास कोड के साथ सफल होने के। वहाँ यह सफल बनाने के लिए एक संक्षिप्त रास्ता नहीं है केवल एक गैर शून्य से बाहर निकलें कोड के साथ?
इलियट स्लॉटर

40

बैश में, का उपयोग करें! कमांड से पहले ऑपरेटर। उदाहरण के लिए:

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

17

तुम कोशिश कर सकते हो:

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

या केवल:

! ls nonexistingpath

5
दुर्भाग्य से !इस्तेमाल नहीं किया जा सकता है git bisect run, या कम से कम मैं नहीं जानता कि कैसे।
अत्तिला ओ।

1
आप हमेशा शेल स्क्रिप्ट में सामान रख सकते हैं; git bisect रन !उस स्थिति में नहीं दिखता है ।
टूलफ़ोर

10

अगर किसी तरह ऐसा होता है कि आपके पास अपने शेल के रूप में बैश नहीं है (उदाहरण के लिए: गिट स्क्रिप्ट, या कठपुतली निष्पादन परीक्षण) तो आप चला सकते हैं:

echo '! ls notexisting' | bash

-> रेटकोड: 0

echo '! ls /' | bash

-> रेटकोड: 1


1
यहां एक और ब्रेडक्रंब लगाने के लिए, ट्रैविस-सी के स्क्रिप्ट अनुभाग में लाइनों के लिए यह आवश्यक है।
किग्राएन

बिटबकेट पाइपलाइनों के लिए भी यह आवश्यक था।
कुमज

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

या

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

मैंने कॉपी किया अगर मूल प्रश्न से, मुझे लगा कि यह एक पाइपलाइन का हिस्सा है, मैं कभी-कभी थोड़ा धीमा होता हूं;)
रॉबर्ट गैंबल

ये दोनों कथन लगभग बराबर हैं, लेकिन इसमें छोड़ा गया रिटर्न मान $?अलग होगा। $?=1यदि nonexistingpathवास्तविक मौजूद है तो पहले वाला छोड़ सकता है। दूसरा हमेशा छोड़ता है $?=0। अगर आप मेकफाइल में इसका इस्तेमाल कर रहे हैं और रिटर्न वैल्यू पर भरोसा करने की जरूरत है, तो आपको सावधान रहना होगा।
मार्क लता

1

नोट: कभी-कभी आप देखेंगे !(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) से जोहानिस Schindelin ( dscho)
(द्वारा विलय Junio सी 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' नामक एकल निर्देशिका शामिल है ), जो निश्चित रूप से परीक्षण में विफल रहता है।

हम इसे केवल ' !' और ' (' के बीच एक स्थान जोड़कर ठीक कर सकते हैं , लेकिन इसके बजाय अनावश्यक उपधारा को हटाकर इसे ठीक कर सकते हैं। विशेष रूप से, 74ec8cf प्रतिबद्ध करें


1

बिना समाधान, बिना सबस्क्रिप्शन के, बिना, और कम से कम बाश में काम करना चाहिए:

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."
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.