Une compilation de documentations   { en , fr }

Manipulation des chaînes de caractères et des tableaux en bash

Étiquettes:
Créé en:
Dernière modification:
Auteur:
Xavier Béguin

Notez que la plupart des fonctionnalités décrites dans ce document (voire la totalité) sont spécifiques à bash et ne font pas partie de la norme POSIX. Elles ne fonctionneront donc pas avec d'autres interpréteurs de commandes.

Il est donc plus prudent de ne les utiliser que dans des scripts ad-hoc pour lesquels la dépendance à bash ne sera jamais un problème.

Extraire une sous-chaîne

Sur une chaîne de caractères

Bash permet d'extraire une partie d'une chaîne de caractère conservée dans une variable en utilisant ce type de syntaxe :

${paramètre:début:longueur}

Si paramètres est une chaîne de caractère, l'expression ci-dessus se développe pour fournir la sous-chaîne débutant au caractère début (le premier étant 0) et de longueur longueur :

$ var="So long, and thanks for all the fish!"
$ echo "[${var:3:4}]"
[long]

À noter :

  • si longueur n'est pas précisée, la sous-chaîne sera extraite de la position début jusuq'à la fin de la valeur de paramètre:
    $ echo "[${var:13}]"
    [thanks for all the fish!]
    
  • début peut être un entier négatif, qui désignera alors une position à partir de la fin de la chaîne (le dernier caractère étant à la position -1) :
    $ var="So long, and thanks for all the fish!"
    $ echo "[${var: -13:3}]"
    [all]
    
    Notez qu'il faut impérativement placer une espace après le premier : pour éviter que bash ne confonde pas avec la syntaxe « ${paramètre:-mot} » (qui représente l'utilisation de valeur par défaut).
  • si longueur est négatif, il ne désignera alors plus la longueur de la sous-chaîne à extraire mais la position de la fin de la sous-chaîne à partir de la fin de la chaîne d'origine :
    $ var="So long, and thanks for all the fish!"
    $ echo "[${var: -13:-10}]"
    [all]
    
  • début et longueur sont évalués comme des expressions arithmétiques, ce qui peut être pratique (bien que cet exemple manque d'imagination) :
    $ var="So long, and thanks for all the fish!"
    $ i=3
    $ echo "[${var:i:2+2}]"
    [long]
    

Sur un tableau indicé

Utilisée avec un tableau indicé, cette fonctionnalité permet d'en extraire un sous-ensemble d'éléments. Par exemple, on récupèrera ainsi 2 éléments à partir de la position 3 du tableau indicé :

$ tableau=("zéro" "un" "deux" "trois" "quatre" "cinq")
$ echo "${tableau[@]:3:2}"
trois quatre

En utilisant @ comme paramètre, on peut extraire un sous-ensemble des paramètres du script ou de la fonction en cours (@ agit comme tout tableau indicé) :

$ myfunc() { echo "${@:3:2}"; }
$ myfunc a b c d e
c d

Cette fonctionnalité n'est pas applicable sur un tableau associatif, son utilisation donnera un résultat indéfini.

Supprimer un préfixe

Sur une chaîne de caractères

Pour supprimer un motif du début d'une chaîne de caractères, on utilisera une syntaxe du type :

${paramètre#mot}
${paramètre##mot}

La première forme enlève le plus court motif correspondant, et la seconde le plus long.

Les motifs mentionnés ici et tout au long de ce document sont les motifs de développements de chemin de bash (comme ceux utilisés dans une commande comme ls *.txt). Ils sont décrits en détail dans la section Développement des chemins du manuel de bash, en français.

À noter aussi :

  • ce motif peut bien sûr aussi être une simple chaîne de caractères :
    $ var="So long, and thanks for all the fish!"
    $ echo "${var#So long, }"
    and thanks for all the fish!
    
  • ces motifs permettent par exemple de récupérer le nom de fichier à partir de son chemin absolu (un peu comme le fait la commande basename) :
    $ fichier="/usr/share/doc/bash/INTRO.gz"
    $ echo "${fichier##*/}"
    INTRO.gz
    
  • dans l'exemple ci-dessus, seule la suppression du plus long motif correspondant (avec ##) a un sens. La suppression du plus court motif (avec #) donnerait le résultat suivant :
    $ fichier="/usr/share/doc/bash/INTRO.gz"
    $ echo "${fichier#*/}"
    usr/share/doc/bash/INTRO.gz
    

Sur un tableau

Appliquée à un tableau indicé, l'opération de suppression du préfixe est effectuée sur tous ses éléments :

$ tableau=(dossier_fry.txt dossier_leela.txt dossier_bender.txt)
$ echo "${tableau[@]#dossier_}"
fry.txt leela.txt bender.txt

Sur un tableau associatif, la transformation est également opérée sur toutes les valeurs du tableau, qui sont retournées sans leur clef et sans ordre particulier :

$ declare -A tableau
$ tableau=(["leela"]="dossier_leela.txt" ["fry"]="dossier_fry.txt" ["bender"]="dossier_bender.txt")
$ echo "${tableau[@]#dossier_}"
fry.txt leela.txt bender.txt

Supprimer un suffixe

Sur une chaîne de caractères

La syntaxe suivante permet de supprimer un motif à la fin d'une chaîne de caractères :

${paramètre%mot}
${paramètre%%mot}

La première forme enlève le plus court motif correspondant, et la seconde le plus long (il s'agit bien sûr toujours des mêmes motifs que ceux des développement de chemins)

À noter aussi :

  • le motif peut représenter une simple chaîne de caractères, par exemple :
    $ var="So long, and thanks for all the fish!"
    $ echo "${var%for all the fish!}"
    So long, and thanks
    
  • ces motifs permettent par exemple de récupérer le répertoire où se trouve un fichier à partir de son chemin absolu (un peu comme le fait la commande dirname) :
    $ fichier="/usr/share/doc/bash/INTRO.gz"
    $ echo ${fichier%/*}
    /usr/share/doc/bash
    
  • attention, dans l'exemple ci-dessus, seule la suppression du motif le plus court correspondant a un sens. La suppression du motif le plus long supprimerait toute la chaîne :
    $ fichier="/usr/share/doc/bash/INTRO.gz"
    $ echo ${fichier%%/*}
    
    
  • on peut également supprimer l'extention du nom d'un fichier (pour rappel, le point n'a pas de signification particulière dans les les développements de chemins de bash, contrairement aux expressions régulières) :
    $ fichier="machin.txt"
    $ echo ${fichier%.*}
    machin
    

Sur un tableau

Appliqué à un tableau indicé, cette opération est appliquée sur tous ses éléments :

$ tableau=(fry.txt leela.txt bender.txt)
$ echo ${tableau[@]%.*}
fry leela bender

Sur un tableau associatif, la transformation est également opérée sur toutes les valeurs du tableau, qui sont retournées sans leur clef et sans ordre particulier :

$ declare -A tableau
$ tableau=(["fry"]="dossier_fry.txt" ["leela"]="dossier_leela.txt" ["bender"]="dossier_bender.txt")
$ echo "${tableau[@]%.*}"
dossier_fry dossier_leela dossier_bender

Remplacer un motif

Sur une chaîne de caractères

Pour remplacer un motif par une chaîne de caractère, on pourra utiliser la forme :

${paramètre/motif/chaîne}

Par exemple, en utilisant une simple chaîne de caractères comme motif :

$ var="So long, and thanks for all the fish!"
$ echo "${var/fish/scripts}"
So long, and thanks for all the scripts!

À noter :

  • si motif commence par un / (c'est à dire si le premier / est doublé), toutes les correspondances sont remplacées, sinon seule la première l'est ;
  • si motif commence par #, il doit correspondre au début de la chaîne ;
  • si motif commence par %, il doit correspondre à la fin de la chaîne ;
  • si chaîne est vide, les portions correspondant au motif sont supprimées et le / qui suit motif peut être omis.

Illustrations en exemples :

  • un motif sans préfixe particulier remplace la première correspondance :
    $ var="I see, ah ah ah"
    $ echo "${var/ah/XX}"
    I see, XX ah ah
    
  • un motif débutant par / remplace toutes les correspondances :
    $ var="I see, ah ah ah"
    $ echo "${var//ah/XX}"
    I see, XX XX XX
    
  • un motif débutant par # remplace une éventuelle correspondance en début de chaîne :
    $ var="I see, ah ah ah"
    echo "${var/#ah/XX}"
    I see, ah ah ah
    echo "${var/#I/You}"
    You see, ah ah ah
    
  • un motif débutant par % remplace une éventuelle correspondance en fin de chaîne :
    $ var="I see, ah ah ah"
    echo "${var/%ah/XX}"
    I see, ah ah XX
    echo "${var/%I/You}"
    I see, ah ah ah
    
  • si chaîne est vide, le dernier / peut être omis, et le développement de motif sera simplement supprimé de la chaîne (ou plutôt remplacé par la chaîne vide) :
    $ var="I see, ah ah ah"
    echo "${var/ah}!"
    I see,  ah ah!
    echo "${var//ah/}!"
    I see,   !
    

Sur un tableau

Appliqué à un tableau indicé, cette manipulation est là encore effectuée sur tous ses éléments :

$ tableau=(fry_and_bender.txt leela_and_fry.txt bender_versus_flexo.txt)
$ echo ${tableau[@]/bender/bender_the_best}
fry_and_bender_the_best.txt leela_and_fry.txt bender_the_best_versus_flexo.txt

Sur un tableau associatif, la transformation est également opérée sur toutes les valeurs du tableau, qui sont retournées sans leur clef et sans ordre particulier :

$ declare -A tableau
$ tableau=(["leela"]="dossier_leela.txt" ["fry"]="dossier_fry.txt" ["bender"]="dossier_bender.txt")
$ echo ${tableau[@]/bender/bender_the_best}
dossier_fry.txt dossier_leela.txt dossier_bender_the_best.txt

Modifier la casse

Sur une chaîne de caractères

Pour changer la casse des caractères dans la valeur d'une variable, on pourra utiliser une syntaxe du type :

${paramètre^motif}
${paramètre,motif}

Le caractère ^ change les lettres en majuscules, et , en minuscules (pour le retenir, pensez à une flèche vers le haut pour les majuscules, et vers le bas pour les minuscules). Lorsqu'on double le caractère (^^ ou ,,), toutes les lettres de la chaîne sont examinées, sinon seule la première lettre de la chaîne est affectée.

Cette fonctionnalité nécessite la version 4.0 ou supérieure de bash. À partir de sa version 5.1, bash propose également une façon peut-être plus intuitive de changer la casse de la valeur de paramètre, sous la forme ${paramètre@opérateur}, détaillée ci-dessous.

Pour l'expliquer plus précisément :

  • ${paramètre^motif} convertit en majuscule le premier caractère de la valeur de paramètre si c'est une lettre et qu'elle correspond à motif ;
  • ${paramètre^^motif} convertit en majuscule toutes les lettres correspondant à motif dans la valeur de paramètre ;
  • ${paramètre,motif} convertit en minuscule le premier caractère de la valeur de paramètre si c'est une lettre et qu'elle correspond à motif ;
  • ${paramètre,,motif} convertit en minuscule toutes les lettres correspondant à motif dans la valeur de paramètre.

Si motif est vide dans les expressions ci-dessus (donc dans des expressions comme ${paramètre^^}, par exemple), le motif utilisé par défaut est ?, qui correspond à n'importe quelle lettre. Les lettres de la chaîne concernées par l'opération sont donc systématiquement transformées.

Illustrations en exemples :

  • pour convertir en majuscule la première lettre de paramètre si c'est une lettre et qu'elle correspond à motif :
    $ var="so long, and thanks for all the fish!"
    $ echo "${var^t}"
    so long, and thanks for all the fish!
    $ echo "${var^s}"
    So long, and thanks for all the fish!
    
  • pour convertir en majuscules toutes les lettres de paramètre qui correspondent à motif :
    $ var="so long, and thanks for all the fish!"
    $ echo "${var^^s}"
    So long, and thankS for all the fiSh!
    
  • pour convertir en majuscules toutes les lettres de paramètre :
    $ var="so long, and thanks for all the fish!"
    $ echo "${var^^}"
    SO LONG, AND THANKS FOR ALL THE FISH!
    
  • pour convertir en minuscule la première lettre de paramètre si elle correspond à motif :
    var="SO LONG, AND THANKS FOR ALL THE FISH!"
    echo "${var,T}"
    SO LONG, AND THANKS FOR ALL THE FISH!
    echo "${var,S}"
    sO LONG, AND THANKS FOR ALL THE FISH!
    
  • pour convertir en minuscules toutes les lettres de paramètre qui correspondent à motif :
    $ var="SO LONG, AND THANKS FOR ALL THE FISH!"
    $ echo "${var,,[ST]}"
    sO LONG, AND tHANKs FOR ALL tHE FIsH!
    
  • pour convertir en minuscules toutes les lettres de paramètre :
    $ var="SO LONG, AND THANKS FOR ALL THE FISH!"
    $ echo "${var,,}"
    so long, and thanks for all the fish!
    

Sur un tableau

Cette fonctionnalité peut être utilisée sur tableau indicé pour transformer chacun de ses éléments :

$ tableau=(fry leela bender farnsworth)
$ echo ${tableau[@]^}
Fry Leela Bender Farnsworth
$ echo ${tableau[@]^^}
FRY LEELA BENDER FARNSWORTH

Sur un tableau associatif, la transformation est opérée sur toutes les valeurs du tableau, qui sont retournées sans leur clef et sans ordre particulier :

$ declare -A tableau
$ tableau=(["leela"]="dossier_leela.txt" ["fry"]="dossier_fry.txt" ["bender"]="dossier_bender.txt")
$ echo "${tableau[@]^}"
Dossier_fry.txt Dossier_leela.txt Dossier_bender.txt
$ echo "${tableau[@]^^}"
DOSSIER_FRY.TXT DOSSIER_LEELA.TXT DOSSIER_BENDER.TXT

Modifier la casse (solution alternative)

Sur une chaîne de caractères

À partir de sa version 5.1, bash propose un opérateur de transformations sous la forme ${paramètre@opérateur} qui permet lui aussi de modifier la casse des lettres de la valeur de paramètre, grâce à ses opérateurs U, u, et L. Il ne permet toutefois pas de sélectionner les lettres à transformer avec un motif :

  • ${paramètre@U} convertit en majuscules toutes les lettres de la valeur de paramètres ;
  • ${paramètre@u} convertit en majuscule le premier caractère de la valeur de paramètres, s'il s'agit d'une lettre ;
  • ${paramètre@L} convertit en minuscules toutes les lettres de la valeur de paramètres.

En voici les illustrations en exemples :

  • pour convertir en majuscules toutes les lettres de paramètre :
    $ var="so long, and thanks for all the fish!"
    $ echo "${var@U}"
    SO LONG, AND THANKS FOR ALL THE FISH!
    
  • pour convertir en majuscule la première lettre de paramètre si c'est une lettre :
    $ var="so long, and thanks for all the fish!"
    $ echo "${var@u}"
    So long, and thanks for all the fish!
    
    Notez que les autres lettres ne sont pas transformées en minucules :
    $ var="SO LONG, AND THANKS FOR ALL THE FISH!"
    $ echo "${var@u}"
    SO LONG, AND THANKS FOR ALL THE FISH!
    
  • pour convertir en minuscules toutes les lettres de paramètre :
    $ var="SO LONG, AND THANKS FOR ALL THE FISH!"
    $ echo "${var@L}"
    so long, and thanks for all the fish!
    

Sur un tableau

Appliqué à un tableau, la transformation de casse est opérée sur chacun de ses élements :

  • pour transformer en masjuscules toutes les lettres des éléments d'un tableau :
    $ tableau=(fry leela bender farnsworth)
    $ echo "${tableau[@]@U}"
    FRY LEELA BENDER FARNSWORTH
    
  • pour transformer en masjuscules le premier caractère de chaque éléments d'un tableau, s'il s'agit d'une lettre :
    $ tableau=(fry leela bender farnsworth)
    $ echo "${tableau[@]@u}"
    Fry Leela Bender Farnsworth
    
  • pour transformer en minuscules toutes les lettres des éléments d'un tableau :
    $ tableau=(FRY LEELA BENDER FARNSWORTH)
    $ echo "${tableau[@]@L}"
    fry leela bender farnsworth
    

L'opération fonctionne de la même manière sur un tableau associatif. La transformation est opérée sur toutes les valeurs du tableau, qui sont retournées sans leur clef et sans ordre particulier :

$ declare -A tableau
$ tableau=(["leela"]="dossier_leela.txt" ["fry"]="dossier_fry.txt" ["bender"]="dossier_bender.txt")
$ echo "${tableau[@]@U}"
DOSSIER_FRY.TXT DOSSIER_LEELA.TXT DOSSIER_BENDER.TXT

Obtenir la longueur d'une variable

Sur une chaîne de caractères

Pour obtenir la longueur d'une chaîne de caractères conservée dans une variable, on la préfixe par # entre accolades :

$ var="So long, and thanks for all the fish!"
$ echo "${#var}"
37

Sur un tableau

Avec un tableau indicé ou associatif, cette opération renvoit le nombre d'éléments qu'il contient :

$ tableau=(fry leela bender farnsworth)
$ echo ${#tableau[@]}
4
$ declare -A tableau
$ tableau=(["fry"]="dossier_fry.txt" ["bender"]="dossier_bender.txt")
$ echo ${#tableau[@]}
2