Configurar pipefail para un solo comando canalizado

18

Necesito ejecutar varios comandos de shell canalizados desde un script que no sea BASH (es decir, un script PHP) como estos:

command1 | command2 | command3

de modo que, si command1falla con un código de salida distinto de cero, cada otro comando también falla. Hasta ahora, lo que se me ocurrió es:

set -o pipefail && command1 | command2 | command3

Pero a pesar de que se ejecuta bien desde la terminal, produce esto si se ejecuta desde el script:

sh: 1: conjunto: opción ilegal -o pipefail

Desmond Hume
fuente
Parece que /bin/shno le gusta set -o pipefail. ¿Está realmente bashdisfrazado o es un caparazón diferente? Cuando bashse ejecuta como /bin/sh, ¿acepta set -o pipefail?
Jonathan Leffler
@JonathanLeffler Cuando lo ejecuto /bin/sh set -o pipefaildice /bin/sh: 0: Can't open set(lo mismo con sudo). Espero haberlo probado bien. El sistema es Ubuntu 12.
Desmond Hume
Tendrías que intentarlo /bin/sh -c "set -o pipefail"; tal como estaba, el shell intentaba ejecutar un script en el directorio actual llamado sety no lo encontró.
Jonathan Leffler
@JonathanLeffler /bin/sh -c "set -o pipefail"no funciona, sin embargo, bash -c "set -o pipefail"sí.
Desmond Hume
Entonces, su problema es que el script es ejecutado por /bin/shlo que no reconoce set -o pipefail. En consecuencia, deberá asegurarse de que el script se ejecute en /bin/bashlugar de hacerlo /bin/sh. O, si tiene confianza, valiente, y probablemente insensato, cambie /bin/shpara ser un enlace o copia de, en /bin/bashlugar de cualquier shell al que esté vinculado actualmente o una copia. Si está seguro de que /bin/shes así bash, está utilizando un comportamiento que bashno se expone cuando se ejecuta como /bin/sh; usar bashcomo bash.
Jonathan Leffler

Respuestas:

18

Desde la línea de comando Bash, debería invocar una subshell para evitar que pipefailse establezca después:

$ (set -o pipefail && command1 | command2 | command3)

Esto limitaría el efecto de la pipefailopción al subshell creado por los paréntesis (...).

Un verdadero ejemplo:

$ (set -o pipefail && false | true) && echo pipefail inactive || echo pipefail active
pipefail active

Si usa una llamada de shell explícita con la -copción, no necesita una subshell, ya sea con basho con un shalias para bash:

$ bash -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active
$ sh -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active

Como su shno acepta la pipefailopción, tendría que suponer que se trata de una versión anterior o modificada de bash, o que en realidad es algún otro shell por completo.

thkala
fuente
1
¿Es $en la primera pieza una parte del comando, es solo un indicador de shell? Intenté en ambos sentidos, realmente no funcionó. La bash -cforma siempre funciona, aunque no demasiado elegante ...
Desmond Hume
5

No estoy seguro de por qué no se mencionó anteriormente, pero es posible desarmar explícitamente pipefail, usando set +o pipefail.

set -o pipefail
command1 | command2 | command3
set +o pipefail

Si está ejecutando un fragmento y no está seguro acerca del pipefailconjunto ya configurado, puede usar esto con la subshell como se sugirió anteriormente:

# explicitly execute with pipefail set
(set -o pipefail ; command1 | command2 | command3 )
# explicitly execute with pipefail unset
(set +o pipefail ; command1 | command2 | command3 )
robbat2
fuente
0

Puede usar $ (comando1) combinado con $? :

a=$(echo "a")
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Imprime "abc"

a=$(ls unexitingDir)
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

Imprime "".

"[$? -eq 0] &&" significa que el siguiente comando se ejecuta solo si el anterior tuvo éxito.

Eso no responde a su pregunta, pero es una solución a su problema.

cglacet
fuente
¿Significa que siempre necesitaría almacenar la salida de cada uno de los comandos en una variable?
Desmond Hume
Siempre no lo sé, pero con mi solución sí lo hará (si lo necesita para su próximo comando). A menos que pueda escribircommand1 && command2 && command3
cglacet