Cómo obtener PIPESTATUS y salida en script bash

9

Estoy tratando de obtener la última fecha de modificación de un archivo con este comando

TM_LOCAL=`ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'`

TM_LOCAL tiene un valor como "2012-05-16 23:18" después de la ejecución de esta línea

También me gustaría verificar PIPESTATUS para ver si hubo un error. Por ejemplo, si el archivo no existe, lsdevuelve 2. Pero $?tiene el valor 0 ya que tiene el valor de retorno de awk.

Si ejecuto este comando solo, puedo verificar el valor de retorno de ls mirando ${PIPESTATUS[0]}

ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'

Pero $PIPESTATUSno funciona como esperaba si asigno la salida a una variable como en el primer ejemplo. En este caso, la $PIPESTATUSmatriz tiene solo 1 elemento que es igual a$?

Entonces, la pregunta es, ¿cómo puedo obtener ambos $PIPESTATUSy asignar la salida a una variable al mismo tiempo?

Mustafa Serdar Şanlı
fuente

Respuestas:

8

Podrías hacer esto:

TM_LOCAL=$(ls -l --time-style=long-iso ~/.vimrc | \
             awk '{ print $6" "$7 }' ; exit ${PIPESTATUS[0]} )

Entonces $?será el código de retorno de ls. Esto no funciona si necesita el código de retorno de más de una de las partes de la tubería (pero podría dividir la tubería si la salida no es demasiado grande, como está aquí).

Aquí hay una forma bastante costosa de obtener la PIPESTATUSmatriz completa y la salida. No muy elegante, pero no he encontrado nada más:

result=$(echo -e "a\nb\nc" | \
          ( cat ; exit 1 ) | \
          ( cat ; exit 42 ) ; echo ${PIPESTATUS[@]})
output=$(head -n -1 <<< "$result")
status=($(tail -n 1 <<< "$result"))
echo "Output:"
echo "$output"
echo "Results:"
echo "${status[@]}"

Lo que da:

Output:
a
b
c
Results:
0 1 42
Estera
fuente
Esto funciona en mi caso, pero todavía tengo curiosidad acerca de si hay una manera de obtener una matriz completa de pipestatus y la salida.
Mustafa Serdar Şanlı
3

Uso set -o pipefailde bashconseguir el código de salida más a la derecha que no sea cero en una secuencia de comandos como por tubería $?. De man bash:

Si se establece, el valor de retorno de una tubería es el valor del último comando (más a la derecha) para salir con un estado distinto de cero, o cero si todos los comandos en la tubería salen con éxito. Esta opción está deshabilitada por defecto.

Entonces puedes simplemente acceder $?. Use set +o pipefailpara deshabilitar nuevamente.

Daniel Beck
fuente
2

Supongo que el problema aquí es que PIPESTATUS desaparece por completo tan pronto como ejecutas un comando. Puede obtener la matriz PIPESTATUS completa en la versión bash 2 o superior de esta manera:

declare -a status
status=(${PIPESTATUS[@]})

A continuación, el acceso ${status[0]}, ${status[1]}etc.

eewanco
fuente
2

El principal problema con "lo que espera" es que un comando en comillas inversas se ejecuta en una subshell; $PIPESTATUSexiste allí y el estado devuelto por ellos sigue las mismas reglas que si ejecutara un solo ejecutable (o script de shell). El estado del comando backquote es el estado más a la derecha ( awk).

Para implementar lo que dijo @ Daniel Beck , configure la pipefailopción en el subshell de esta manera:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` ahora el estado almacenado $?posteriormente será el estado de ls(si no es cero).

Sin embargo, creo que una if [ -f ~/.vimrc ];prueba explícita ... sería más legible.

No puede obtener el resultado en una variable y PIPESTATUSdevolverlo sin un archivo temporal para el primero o sin ordenar el último en una cadena.

toddkaufmann
fuente
0

Quería enviar correos electrónicos fron cron solo si el estado de salida no era cero

El truco es que para obtener el stdin para el final de la tubería, debe colocarlo en una subcapa, pero eso parece ocultar el valor de PIPESTATUS ...

prueba cron escupe alguna salida y sale con 1 o 0 ..

./testcron | (test ${PIPESTATUS[0]} -ne 0 || mail -s "testcron output" paul)

ACTUALIZACIÓN: el PIPESTATUS no es visible hasta que se procese el comando de canalización

Paul Davey
fuente
0

Una opción es verificar la existencia de su archivo antes de obtener su hora de modificación con una llamada a stat. Como statdevuelve un poco más de lo que desea en la marca de tiempo, puede recortarlo mediante la expansión de parámetros.

Con GNU stat(por ejemplo, en Linux), puede ejecutar:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -c '%y' ~/.vimrc 2>/dev/null)
TM_LOCAL=${TM_LOCAL%:*}  # Safe to do, even if stat fails

En Mac OS X y otros sistemas BSD, la statsintaxis difiere y puede especificar un formato de hora:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)
chepner
fuente
En lo que ahora es la respuesta GNU, usted dice que el cambio $TM_LOCALes seguro. Solo es seguro si esperaba que no tuviera un valor anterior. Digamos que el valor era anteriormente 2020-02-27 17:14y no hay ~/.vimrcarchivo. Entonces lo habrías hecho 2020-02-27 17. Por lo tanto, encadenaría esas dos líneas con &&una ifestrofa adicional o (preferiblemente ya que no es tan legible) .
Adam Katz