803 votes

Supprimer un préfixe/suffixe fixe d'une chaîne de caractères en Bash

Dans mon bash script J'ai une chaîne de caractères et son préfixe/suffixe. J'ai besoin de supprimer le préfixe/suffixe de la chaîne originale.

Par exemple, disons que j'ai les valeurs suivantes :

string="hello-world"
prefix="hell"
suffix="ld"

Comment arriver au résultat suivant ?

result="o-wor"

5 votes

Jetez un coup d'œil Guide avancé des scripts Bash

26 votes

Méfiez-vous des liens vers le soi-disant Guide avancé des scripts Bash ; il contient un mélange de bons et de mauvais conseils.

1217voto

Adrian Frühwirth Points 8883
$ foo=${string#"$prefix"}
$ foo=${foo%"$suffix"}
$ echo "${foo}"
o-wor

Ceci est documenté dans le Expansion des paramètres de l'enveloppe section du manuel :

${parameter#word}
${parameter##word}

Le mot est développé afin de produire un motif et mis en correspondance selon les règles décrites ci-dessous (cf. Correspondance de motifs ). Si le motif correspond au début de la valeur développée du paramètre, le résultat de l'expansion est la valeur développée du paramètre avec le motif correspondant le plus court (l'élément # ) ou le modèle de correspondance le plus long (le cas ## cas) supprimé. [ ]

${parameter%word}
${parameter%%word}

Le mot est développé afin de produire un motif et mis en correspondance selon les règles décrites ci-dessous (cf. Correspondance de motifs ). Si le motif correspond à une partie arrière de la valeur développée du paramètre, le résultat de l'expansion est la valeur du paramètre avec le motif de correspondance le plus court (l'élément % ) ou le modèle de correspondance le plus long (le cas %% cas) supprimé. [ ]

44 votes

Il y a aussi ## et %% , qui suppriment autant que possible si $prefix ou $suffix contiennent des caractères génériques.

47 votes

Y a-t-il un moyen de combiner les deux en une seule ligne ? J'ai essayé ${${string#prefix}%suffix} mais ça ne marche pas.

36 votes

@static_rtti Non, malheureusement, vous ne pouvez pas imbriquer la substitution de paramètres comme cela. Je sais, c'est dommage.

160voto

Chris Kolodin Points 49

En utilisant sed :

$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

Dans la commande sed, l'élément ^ correspond à un texte commençant par $prefix et la queue $ correspond au texte se terminant par $suffix .

Adrian Frühwirth soulève quelques bons points dans les commentaires ci-dessous, mais sed à cet effet peut être très utile. Le fait que le contenu de $prefix et $suffix soit interprété par sed peut être bon OU mauvais - tant que vous faites attention, tout devrait bien se passer. La beauté de la chose est que vous pouvez faire quelque chose comme ceci :

$ prefix='^.*ll'
$ suffix='ld$'
$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor

qui peut être ce que vous voulez, et qui est à la fois plus sophistiqué et plus puissant que la substitution de variable en bash. Si vous vous souvenez que de grands pouvoirs impliquent de grandes responsabilités (comme le dit Spiderman), tout devrait bien se passer.

Une introduction rapide à sed peut être trouvée à http://evc-cit.info/cit052/sed_tutorial.html

Une note concernant le shell et son utilisation des chaînes de caractères :

Pour l'exemple particulier donné, la formule suivante fonctionnerait également :

$ echo $string | sed -e s/^$prefix// -e s/$suffix$//

...mais seulement parce que :

  1. echo ne se soucie pas du nombre de chaînes de caractères dans sa liste d'arguments, et
  2. Il n'y a pas d'espace dans $prefix et $suffix

C'est généralement une bonne pratique de citer une chaîne sur la ligne de commande, car même si elle contient des espaces, elle sera présentée à la commande comme un seul argument. Nous mettons $prefix et $suffix entre guillemets pour la même raison : chaque commande d'édition à sed sera passée comme une seule chaîne. Nous utilisons des guillemets doubles parce qu'ils permettent l'interpolation de variables ; si nous avions utilisé des guillemets simples, la commande sed aurait obtenu une chaîne littérale $prefix y $suffix ce qui n'est certainement pas ce que nous voulions.

Remarquez également que j'utilise des guillemets simples pour définir les variables prefix y suffix . Nous ne voulons certainement pas que les chaînes de caractères soient interprétées, nous les mettons donc entre guillemets afin qu'aucune interpolation n'ait lieu. Encore une fois, ce n'est peut-être pas nécessaire dans cet exemple, mais c'est une très bonne habitude à prendre.

11 votes

Malheureusement, c'est un mauvais conseil pour plusieurs raisons : 1) Non cotées, $string est soumis à la division des mots et à la globalisation. 2) $prefix y $suffix peut contenir des expressions qui sed interpréteront, par exemple, des expressions régulières ou le caractère utilisé comme délimiteur qui brisera toute la commande. 3) Appel sed deux fois n'est pas nécessaire (vous pouvez -e 's///' -e '///' à la place) et le tuyau pourrait également être évité. Par exemple, considérez string='./ *' et/ou prefix='./' et le voir se casser horriblement à cause de 1) y 2) .

0 votes

Note amusante : sed peut prendre presque n'importe quoi comme délimiteur. Dans mon cas, puisque j'analysais les préfixes-répertoires à partir des chemins, je ne pouvais pas utiliser / alors j'ai utilisé sed "s#^$prefix## à la place. (Fragilité : les noms de fichiers ne peuvent pas contenir # . Comme je contrôle les fichiers, nous sommes en sécurité, là).

0 votes

@Olie Les noms de fichiers peuvent contenir cualquier à l'exception de la barre oblique et du caractère nul. Ainsi, à moins que vous ne contrôliez la situation, vous ne pouvez pas supposer qu'un nom de fichier ne contient pas certains caractères.

31voto

Vijay Vat Points 317
$ string="hello-world"
$ prefix="hell"
$ suffix="ld"

$ #remove "hell" from "hello-world" if "hell" is found at the beginning.
$ prefix_removed_string=${string/#$prefix}

$ #remove "ld" from "o-world" if "ld" is found at the end.
$ suffix_removed_String=${prefix_removed_string/%$suffix}
$ echo $suffix_removed_String
o-wor

Notes :

$prefix : l'ajout de # garantit que la sous-chaîne "hell" n'est supprimée que si elle se trouve au début. %$suffix : l'ajout de % permet de s'assurer que la sous-chaîne "ld" est supprimée uniquement si elle se trouve à la fin.

Sans cela, les sous-chaînes "hell" et "ld" seront supprimées partout, même si elles se trouvent au milieu.

1 votes

Merci pour les Notes ! qq : dans votre exemple de code, vous avez également un slash avant / juste après la chaîne, c'est pour quoi faire ?

1 votes

/ sépare la chaîne actuelle et la sous-chaîne. La sous-chaîne est ici le suffixe de la question posée.

26voto

tommy.carstensen Points 636

Connaissez-vous la longueur de votre préfixe et de votre suffixe ? Dans votre cas :

result=$(echo $string | cut -c5- | rev | cut -c3- | rev)

Ou plus général :

result=$(echo $string | cut -c$((${#prefix}+1))- | rev | cut -c$((${#suffix}+1))- | rev)

Mais le solution d'Adrian Frühwirth est vraiment cool ! Je ne connaissais pas ça !

22voto

J'utilise grep pour supprimer les préfixes des chemins (qui ne sont pas bien gérés par la commande sed ) :

echo "$input" | grep -oP "^$prefix\K.*"

\K supprime de la correspondance tous les caractères qui le précèdent.

2 votes

grep -P est une extension non standard. Si elle est prise en charge par votre plate-forme, vous pouvez en profiter, mais c'est un conseil douteux si votre code doit être raisonnablement portable.

0 votes

@tripleee En effet. Mais je pense qu'un système avec GNU Bash installé a aussi un grep qui supporte PCRE.

3 votes

Non, MacOS par exemple est équipé de Bash, mais pas de GNU. grep . Les versions antérieures avaient en fait le -P option de BSD grep mais ils l'ont retiré.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X