it-swarm.dev

¿Cómo puedo negar el valor de retorno de un proceso?

Estoy buscando un proceso simple, pero multiplataforma negate - que niega el valor que devuelve un proceso. Debe asignar 0 a algún valor! = 0 y cualquier valor! = 0 a 0, es decir, el siguiente comando debe devolver "sí, no existe ninguna ruta":

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

Los ! - El operador es genial, pero desafortunadamente no es independiente de Shell.

77
secr

Anteriormente, la respuesta se presentaba con lo que ahora es la primera sección como la última sección.

POSIX Shell incluye un operador !

Revisando la especificación de Shell por otros problemas, recientemente (septiembre de 2015) noté que POSIX Shell es compatible con un operador !. Por ejemplo, aparece como palabra reservada y puede aparecer al inicio de --- tubería - donde un comando simple es un caso especial de "tubería". Por lo tanto, se puede usar en if sentencias y while o until bucles también - en shells compatibles con POSIX. En consecuencia, a pesar de mis reservas, es probable que esté más disponible de lo que pensé en 2008. Una revisión rápida de POSIX 2004 y SUS/POSIX 1997 muestra que ! estaba presente en ambas versiones.

Tenga en cuenta que el operador ! debe aparecer al principio de la tubería y niega el código de estado de toda la tubería (es decir, la último comando ). Aquí hay unos ejemplos.

# 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

Respuesta portátil - trabaja con conchas antiguas.

En un script Bourne (Korn, POSIX, Bash), uso:

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

Esto es tan portátil como puede ser. El 'comando y argumentos' puede ser una tubería u otra secuencia compuesta de comandos.

Un comando not

Los '!' El operador, ya sea integrado en su Shell o proporcionado por el o/s, no está disponible universalmente. Sin embargo, no es terriblemente difícil escribir, ya que el código a continuación se remonta al menos a 1991 (aunque creo que escribí una versión anterior hace más tiempo). Sin embargo, no tiendo a usar esto en mis scripts, porque no está disponible de manera confiable.

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

Esto devuelve 'éxito', lo opuesto a la falla, cuando falla al ejecutar el comando. Podemos debatir si la opción 'no hacer nada con éxito' fue correcta; tal vez debería informar un error cuando no se le pide que haga nada. El código en '"stderr.h"' proporciona facilidades de reporte de errores simples, lo uso en todas partes. Código fuente a petición - vea mi página de perfil para contactarme.

74
Jonathan Leffler

En Bash, usa el! Operador antes del comando. Por ejemplo:

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

Tu podrías intentar:

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

o solo:

! ls nonexistingpath
17
Eduard Wirch

Si de alguna manera sucede que no tienes Bash como tu Shell (por ejemplo: git scripts, o puppet exec tests) puedes ejecutar:

echo '! ls notexisting' | bash

-> código de retorno: 0

echo '! ls /' | bash

-> código de retorno: 1

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

o

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

Nota: a veces verá !(command || other command).
Aquí ! ls nonexistingpath && echo "yes, nonexistingpath doesn't exist." es suficiente.
No hay necesidad de un sub-shell.

Git 2.22 (Q2 2019) ilustra esa mejor forma con:

Commit 74ec8cf , commit 3fae7ad , commit 0e67c32 , commit 07353d9 , commit 3bc2702 , commit 8c3b9f7 , commit 80a539a , commit c5c39f4 (13 de marzo de 2019) por SZEDER Gábor (szeder) .
Ver cometer 99e37c2 , cometer 9f82b2a , cometer 900721e (13 de marzo de 2019) por Johannes Schindelin (dscho) .
(Fusionado por Junio ​​C Hamano - gitster - in commit 579b75a , 25 de abril de 2019)

t9811-git-p4-label-import: arregla la negación de la tubería

En 't9811-git-p4-label-import.sh', la prueba 'tag that cannot be exported' se ejecuta:

!(p4 labels | grep GIT_TAG_ON_A_BRANCH)

para comprobar que la cadena dada no se imprime con 'p4 labels'.
Esto es problemático, porque de acuerdo a POSIX :

"Si la tubería comienza con la palabra reservada ! y command1 es un comando de subshell, la aplicación garantizará que el operador ( al comienzo de command1 esté separado de ! por uno o más <blank> caracteres.
El comportamiento de la Palabra reservada ! inmediatamente seguido por el operador ( no está especificado. "

Mientras que los shells más comunes aún interpretan este '!' como "negar el código de salida del último comando en la tubería", 'mksh/lksh' no lo hacen e interpretarlo como un patrón de nombre de archivo negativo.
Como resultado, intentan ejecutar un comando compuesto de las rutas de acceso en el directorio actual (contiene un directorio único llamado 'main'), que, por supuesto, no pasa la prueba.

Podríamos arreglarlo simplemente agregando un espacio entre '!' y '(', pero en lugar de eso, reparémoslo eliminando la subshell innecesaria. En particular, Commit 74ec8cf

0
VonC