Chapitre 9. Chaînes de caractères

Table des matières

9.1. Protection de caractères
9.1.1. Mécanismes
9.1.2. Exemples d’utilisation
9.2. Longueur d'une chaîne de caractères
9.3. Modificateurs de chaînes
9.4. Extraction de sous-chaînes
9.5. Remplacement de sous-chaînes
9.6. Transformation en majuscules/minuscules
9.7. Formatage de chaînes
9.8. Génération de chaînes de caractères

9.1. Protection de caractères

9.1.1. Mécanismes

Le shell utilise différents caractères particuliers pour effectuer ses propres traitements ($ pour la substitution, > pour la redirection de la sortie standard, * comme caractère générique, etc.). Pour utiliser ces caractères particuliers comme de simples caractères, il est nécessaire de les protéger de l'interprétation du shell. Trois mécanismes sont utilisables :

  • Protection d'un caractère à l'aide du caractère \

    Ce caractère protège le caractère qui suit immédiatement le caractère \.

    $ ls
    tata  toto
    $ echo *
    tata toto
    $ echo \*	=> le caractère * perd sa signification de caractère générique
    *
    $
    $ echo \\  => le deuxième caractère \ perd sa signification de caractère de protection
    \
    $ 
    $

    Le caractère \ permet également d’ôter la signification de la touche Entrée. Cela a pour effet d’aller à la ligne sans qu’il y ait exécution de la commande. En effet, après saisie d’une commande, l’utilisateur demande au shell l’exécution de celle-ci en appuyant sur cette touche. Annuler l’interprétation de la touche Entrée autorise l’utilisateur à écrire une longue commande sur plusieurs lignes.

    Dans l’exemple ci-dessous, la signification de la touche Entrée a été inhibée par le caractère \ : bash détecte que la commande interne echo n’est pas terminée et affiche la chaîne d’appel contenue dans la variable prédéfinie PS2 du shell.

    $ echo coucou \Entrée
    > salut Entrée   => terminaison de la commande : le shell l’exécute !
    coucou salut
    $

  • Protection partielle "chaîne"

    A l'intérieur d'une paire de guillemets, tous les caractères de chaîne sauf $ \ ` " sont protégés de l'interprétation du shell. Cela signifie, par exemple, que le caractère $ sera quand même interprété comme une substitution à effectuer.

    $ echo  "< * $PWD  ' >"
    < * /home/sanchis ' >
    $
    $ echo  "\"$PS2\""
    "> "			=> valeur de la variable prédéfinie PS2 entre guillemets
    $
  • Protection totale 'chaîne'

    Entre une paire d’apostrophes (), aucun caractère de chaîne (sauf le caractère ') n'est interprété.

    $ echo '< * $PWD * >'
    < * $PWD * >
    $

    L’utilisation du caractère apostrophe peut provoquer l’erreur suivante.

    $ echo c'est lundi
    >			=> le shell interprète l’apostrophe comme un début de chaîne à 
    > '			=>    protéger et attend par conséquent la deuxième apostrophe
    cest lundi
    
    
    $

    Pour éviter cela, on peut supprimer la signification particulière de cette apostrophe ou bien utiliser une paire de guillemets.

    $ echo N\'oublie pas !
    N'oublie pas !
    $
    $ echo "c'est lundi"
    c'est lundi
    $

9.1.2. Exemples d’utilisation

  • Certaines commandes unix telles que find utilisent pour leur propre fonctionnement les mêmes caractères génériques que ceux du shell. Utilisés sans précaution, ces caractères provoquent le plus souvent une erreur d’exécution. Par exemple, la commande

                 find . -name *.py  –print 

    recherche à partir du répertoire courant et affiche (ou plutôt devrait afficher) tous les noms d’entrées se terminant par la chaîne .py.

    $ ls -l
    total 20
    -rw-r--r-- 1 sanchis sanchis 1280 oct.   5  2010 cliTCP.py
    drwxr-xr-x 2 sanchis sanchis 4096 oct.   5  2010 Divers
    -rwxr--r-- 1 sanchis sanchis  117 oct.   5  2010 ou
    -rwxr--r-- 1 sanchis sanchis  117 oct.   5  2010 ou0
    -rw-r--r-- 1 sanchis sanchis 2397 oct.   5  2010 servTCP.py
    $
    $ find . -name *.py -print
    find: les chemins doivent précéder l'expression : servTCP.py
    Utilisation : find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [chemin...] [expression]
    $

    L’origine du problème est que le caractère * aurait dû être interprété par la commande find alors qu’il a été interprété par bash, ce qui a conduit à l’exécution de la commande

                 find . -name cliTCP.py  servTCP.py –print

    La commande find signale une erreur de syntaxe car elle n’attend qu’une seule chaîne après son option –name.

    Pour corriger le problème, il suffit de protéger le caractère * de l’interprétation de bash.

    $ find . -name "*.py" -print
    ./servTCP.py
    ./cliTCP.py
    $

    Dans ce cas de figure, le type de protection utilisé n’a pas d’importance : on aurait également pu écrire '*.py' ou \*.py.

  • La présence de caractères espace (ou de caractères spéciaux de bash) dans la valeur d’un paramètre (variable ou paramètre de position) peut poser problème lorsque cette valeur participe à l’exécution d’une commande.

    Soit le programme shell nblig0 qui affiche le nombre de lignes (commande wc –l) d’un fichier dont le nom est saisi par l’utilisateur et contenu dans la variable rep.

    	#!/bin/bash
    	#	@(#)	nblig0
    
    	read -p "Saisissez un nom d'entree : " rep
    	wc -l $rep
    
    $ ls -l
    total 12
    -rw-r--r-- 1 sanchis sanchis 1728 nov.   1  2010 Meteo du jour.txt
    -rwxr--r-- 1 sanchis sanchis   83 nov.   1  2010 nblig
    -rwxr--r-- 1 sanchis sanchis   82 nov.   1  2010 nblig0
    $ 
    $ nblig0
    Saisissez un nom d'entree : Meteo du jour.txt
    wc: Meteo: Aucun fichier ou dossier de ce type
    wc: du: Aucun fichier ou dossier de ce type
    wc: jour.txt: Aucun fichier ou dossier de ce type
    0 total
    $

    La variable rep contient bien la chaîne Meteo du jour.txt mais après substitution de $rep, la commande qui sera finalement exécutée est : wc –l Meteo du jour.txt

    La commande unix wc n’est pas exécutée avec un seul argument mais avec trois. Chacun d’eux est considéré par wc comme un nom de fichier qui, absent, provoque une erreur d’exécution.

    La solution à ce problème consiste à préserver l’intégrité de la valeur de la variable rep après que la substitution ait été effectuée par le shell. On utilise pour cela la syntaxe "$rep". Il est à noter que la syntaxe '$rep' aurait été inadéquate car elle interdit au shell d’effectuer la substitution $rep.

    $ cat nblig
    #!/bin/bash
    #	@(#)	nblig
    
    read -p "Saisissez un nom d'entree : " rep
    wc -l "$rep"
    
    $ 
    $ nblig
    Saisissez un nom d'entree : Meteo du jour.txt
    35 Meteo du jour.txt
    $

    De manière générale, il est fortement conseillé de préserver l’intégrité d’une chaîne de caractères obtenue après une substitution, surtout lorsque l’on ne connait pas à l’avance la composition de cette chaîne.

    Cette politique est systématiquement suivie dans l’écriture des programmes shell qui accompagnent l’interpréteur bash (ex : .bashrc, .profile).

    $ cat ~/.profile
    # ~/.profile: executed by the command interpreter for login shells.
    #	. . . 
    
    # if running bash
    if [ -n "$BASH_VERSION" ]; then
       	# include .bashrc if it exists
       	if [ -f "$HOME/.bashrc" ]; then
    		. "$HOME/.bashrc"
       	fi
    fi
    
    # set PATH so it includes user's private bin if it exists
    #if [ -d "$HOME/bin" ] ; then
    #	PATH="$HOME/bin:$PATH"
    #fi
    $

    Les fichiers shell .bashrc et .profile permettent à l’utilisateur de configurer son environnement de travail [cf. Chapitre 1, Introduction à Bash, § 1.1]. Ces deux fichiers sont présents dans le répertoire de connexion de l’utilisateur dont le chemin peut être rapidement désigné par le caractère ~.

  • Avant qu’une syntaxe spécifique ne soit introduite dans bash, le mécanisme d’indirection [cf. Chapitre 2, Substitution de paramètres, § 6] était obtenu à l’aide de la commande interne eval et des caractères de protection du shell.

    La syntaxe de cette commande interne est la suivante : eval [ arg ... ]

    La commande interne eval interprète au sens du shell (substitution de paramètres, etc.) chacun de ses arguments arg puis concatène la chaîne résultante en une seule chaîne. Celle-ci est ensuite à nouveau interprétée et exécutée comme une commande par le shell. En d’autres termes, eval permet d’effectuer une interprétation en deux passes.

    $ var=v1
    $ v1=un
    $ eval echo \$$var
    un
    $

    Lors de la première passe, le première caractère $ n’est pas interprété car il est protégé de l’interprétation du shell (\$) et la chaîne $var est remplacée par la valeur de la variable var. A l’issu de la première interprétation, on obtient la chaîne suivante : echo $v1

    Cette chaîne est ensuite interprétée comme commande par bash.

    Une autre manière de procéder est la suivante :

    $ var=\$v1
    $ v1=un
    $ 
    $ eval echo $var
    un
    $

    Cette formulation permet de généraliser le mécanisme et d’obtenir une indirection multi niveau.

    $ var=\$v1
    $ v1=\$v2
    $ v2=\$v3
    $ v3=trois
    $ 
    $ eval eval eval echo $var
    trois
    $

    Les interprétations successives donnent les résultats suivants :

    $ echo $var
    $v1
    $ eval echo $var
    $v2
    $ eval eval echo $var
    $v3
    $
    $ eval eval eval echo $var
    trois
    $