Come ottenere sia PIPESTATUS sia l'output nello script bash


9

Sto cercando di ottenere la data dell'ultima modifica di un file con questo comando

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

TM_LOCAL ha un valore come "2012-05-16 23:18" dopo l'esecuzione di questa riga

Vorrei anche controllare PIPESTATUS per vedere se si è verificato un errore. Ad esempio, se il file non esiste, lsrestituisce 2. Ma $?ha valore 0 in quanto ha il valore restituito di awk.

Se eseguo questo comando da solo, posso controllare il valore di ritorno di ls guardando ${PIPESTATUS[0]}

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

Ma $PIPESTATUSnon funziona come mi aspettavo se assegnassi l'output a una variabile come nel primo esempio. In questo caso, l' $PIPESTATUSarray ha solo 1 elemento uguale a$?

Quindi, la domanda è: come posso ottenere entrambi $PIPESTATUSe assegnare l'output a una variabile contemporaneamente?

Risposte:


8

Potresti farlo:

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

Quindi $?sarà il codice di ritorno da ls. Questo non funziona se è necessario il codice di ritorno da più di una delle parti del tubo (ma è possibile dividere la pipeline se l'output non è troppo grande, poiché è qui).

Ecco un modo piuttosto costoso per ottenere l'intero PIPESTATUSarray e l'output. Non molto elegante, ma non ho trovato nient'altro:

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[@]}"

Che dà:

Output:
a
b
c
Results:
0 1 42

Questo lavoro nel mio caso, ma sono ancora curioso di sapere se esiste un modo per ottenere l'array pipestatus completo e l'output.
Mustafa Serdar Şanlı,

3

Utilizzare set -o pipefailin bashper ottenere il codice di uscita diverso da zero più a destra in una sequenza di comandi convogliata come $?. Da man bash:

Se impostato, il valore restituito di una pipeline è il valore dell'ultimo comando (più a destra) per uscire con uno stato diverso da zero o zero se tutti i comandi nella pipeline vengono chiusi correttamente. Questa opzione è disabilitata di default.

Quindi puoi semplicemente accedere $?. Utilizzare set +o pipefailper disabilitare di nuovo.


2

Presumo che il problema qui sia che PIPESTATUS scompaia completamente non appena si esegue un comando. È possibile ottenere l'array PIPESTATUS completo in bash versione 2 o successiva in questo modo:

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

Poi accesso ${status[0]}, ${status[1]}ecc


2

Il problema principale con "quello che ti aspetti" è che un comando in backquotes sia eseguito in una subshell; $PIPESTATUSesiste lì e lo stato restituito da m segue le stesse regole come se si eseguisse un singolo eseguibile (o script di shell). Lo stato del comando backquote è lo stato più a destra ( awk).

Per implementare ciò che ha detto @ Daniel Beck , imposta l' pipefailopzione nella subshell in questo modo:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` ora lo stato memorizzato in $?seguito sarà lo stato di ls(se diverso da zero).

Tuttavia, penso che un if [ -f ~/.vimrc ];test esplicito ... sarebbe più leggibile.

Non è possibile ottenere l'output in una variabile e PIPESTATUSrestituito senza un file temporaneo per il primo o il marshalling di quest'ultimo in una stringa.


0

Volevo inviare e-mail da cron solo se lo stato di uscita non era zero

Il trucco è che per ottenere lo stdin per la fine della pipeline devi metterlo in una subshell - ma sembra nascondere il valore PIPESTATUS ...

test cron sputa un po 'di output ed esce con 1 o 0 ..

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

AGGIORNAMENTO: il PIPESTATUS non è visibile fino a quando il comando della pipeline non viene elaborato


0

Un'opzione è verificare l'esistenza del file prima di ottenere il tempo di modifica con una chiamata a stat. Poiché statrestituisce un valore leggermente superiore a quello desiderato nel timestamp, è possibile tagliarlo utilizzando l'espansione dei parametri.

Con GNU stat(ad es. Su Linux), puoi eseguire:

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

Su Mac OS X e altri sistemi BSD, la statsintassi differisce e può specificare un formato orario:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)

In quella che ora è la risposta GNU, dici che la modifica $TM_LOCALè sicura. È sicuro solo se ti aspettavi che non avesse un valore precedente. Supponiamo che il valore sia stato precedentemente 2020-02-27 17:14e che non ci siano ~/.vimrcfile. Avresti quindi 2020-02-27 17. Avrei quindi incatenato quelle due linee insieme a un'ulteriore &&o (preferibilmente poiché non è così leggibile) avrei usato una ifstrofa.
Adam Katz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.