Comment obtenir à la fois PIPESTATUS et la sortie dans le script bash

9

J'essaie d'obtenir la dernière date de modification d'un fichier avec cette commande

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

TM_LOCAL a une valeur comme "2012-05-16 23:18" après l'exécution de cette ligne

Je voudrais également vérifier PIPESTATUS pour voir s'il y a une erreur. Par exemple, si le fichier n'existe pas, lsrenvoie 2. Mais $?a la valeur 0 car il a la valeur de retour de awk.

Si j'exécute cette commande seule, je peux vérifier la valeur de retour de ls en regardant ${PIPESTATUS[0]}

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

Mais $PIPESTATUSne fonctionne pas comme je m'y attendais si j'affecte la sortie à une variable comme dans le premier exemple. Dans ce cas, le $PIPESTATUStableau n'a qu'un seul élément qui est identique à$?

Donc, la question est, comment puis-je obtenir les deux $PIPESTATUSet affecter la sortie à une variable en même temps?

Mustafa Serdar Şanlı
la source

Réponses:

8

Vous pouvez faire ceci:

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

Ensuite, $?sera le code retour de ls. Cela ne fonctionne pas si vous avez besoin du code retour de plusieurs parties de tuyau (mais vous pouvez diviser le pipeline si la sortie n'est pas trop grande, car elle est ici).

Voici un moyen assez coûteux d'obtenir le PIPESTATUStableau complet et la sortie. Pas très élégant, mais je n'ai rien trouvé d'autre:

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

Qui donne:

Output:
a
b
c
Results:
0 1 42
Tapis
la source
Cela fonctionne dans mon cas, mais je suis toujours curieux de savoir s'il existe un moyen d'obtenir un tableau pipestatus complet et la sortie.
Mustafa Serdar Şanlı
3

Utilisez set -o pipefailin bashpour obtenir le code de sortie non nul le plus à droite dans une séquence de commandes canalisée comme $?. De man bash:

S'il est défini, la valeur de retour d'un pipeline est la valeur de la dernière commande (la plus à droite) pour quitter avec un état différent de zéro, ou zéro si toutes les commandes du pipeline se terminent avec succès. Cette option est désactivée par défaut.

Ensuite, vous pouvez simplement y accéder $?. Utilisez set +o pipefailpour désactiver à nouveau.

Daniel Beck
la source
2

Je suppose que le problème ici est que PIPESTATUS disparaît dans son intégralité dès que vous exécutez une commande. Vous pouvez obtenir le tableau PIPESTATUS complet dans la version bash 2 ou supérieure de cette façon:

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

Ensuite , l' accès ${status[0]}, ${status[1]}etc.

eewanco
la source
2

Le principal problème avec "ce que vous attendez" est qu'une commande dans les guillemets est exécutée dans un sous-shell; $PIPESTATUSexiste là et le statut renvoyé par m suit les mêmes règles que si vous exécutiez un seul exécutable (ou script shell). Le statut de la commande backquote est le awkstatut le plus à droite ( ).

Pour implémenter ce qu'a dit @ Daniel Beck , définissez ainsi l' pipefailoption dans le sous-shell:

TM_LOCAL=`set -o pipefail; ls -l --time-style=long-iso ~/.vimrc | awk '{ print $6" "$7 }'` maintenant le statut stocké par la $?suite sera le statut de ls(si différent de zéro).

Cependant, je pense qu'un if [ -f ~/.vimrc ];test explicite ... serait plus lisible.

Vous ne pouvez pas obtenir la sortie dans une variable et la PIPESTATUSrenvoyer sans fichier temporaire pour la première, ni regroupement de la seconde dans une chaîne.

toddkaufmann
la source
0

Je voulais envoyer un e-mail à fron cron uniquement si le statut de sortie n'était pas nul

L'astuce est que pour obtenir le stdin pour la fin du pipeline, vous devez le mettre dans un sous-shell - mais cela semble cacher la valeur PIPESTATUS ...

cron de test crache une sortie et sort avec 1 ou 0 ..

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

MISE À JOUR: le PIPESTATUS n'est pas visible tant que la commande de pipeline n'est pas en cours

Paul Davey
la source
0

Une option consiste à vérifier l'existence de votre fichier avant d'obtenir son heure de modification avec un appel à stat. Étant donné que statrenvoie un peu plus que vous le souhaitez dans l'horodatage, vous pouvez le découper en utilisant l'expansion des paramètres.

Avec GNU stat(par exemple sous Linux), vous pouvez exécuter:

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

Sur Mac OS X et d'autres systèmes BSD, la statsyntaxe diffère et peut spécifier un format d'heure:

[[ -f ~/.vimrc ]] && TM_LOCAL=$(stat -f '%Sm' -t '%Y-%m-%d %H:%M' ~/.vimrc 2>/dev/null)
chepner
la source
Dans ce qui est maintenant la réponse GNU, vous dites que le changement $TM_LOCALest sûr. Il n'est sûr que si vous vous attendez à ce qu'il n'ait pas de valeur antérieure. Supposons que la valeur était antérieure 2020-02-27 17:14et qu'il n'y a pas de ~/.vimrcfichier. Vous auriez alors 2020-02-27 17. Je voudrais donc enchaîner ces deux lignes avec un supplément&& ou (de préférence car ce n'est pas si lisible) utiliser une ifstrophe.
Adam Katz