web-dev-qa-db-fra.com

Comment puis-je annuler la valeur de retour d'un processus?

Je recherche un processus simple mais multiplateforme négation - qui annule la valeur qu'un processus retourne. Il doit mapper 0 à une valeur! = 0 et n'importe quelle valeur! = 0 à 0, c'est-à-dire que la commande suivante doit retourner "oui, le chemin inexistant n'existe pas":

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

Le ! - l'opérateur est génial mais malheureusement pas indépendant de Shell.

79
secr

Auparavant, la réponse était présentée avec ce qui est maintenant la première section comme dernière section.

POSIX Shell comprend un ! opérateur

En fouillant la spécification Shell pour d'autres problèmes, j'ai récemment (septembre 2015) remarqué que le shell POSIX prend en charge un ! opérateur. Par exemple, il est répertorié comme mot réservé et peut apparaître au début d'un pipeline - où une simple commande est un cas particulier de 'pipeline'. Il peut donc être utilisé dans les instructions if et les boucles while ou until - dans les shells compatibles POSIX. Par conséquent, malgré mes réserves, il est probablement plus largement disponible que je ne l'avais imaginé en 2008. Un rapide contrôle de POSIX 2004 et SUS/POSIX 1997 montre que ! était présent dans ces deux versions.

Notez que le ! L'opérateur doit apparaître au début du pipeline et annule le code d'état de l'ensemble du pipeline (c'est-à-dire le dernière commande ). Voici quelques exemples.

# 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

Réponse portable - fonctionne avec des coquilles antiques

Dans un script Bourne (Korn, POSIX, Bash), j'utilise:

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

C'est aussi portable que possible. La "commande et les arguments" peuvent être un pipeline ou une autre séquence composée de commandes.

Une commande not

Le '!' , qu'il soit intégré à votre Shell ou fourni par les O/S, n'est pas universellement disponible. Ce n'est pas terriblement difficile à écrire, cependant - le code ci-dessous remonte à au moins 1991 (bien que je pense avoir écrit une version précédente il y a encore plus longtemps). Cependant, je n'ai pas tendance à l'utiliser dans mes scripts, car il n'est pas disponible de manière fiable.

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

Cela renvoie le "succès", l'opposé de l'échec, lorsqu'il échoue à exécuter la commande. Nous pouvons débattre si l'option "ne rien faire avec succès" était correcte; il devrait peut-être signaler une erreur lorsqu'il ne lui est pas demandé de faire quoi que ce soit. Le code dans '"stderr.h" 'fournit des fonctionnalités simples de rapport d'erreurs - je l'utilise partout. Code source sur demande - voir ma page de profil pour me contacter.

80
Jonathan Leffler

Dans Bash, utilisez le! avant la commande. Par exemple:

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

Tu pourrais essayer:

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

ou juste:

! ls nonexistingpath
17
Eduard Wirch

Si, d'une manière ou d'une autre, vous n'avez pas Bash comme shell (par exemple: scripts git ou tests d'exécution de marionnettes), vous pouvez exécuter:

echo '! ls notexisting' | bash

-> retcode: 0

echo '! ls /' | bash

-> retcode: 1

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

ou

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

Remarque: parfois, vous verrez !(command || other command).
Ici ! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist." est assez.
Pas besoin d'un sous-shell.

Git 2.22 (Q2 2019) illustre cette meilleure forme avec:

validation 74ec8cf , validation 3fae7ad , validation 0e67c32 , validation 07353d9 , validation 3bc2702 , commit 8c3b9f7 , commit 80a539a , commit c5c39f4 (13 mars 2019) par SZEDER Gábor (szeder) .
Voir commit 99e37c2 , commit 9f82b2a , commit 900721e (13 mars 2019) par Johannes Schindelin (dscho) .
(Fusionné par Junio ​​C Hamano - gitster - in commit 579b75a , 25 avr 2019)

t9811-git-p4-label-import: correction de la négation du pipeline

Dans 't9811-git-p4-label-import.sh', le test 'tag that cannot be exported 's'exécute:

!(p4 labels | grep GIT_TAG_ON_A_BRANCH)

pour vérifier que la chaîne donnée n'est pas imprimée par 'p4 labels '.
C'est problématique, car selon [~ # ~] posix [~ # ~] :

"Si le pipeline commence par le mot réservé ! et command1 est une commande de sous-shell, l'application doit s'assurer que ( opérateur au début de command1 est séparé du ! par un ou plusieurs <blank> personnages.
Le comportement du mot réservé ! immédiatement suivi du ( L'opérateur n'est pas spécifié. "

Bien que les obus les plus courants continuent d'interpréter cette '! 'as "annuler le code de sortie de la dernière commande du pipeline",' mksh/lksh 'pas et l'interpréter comme un modèle de nom de fichier négatif à la place.
En conséquence, ils tentent d'exécuter une commande composée des chemins d'accès dans le répertoire courant (il contient un seul répertoire appelé 'main'), ce qui, bien sûr, échoue au test.

Nous pourrions le corriger simplement en ajoutant un espace entre les '!' et '( ', mais corrigeons-le à la place en supprimant le sous-shell inutile. En particulier, Commit 74ec8cf

0
VonC