La sortie de Bash version 4 a été annoncée le 20 février 2009, par Chet Ramey. Cette livraison comprenait un certain nombre de fonctionnalités notables ainsi que des corrections majeures.
Parmi les nouveautés :
Exemple 37.5. Petite base de données d'adresses
#!/bin/bash4 # fetch_address.sh declare -A address # -A option declares associative array. address[Charles]="414 W. 10th Ave., Baltimore, MD 21236" address[John]="202 E. 3rd St., New York, NY 10009" address[Wilma]="1854 Vermont Ave, Los Angeles, CA 90023" echo "Charles's address is ${address[Charles]}." # Charles's address is 414 W. 10th Ave., Baltimore, MD 21236. echo "Wilma's address is ${address[Wilma]}." # Wilma's address is 1854 Vermont Ave, Los Angeles, CA 90023. echo "John's address is ${address[John]}." # John's address is 202 E. 3rd St., New York, NY 10009.
Exemple 37.6. Base de données d'adresses un peu plus élaborée
#!/bin/bash4 # fetch_address-2.sh # A more elaborate version of fetch_address.sh. SUCCESS=0 E_DB=99 # Error code for missing entry. declare -A address # -A option declares associative array. store_address () { address[$1]="$2" return $? } fetch_address () { if [[ -z "${address[$1]}" ]] then echo "$1's address is not in database." return $E_DB fi echo "$1's address is ${address[$1]}." return $? } store_address "Charles Jones" "414 W. 10th Ave., Baltimore, MD 21236" store_address "John Smith" "202 E. 3rd St., New York, NY 10009" store_address "Wilma Wilson" "1854 Vermont Ave, Los Angeles, CA 90023" # Exercise: # Rewrite the above store_address calls to read data from a file, #+ then assign field 1 to name, field 2 to address in the array. # Each line in the file would have a format corresponding to the above. # Use a while-read loop to read from file, sed or awk to parse the fields. fetch_address "Charles Jones" # Charles Jones's address is 414 W. 10th Ave., Baltimore, MD 21236. fetch_address "Wilma Wilson" # Wilma Wilson's address is 1854 Vermont Ave, Los Angeles, CA 90023. fetch_address "John Smith" # John Smith's address is 202 E. 3rd St., New York, NY 10009. fetch_address "Bozo Bozeman" # Bozo Bozeman's address is not in database. exit $? # In this case, exit code = 99, since that is function return.
Cf Exemple A.53, « Pratiquer le code Morse » pour une utilisation intéressante de tableau associatif.
Les éléments du tableau des indexes peuvent intégrer des caractères d'espacement, ou même des espaces précédant ou suivant l'expression. Cependant, les éléments du tableau des indexes qui contiennent uniquement des espaces ne sont pas permis.
adresse[ ]="Rien" # Erreur !
Améliorations de la syntaxe de case : caractères de fin ;;& et ;&.
Exemple 37.7. Tests de caractères
#!/bin/bash4 test_char () { case "$1" in [[:print:]] ) echo "$1 is a printable character.";;& # | # The ;;& terminator continues to the next pattern test. | [[:alnum:]] ) echo "$1 is an alpha/numeric character.";;& # v [[:alpha:]] ) echo "$1 is an alphabetic character.";;& # v [[:lower:]] ) echo "$1 is a lowercase alphabetic character.";;& [[:digit:]] ) echo "$1 is an numeric character.";& # | # The ;& terminator executes the next statement ... # | %%%@@@@@ ) echo "********************************";; # v # ^^^^^^^^ ... even with a dummy pattern. esac } echo test_char 3 # 3 is a printable character. # 3 is an alpha/numeric character. # 3 is an numeric character. # ******************************** echo test_char m # m is a printable character. # m is an alpha/numeric character. # m is an alphabetic character. # m is a lowercase alphabetic character. echo test_char / # / is a printable character. echo # The ;;& terminator can save complex if/then conditions. # The ;& is somewhat less useful.
La nouvelle commande intégrée coproc permet à deux processus parallèles de communiquer et d'interagir. Voyez ce qu'écrit Chet Ramey dans la FAQ de Bash. [127] , ver. 4.01 :
Le nouveau mot réservé 'coproc' spécifie un
co-processus : une commande asynchrone tourne avec
deux tubes connectés au shell qui les a créés. Les
co-processus peuvent être nommés. Les descripteurs de
fichiers d'entrée et de sortie, les PID des co-processus
sont disponibles pour le shell appelant dans des
variables dont les noms renvoient au co-processus
concerné.
George Dimitriu explique : "... coproc ... est une
fonctionnalité utilisée par Bash dans la substitution de
processus, qui maintenant est devenue disponible pour
l'utilisateur.
Il en résulte qu'elle peut être explicitement invoquée
dans un script, au lieu d'être seulement un mécanisme de
Bash présent en coulisses.
Voir http://linux010.blogspot.com/2008/12/bash-process-substitution.html.
Les co-processus utilisent des descripteurs de fichiers. Les descripteurs de fichiers permettent aux processus et aux tubes de se parler.
#!/bin/bash4 # Un co-processus communique avec une boucle while-read coproc { cat mx_data.txt; sleep 2; } # ^^^^^^^ # Essayez sans "sleep 2" et voyez ce qui se passe. while read -u ${COPROC[0]} line # ${COPROC[0]} est le do #+ descripteur de fichier du co-processus. echo "$line" | sed -e 's/line/TEXTE-NON-ORIGINAL/' done kill $COPROC_PID # Plus besoin du co-processus #+ donc on tue son PID.
Mais attention !
#!/bin/bash4 echo; echo a=aaa b=bbb c=ccc coproc echo "un deux trois" while read -u ${COPROC[0]} a b c; # Remarquez que cette boucle do #+ tourne dans un sous-shell. echo "Dans le boucle while-read : "; echo "a = $a"; echo "b = $b"; echo "c = $c" echo "descripteur de fichier du co-processus : ${COPROC[0]}" done # a = un # b = deux # c = trois # Jusque là, pas de problème, mais... echo "-----------------" echo "Hors de la boucle while-read : echo "a = $a" # a = echo "b = $b" # b = echo "c = $c" # c = echo "descripteur de fichier du co-processus : ${COPROC[0]}" echo # Le co-processus tourne toujours, mais... #+ il ne permet toujours pas au processus parent #+ d'"hériter" les variables du processus enfant, la boucle while-read # Comparez avec le script "badread.sh".
Le co-processus est asynchrone, ce qui peut poser problème. Il peut en effet se terminer avant qu'un autre processus ait fini de communiquer avec lui.
#!/bin/bash4 A VERIF coproc nomcopr { for i in {0..10}; do echo "index = $i"; done; } # ^^^^^^^ Ce co-processus est *nommé*. read -u ${nomcopr[0]} echo $REPONSE # index = 0 echo ${COPROC[0]} #+ Aucun résultat... Le co-processus s'est terminé # après la première itération de la boucle. # Néanmoins, George Dimitriu a trouvé une correction partielle. coproc nomcp { for i in {0..10}; do echo "index = $i"; done; sleep 1; echo salut > myo; cat - >> myo; } # ^^^^^ Ceci est un coprocesseur *nommé*. echo "Je suis le principal"$'\04' >&${nomcp[1]} myfd=${nomcopr[0]} echo monfd=$monfd ### while read -u $monfd ### do ### echo $REPONSE; ### done echo $nomcopr_PID # Lancez ceci avec et sans la boucle while en commentaire, vous #+ verrez alors que chacun des processus, le shell en cours #+ d'exécution et le co-processus, attend que les autres aient fini #+ d'écrire dans son propre pipe inscriptible.
La nouvelle commande intégrée mapfile permet de charger un tableau portant le contenu d'un fichier texte sans utiliser de boucle ni de substitution de commande.
#!/bin/bash4 mapfile Tabl1 < $0 # Même résultat que Tabl1=( $(cat $0) ) echo "${Tabl1[@]}" # Recopie tout ce script vers la sortie standard. echo "--"; echo # Mais pas le même que read -a !!! read -a Tabl2 < $0 echo "${Tabl2[@]}" # Lit seulement la première ligne du script #+ dans le tableau exit
La commande intégrée read a fait l'objet d'un léger lifting. L'option -t timeout accepte maintenant les valeurs (décimales) fractionnelles [128] et l'option -i permet le pré-chargement du tampon d'édition. [129] Malheureusement, l'implémentation des améliorations ci-dessus est encore en cours, elles ne sont donc pas (encore) utilisables dans des scripts.
La substitution de paramètre reçoit un nouvel opérateur de modification de la casse.
#!/bin/bash4 var=variableTresMelangee echo ${var} # variableTresMelangee echo ${var^} # VariableTresMelangee # * Premier caractère --> en majuscule echo ${var^^} # VARIABLETRESMELANGEE # ** Tous les caractères --> en majuscules echo ${var,} # variableTresMelangee # * Premier caractère --> en minuscule echo ${var,,} # variabletresmelangee # ** Tous les caractères --> en minuscules.
La commande intégrée declare accepte maintenant les options -l en minuscules (lowercase) et -c capitalisation (capitalize).
#!/bin/bash4 declare -l var1 # Changera en minuscules var1=VariableDeCasseMIXTE echo "$var1" # variabledecassemixte # Même effet que echo $var1 | tr A-Z a-z declare -c var2 # Changera juste le premier caractère en majuscule var2=au_depart_en_minuscules echo "$var2" # Au_depart_en_minuscules # PAS la même chose que echo $var2 | tr a-z A-Z
De nouvelles options pour le développement des accolades.
Incrémentation/décrémentation, spécifiée dans le dernier terme entre accolades.
#!/bin/bash4 echo {40..60..2} # 40 42 44 46 48 50 52 54 56 58 60 # Tous les nombres pairs de 40 à 60 echo {60..40..2} # 60 58 56 54 52 50 48 46 44 42 40 # Tous les nombres pairs entre 40 et 60, dans l'ordre inverse # Donc en fait c'est une décrémentation. echo {60..40..-2} # Même résultat. Le signe moins n'est pas nécessaire. # Est-ce que ça marche aussi avec les lettres et des symboles ? echo {X..d} # X Y Z [ ] ^ _ ` a b c d # N'affiche pas le caractère '\' d'échappement #+ d'espace.
Le remplissage avec des zéros, s'il est spécifié dans le premier terme entre accolades, ajoute le même nombre de zéros comme préfixe des nombres de la chaîne résultat.
bash4$ echo {010..15} 010 011 012 013 014 015 bash4$ echo {000..10} 000 001 002 003 004 005 006 007 008 009 010
L'extraction de sous-chaîne sur les paramètres de postion commence maintenant par $0, en tant qu'index zéro. (Ceci corrige une incohérence dans le traitement des paramètres de position.)
#!/bin/bash # montre-params.bash4 # Nécessite la version 4+ de Bash. # Invoquer ces scripts avec au moins un paramètre de position. E_MAUVAISPARAMS=99 if [ -z "$1" ] then echo "Usage $0 param1 ..." exit $E_MAUVAISPARAMS fi echo ${@:0} # bash3 montre-params.bash4 un deux trois # un deux trois # bash4 montre-params.bash4 un deux trois # show-params.bash4 un deux trois # $0 $1 $2 $3
Le nouvel opérateur de remplacement ** attrape tous les noms de fichiers et les noms de répertoires, récursivement.
#!/bin/bash4 # listefichiers.bash4 shopt -s globstar # Il faut activer globstar pour que ** fonctionne # L'option du shell 'globstar' est une nouveauté #+ de Bash 4 echo "Avec *"; echo for nomfichier in * do echo "$nomfichier" done # Liste seulement les fichiers du répertoire courant ($PWD). echo; echo "--------------"; echo echo "Avec **" for nomfichier in ** do echo "$nomfichier" done # Liste intégralement, récursivement, #+ l'arborescence de fichiers exit Using * tousmesfichiers listefichiers.bash4 -------------- Avec ** tousmesfichiers tousmesfichiers/index.des.fichiers.txt tousmesfichiers/ma_musique tousmesfichiers/ma_musique/je-chante-des-chansons-des-annees-60.ogg tousmesfichiers/ma_musique/je-chante-de-l-opera.ogg tousmesfichiers/ma_musique/lecon-de-piano.1.ogg tousmesfichiers/mes_images tousmesfichiers/mes_images/a-la-plage-avec-Jade.png tousmesfichiers/mes_images/piqueniqye-avec-Melissa.png listefichiers.bash4
Nouvelle variable interne $BASHPID.
On a une nouvelle commande intégrée, une fonction de traitement d'erreur appelée command_not_found_handle.
#!/bin/bash4 command_not_found_handle () { # Accepte les paramètres implicites. echo "La commande suivante n'est pas valable : \""$1\""" echo "Avec l'(es) argument(s) suivant(s) : \""$2\"" \""$3\""" # $4, $5 ... } # $1, $2, etc... ne sont pas passés explicitement à la fonction. mauvaise_commande arg1 arg2 # La commande suivante n'est pas valable : "mauvaise_commande" # avec l'(es) argument(s) suivant(s) : "arg1" "arg2"
La version 4.1 of Bash, publiée en mai 2010, était pour l'essentiel une mise à jour de correctifs.
La commande printf accepte maintenant l'option -v pour assigner les indexes des tableaux.
Entre les crochets doubles, les opérateurs de comparaison de chaînes de caractères > et < se conforment à présent à la locale. Étant donné que le réglage de la locale peut affecter l'ordre de tri des expressions littérales, il y a un risque d'effets de bord sur les tests de comparaison des expressions entre [[ ... ]].
La fonction intégrée read accepte maintenant l'option -N (read -N nb_car). Cette option provoque la sortie de read après nb_car caractères.
Exemple 37.8. Lecture de N caractères
#!/bin/bash # Nécessite Bash 4.1 au minimum nombre_de_caracteres=68 # Lit les 75 premiers read -N $nombre_de_caracteres var < $0 # caractères du script echo "$var" exit ####### Sortie du script ####### #!/bin/bash # Nécessite une version de Bash >= 4.1 nombre_de_caracteres=68
Here documents, intégrés dans une substitution de commande de la forme $( ... ), peuvent de terminer avec une simple ).
Exemple 37.9. Utilisation d'un here document pour assigner une valeur à une variable
#!/bin/bash # here-commsub.sh # Nécessite Bash 4.1 au minimum variable_multiligne=$( cat <<FINxxx ------------------------------ Ceci est la ligne 1 de la variable Ceci est la ligne 2 de la variable Ceci est la ligne 3 de la variable ------------------------------ FINxxx) # Rather than what Bash 4.0 requires: #+ that the terminating limit string and #+ the terminating close-parenthesis be on separate lines. # Ce qui est différent de la syntaxe de Bash 4.0, #+ qui exige que la chaîne marquant la fin de la #+ variable soit sur une ligne différente de #+ la parenthèse fermante. # FINxxx # ) echo "$variable_multiligne" # Bash affiche quand même un avertissement : # avertissement : « here-document » à la ligne 12 délimité #+ par la fin du fichier (au lieu de « FINxxx »)
La version 4.2 of Bash, publiée en février 2011, contient un certain nombre de nouvelles fonctionnalités et d'améliorations, en plus des corrections de bugs.
Bash implémente à présent les échappements Unicode \u et \U.
echo -e '\u2630' # Caractère triple tiret horizontal empilé # Équivaut à la formulation détournée suivante : echo -e "\xE2\x98\xB0" # Reconnue par les versions de Bash antérieures. echo -e '\u220F' # Pi (lettre grecque et symbole mathématique) echo -e '\u0416' # "Zhe" majuscule (caractère cyrillique) echo -e '\u2708' # Symbole avion (police Dingbat) echo -e "Le circuit de cet amplificateur nécessite une résistance de tirage de 100 \u2126." var_unicode='\u2640' echo -e $var_unicode # Symbole féminin printf "$var_unicode \n" # Symbole féminin avec retour à la ligne # Et pour un cas un peu plus recherché... # On peut stocker les symboles Unicode dans un tableau associatif #+ pour ensuite les retrouver par leur nom. # Lancez cet exemple dans un terminal Gnome, ou un terminal muni #+ d'une police assez grosse et grasse pour la lisibilité. declare -A symbole # Tableau associatif. symbole[script_E]='\u2130' symbole[script_F]='\u2131' symbole[script_J]='\u2110' symbole[script_M]='\u2133' symbole[Rx]='\u211E' symbole[TEL]='\u2121' symbole[FAX]='\u213B' symbole[care_of]='\u2105' symbole[account]='\u2100' symbole[trademark]='\u2122' echo -ne "${symbole[script_E]} " echo -ne "${symbole[script_F]} " echo -ne "${symbole[script_J]} " echo -ne "${symbole[script_M]} " echo -ne "${symbole[Rx]} " echo -ne "${symbole[TEL]} " echo -ne "${symbole[FAX]} " echo -ne "${symbole[care_of]} " echo -ne "${symbole[account]} " echo -ne "${symbole[trademark]} " echo
L'exemple ci-dessus utilise la syntaxe de développement de chaînes $' ... '.
Lorsque l'option du shell lastpipe est mise, la dernière commande dans un pipe n'est pas exécutée dans un sous-shell.
Exemple 37.10. Envoyer (par pipe) un flux d'entrée vers read
#!/bin/bash # lastpipe-option.sh ligne='' # Null value. echo "\$ligne = « $ligne »" # $ligne = echo shopt -s lastpipe # Erreur avant la version 4.2 echo "Code de retour du passage à 1 de l'option « lastpipe » : $?" # 1 pour les Bash avant 4.2, 0 dans le cas contraire. echo # Envoie, via un tube, la première ligne du head -1 $0 | read ligne # script à read. # ^^^^^^^^^^ Pas dans un sous-shell. echo "\$ligne = « $ligne »" # Anciennes versions de Bash $ligne = « » # Bash version 4.2 $ligne = « #!/bin/bash »
Cette option apporte éventuellement des « correctifs » pour ces scripts d'exemple : Exemple 34.3, « Envoyer la sortie de echo dans un tube pour un read » et Exemple 15.8, « Problèmes lors de la lecture d'un tube ».
Les indices de tableau négatifs permettent le décompte à l'envers, depuis la fin d'un tableau.
Exemple 37.11. Indices de tableau négatifs
#!/bin/bash # neg-array.sh # Nécessite Bash 4.2 au minimum tableau=( zero un deux trois quatre cinq ) # Tableau de 6 éléments. # 0 1 2 3 4 5 # -6 -5 -4 -3 -2 -1 # Il est maintenant possible d'utiliser des indices négatifs. echo ${tableau[-1]} # cinq echo ${tableau[-2]} # quatre # ... echo ${tableau[-6]} # zero # Les indices négatifs sont comptés à rebours à partir du # dernier élément+1 # Mais vous ne pouvez pas utiliser d'indice au-delà du # premier élément du tableau. echo ${tableau[-7]} # tableau: mauvais indice de tableau # Alors, à quoi sert cette nouvelle fonctionnalité ? echo "Le dernier élément du tableau est « ${tableau[-1]} »" # Ce qui est plus simple que : echo "Le dernier élément du tableau est « ${tableau[${#tableau[*]}-1]} »" echo # Et... indice=0 let "dernier_indice_negatif = 0 - ${#tableau[*]}" # Nombre d'éléments, converti en nombre négatif. while [ $indice -gt $dernier_indice_negatif ]; do ((indice--)); echo -n "${tableau[indice]} " done # Affiche les éléments du tableau, à l'envers. # Nous venons de simuler la commande « tac » avec ce # tableau. echo # Consultez également « neg-offset.sh ».
L'extraction de sous-chaîne utilise un paramètre de longueur négative pour spécifier un intervalle depuis la fin de la chaîne cible.
Exemple 37.12. Paramètre négatif dans une extraction de chaîne
#!/bin/bash # Bash, version 4.2 au minimum # Utilisation d'indices négatifs dans l'extraction de sous-chaînes. # Attention : cela change l'interprétation de cette structure. chaineZ=abcABC123ABCabc echo ${chaineZ} # abcABC123ABCabc # Position dans la chaine : 0123456789..... echo ${chaineZ:2:3} # cAB # Sautez 2 caractères à partir du début de la chaîne, et #+ extrayez-en les 3 caractères suivant. # ${chaîne:position:longeur} # So far, nothing new, but now ... # Jusqu'à présent, rien de neuf, mais maintenant... # abcABC123ABCabc # Position dans la chaine : 0123....6543210 echo ${chaineZ:3:-6} # ABC123 # ^ # Identifiez les indices situés 3 caractères après le début et #+ 6 caractères avant la fin et extrayez tous les caractères #+ entre les 2. # ${chaîne:position-depuis-le-début:position-depuis-la-fin} # Lorsque le paramètre « longueur » est négatif, il indique la #+ position à partir de la fin. # Consultez également « neg-array.sh ».
[126] Plus particulièrement, Bash à partir de la version 4 comprend un support limité des tableaux associatifs. C'est une implémentation de base, à laquelle il manque la plupart des fonctionnalités des tableaux du même genre dans les autres langages de programmation.
[127] Copyright 1995-2009 by Chester Ramey.
[130] Et pendant que vous y êtes, pensez à remédier au problème bien connu du piped read.