2012-02-16 4 views
2

Existe-t-il un moyen simple de "rembobiner" /dev/stdin dans mon script bash qui a déjà lu tout ou partie du tube d'entrée?rembobiner stdin dans un script bash

Application: J'ai écrit simple MDA dans la partie 1, lit un e-mail de la ligne fetchmail par ligne, comme ceci:

while read -a linA; do 
    echo -e "$[++linenum]:\t${#linA[@]},${linA[*]}" > /dev/null # verbose 
    [ "${linA[0]}" = "Date:" ] && unset linA[0] && mailDate="${linA[*]}" 
    [ "${linA[0]}" = "Subject:" ] && unset linA[0] && mailSubject="${linA[*]}" 
    [ "$mailSubject" = "Courtesy Fill Notification" ] || break # if wrong subject then thank you, we're done with this mail 
done 

et à la fin du traitement, je souhaite enregistrer le message entier dans un fichier, à la fois pour le débogage, et de sorte que le côté écrivain du tube voit que toute sa sortie a été lue, et ne retourne pas l'échec (donc le message n'est pas lu dans la boîte aux lettres).

Répondre

5

La lecture d'un tuyau est destructive; il n'y a aucun moyen de rechercher sur un tuyau:

ESPIPE (29): Illegal seek 

Le numéro d'erreur est de MacOS X, mais le nom est traditionnel (et mandatée par POSIX) et donne une indication de l'endroit où la restriction vient.

Donc, si vous voulez retraiter l'entrée dans un script shell, vous devez cacher l'entrée standard loin dans un fichier afin de pouvoir retraiter:

tmp=${TMPDIR:-/tmp}/xx.$$ # Consider mktemp or something 
trap "rm -f $tmp.1; exit 1" 0 1 2 3 13 15 # Exit, HUP, INT, QUIT, PIPE, TERM 

tee $tmp.1 | 
while read -a linA 
do 
    ... 
done 

...reprocess $tmp.1 here... 

rm -f $tmp.1 
trap 0 
exit $exit_status 

Le seul piège à regarder est que parce que du pipeline, les variables définies dans la boucle while ne sont pas accessibles dans le shell parent. Si c'est un problème, vous pouvez utiliser:

tee $tmp.1 | 
{ 
while read -a linA 
do 
    ... 
done 

...reprocess $tmp.1 here... 
} 

Les accolades regroupent les instructions pour la redirection d'E/S. Le processus tee sera terminé en raison de EOF, de sorte que le fichier sera terminé lorsque le while read détecte EOF, donc il est sûr d'accéder à $tmp.1 après la boucle.

La seule chose à surveiller est que tee ne répondra pas à l'entrée du terminal. Les fichiers ne seront pas un problème; l'entrée par canalisation est peu susceptible d'être un problème. Cependant, tee tamponnera probablement complètement sa sortie plutôt que de mettre en mémoire tampon sa sortie, de sorte que la boucle de lecture ne verra peut-être rien tant que vous n'aurez pas tapé beaucoup de lignes d'entrée.

+0

++ parce que j'aime les astuces de bash dans les trois premières lignes de code. Mon script mda.sh est toujours utilisé de manière non interactive de toute façon ... même pour les tests je redirige/cat un fichier de test. – Marcos

+0

Par curiosité, avec quel code avez-vous cherché à obtenir cette erreur 'ESPIPE'? Je peux utiliser pour les cas non-pipe. – Marcos

+1

J'ai en fait exécuté un programme que j'ai écrit/généré appelé 'errno' qui imprime les informations correspondant à un numéro d'erreur spécifié numériquement ou sous forme de chaîne (j'ai spécifié ESPIPE).Vous l'obtiendriez si vous faites: 'int p [2], e; tuyau (p); lseek (p [0], -1L, 2); e = errno; printf ("% d (% s) \ n", e, strerror (e)); ', sauf que le nom symbolique est plus difficile à obtenir que cela. (J'ai un script Perl pour générer le code réel de 'errno'.) –

0

Pas vraiment, non.

Il vous suffira d'ajouter chaque ligne dans une variable au fur et à mesure de la lecture et d'effacer la variable si nécessaire.

+0

sauf souvent la boucle sort tôt d'un 'break' – Marcos

0

que diriez-vous:

tmpfile=$(mktemp) 
while read -a linA; do 
    echo -e "$[++linenum]:\t${#linA[@]},${linA[*]}" > /dev/null # verbose 
    [ "${linA[0]}" = "Date:" ] && unset linA[0] && mailDate="${linA[*]}" 
    echo "${linA}">>$tmpfile 
done 
mv $tmpfile fulltext.txt 

je pense qu'il est mieux parce que vous lisez un message juste une fois

+0

Merci. Parce que j'ai l'intention de garder l'entrée dans un fichier de toute façon, je pense que je vais ignorer toute cette affaire _tmpfilename_ et (sur) écrire un nom de fichier fixe au début, plutôt que la fin, de mon script. Aurait besoin de changer légèrement ma boucle pour ça. Donc je pense que je pourrais aller avec quelque chose comme 'cat/dev/stdin> $ inputfile' suivi de' cat $ inputfile | tandis que lisez -a ... ' – Marcos

0

Essayez exec < /dev/stdin qui pourrait fonctionner sous Linux.