Les blocs de code, comme les boucles while, until et for, voire même les blocs de test if/then peuvent aussi incorporer une redirection de stdin. Même une fonction peut utiliser cette forme de redirection (voir l'Exemple 24.11, « Vrai nom pour un utilisateur »). L'opérateur < à la fin du bloc de code accomplit ceci.
Exemple 20.5. Boucle while redirigée
#!/bin/bash # redir2.sh if [ -z "$1" ] then Fichier=noms.donnees # par défaut, si aucun fichier n'est spécifié. else Fichier=$1 fi #+ Fichier=${1:-noms.donnees} # peut remplacer le test ci-dessus (substitution de paramètres). compteur=0 echo while [ "$nom" != Smith ] # Pourquoi la variable $nom est-elle entre guillemets? do read nom # Lit à partir de $Fichier, plutôt que de stdin. echo $nom let "compteur += 1" done <"$Fichier" # Redirige stdin vers le fichier $Fichier. # ^^^^^^^^^^^^ echo; echo "$compteur noms lus"; echo exit 0 # Notez que dans certains vieux langages de scripts shells, #+ la boucle redirigée pourrait tourner dans un sous-shell. # Du coup, $compteur renverrait 0, la valeur initialisée en dehors de la boucle. # Bash et ksh évitent de lancer un sous-shell *autant que possible*, #+ de façon à ce que ce script, par exemple, tourne correctement. # Merci à Heiner Steven pour nous l'avoir indiqué. # Néanmoins... # Bash *peut* quelque fois commencer un sous-shell dans une boucle "while" #+ alimentée par un *tube*, #+ à distinguer d'une boucle "while" *redirigée*. abc=hi echo -e "1\n2\n3" | while read l do abc="$l" echo $abc done echo $abc # Merci à Bruno de Oliveira Schneider pour avoir démontré ceci #+ avec l'astuce de code ci-dessus. # Et merci à Brian Onn pour avoir corriger une erreur dans un commentaire.
Exemple 20.6. Autre forme de boucle while redirigée
#!/bin/bash # Ceci est une forme alternative au script précédent. # Suggéré par Heiner Steven #+ comme astuce dans ces situations où une boucle de redirection est lancée #+ comme un sous-shell, et donc que les variables à l'intérieur de la boucle #+ ne conservent pas leurs valeurs une fois la boucle terminée. if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun fichier spécifié. else Fichier=$1 fi exec 3<&0 # Sauve stdin sur le descripteur de fichier 3. exec 0<"$Fichier" # Redirige l'entrée standard. compteur=0 echo while [ "$nom" != Smith ] do read nom # Lit à partir du stdin redirigé ($Fichier). echo $nom let "compteur += 1" done # La boucle lit à partir du fichier $Fichier #+ à cause de la ligne 20. # La version originale de ce script terminait la boucle "while" avec #+ done <"$Filename" # Exercice : # Pourquoi cela n'est-il pas nécessaire ? exec 0<&3 # Restaure l'ancien stdin. exec 3<&- # Ferme le temporaire fd 3. echo; echo "$compteur noms lus"; echo exit 0
Exemple 20.7. Boucle until redirigée
#!/bin/bash # Identique à l'exemple précédent, mais avec une boucle "until". if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun nom de fichier n'est spécifié. else Fichier=$1 fi # while [ "$nom" != Smith ] until [ "$nom" = Smith ] # Modification de != en =. do read nom # Lit à partir de $Fichier, plutôt que de stdin. echo $nom done <"$Fichier" # Redirige stdin vers le fichier $Fichier. # ^^^^^^^^^^^^ # Même résultats qu'avec la boucle "while" du précédent exemple. exit 0
Exemple 20.8. Boucle for redirigée
#!/bin/bash if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun fichier n'est spécifié. else Fichier=$1 fi compteur_lignes=`wc $Fichier | awk '{ print $1 }'` # Nombre de lignes du fichier cible. # # Très peu naturel, néanmoins cela montre qu'il est possible de rediriger #+ stdin à l'intérieur d'une boucle "for"... #+ si vous êtes assez intelligent. # # Une autre façon plus concise est compteur_lignes=$(wc -l < "$Fichier") for nom in `seq $compteur_lignes` # Rappelez-vous que "seq" affiche une séquence de nombres. # while [ "$nom" != Smith ] -- plus compliqué qu'une boucle "while" -- do read nom # Lit à partir de $Fichier, plutôt que de stdin. echo $nom if [ "$nom" = Smith ] # A besoin de tout ce bagage supplémentaire ici. then break fi done <"$Fichier" # Redirige stdin vers le fichier $Fichier. # ^^^^^^^^^^^^ exit 0
Nous pouvons modifier le précédent exemple pour rediriger aussi la sortie de la boucle.
Exemple 20.9. Rediriger la boucle for (à la fois stdin et stdout)
#!/bin/bash if [ -z "$1" ] then Fichier=noms.donnees # Par défaut, si aucun fichier n'est spécifié. else Fichier=$1 fi FichierSauvegarde=$Fichier.nouveau # Fichier où sauvegarder les résultats. NomFinal=Jonah # Nom par lequel terminer la lecture. nb_lignes=`wc $Fichier | awk '{ print $1 }'` # Nombre de lignes du fichier cible. for nom in `seq $nb_lignes` do read nom echo "$nom" if [ "$nom" = "$NomFinal" ] then break fi done < "$Fichier" > "$FichierSauvegarde" # Redirige stdin dans $Fichier, # ^^^^^^^^^^^^^^^^^^^^^^^^^^^ et sauvegarde dans le fichier. exit 0
Exemple 20.10. Rediriger un test if/then
#!/bin/bash if [ -z "$1" ] then Fichier=noms.donnees # Valeur par défaut, si aucun nom de fichier n'est #+ spécifié. else Fichier=$1 fi VRAI=1 if [ "$VRAI" ] # if true et if : fonctionnent aussi. then read nom echo $nom fi <"$Fichier" # ^^^^^^^^^^^^ # Lit seulement la première ligne du fichier. # Un test "if/then" n'a aucun moyen de faire une itération sauf si il est #+ intégré dans une boucle. exit 0
Exemple 20.11. Fichier de données nom.données pour les exemples ci-dessus
Aristotle Belisarius Capablanca Euler Goethe Hamurabi Jonah Laplace Maroczy Purcell Schmidt Semmelweiss Smith Turing Venn Wilson Znosko-Borowski # This is a data file for #+ "redir2.sh", "redir3.sh", "redir4.sh", "redir4a.sh", "redir5.sh".
Rediriger stdout d'un bloc de code a le même effet que d'en sauver la sortie dans un fichier. Voir l'Exemple 3.2, « Enregistrer la sortie d'un bloc de code dans un fichier ».
Les documents intégrés sont un cas spécial pour la redirection de blocs de code. Dans ce cas, il devrait être possible d'alimenter le stdin d'une boucle while avec la sortie d'un document en ligne.
# Exemple de Albert Siersema # Utilisé avec sa permission (merci !). function sortie() # Pourrait aussi être une commande externe, bien sûr. # Ici, nous démontrons que vous pouvez utiliser aussi une fonction. { ls -al *.jpg | awk '{print $5,$9}' } nr=0 # Nous voulons que la boucle while puisse les manipuler et totalSize=0 #+ nous voulons être capable de voir les changements #+ après la fin de 'while'. while read tailleFichier nomFichier ; do echo "$nomFichier fait $tailleFichier octets" let nr++ tailleTotale=$((tailleTotale+$tailleFichier)) # Or: "let tailleTotale+=tailleFichier" done<<EOF $(sortie) EOF echo "$nr fichiers totalisant $tailleTotale octets"