372 votes

Échapper une chaîne pour un motif de remplacement sed

Dans mon bash script j'ai une chaîne externe (reçue de l'utilisateur), que je devrais utiliser dans le motif sed.

REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"

Comment puis-je échapper à la $REPLACE afin qu'elle soit acceptée en toute sécurité par sed comme un remplacement littéral ?

NOTE : Le site KEYWORD est une sous-chaîne muette sans correspondance, etc. Il n'est pas fourni par l'utilisateur.

13 votes

Essayez-vous d'éviter le problème des "Petites Tables Bobby" si elles disent "/g -e 's/PASSWORD=.*/PASSWORD=abc/g'" ?

3 votes

Si vous utilisez bash, vous n'avez pas besoin de sed. Utilisez simplement outputvar="${inputvar//"$txt2replace"/"$txt2replacewith"}".

0 votes

@destenson Je pense que vous ne devriez pas mettre les deux variables à l'extérieur des guillemets. Bash peut lire les variables à l'intérieur des guillemets doubles (dans votre exemple, les espaces blancs pourraient faire foirer les choses).

323voto

Pianosaurus Points 1734

Avertissement : Cela ne pas considérer les nouvelles lignes. Pour une réponse plus approfondie, voir cette question sur le SO à la place. (Merci, Ed Morton & Niklas Peter)

Notez que le fait de tout échapper est une mauvaise idée. Sed a besoin que de nombreux caractères soient échappés vers obtenir leur signification particulière. Par exemple, si vous échappez un chiffre dans la chaîne de remplacement, il se transformera en une rétro-référence.

Comme l'a dit Ben Blank, seuls trois caractères doivent être échappés dans la chaîne de remplacement (les caractères échappés eux-mêmes, la barre oblique pour la fin de la déclaration et & pour le remplacement de tous) :

ESCAPED_REPLACE=$(printf '%s\n' "$REPLACE" | sed -e 's/[\/&]/\\&/g')
# Now you can use ESCAPED_REPLACE in the original sed statement
sed "s/KEYWORD/$ESCAPED_REPLACE/g"

Si jamais vous avez besoin de vous échapper de la KEYWORD la chaîne suivante est celle qu'il vous faut :

sed -e 's/[]\/$*.^[]/\\&/g'

Et peut être utilisé par :

KEYWORD="The Keyword You Need";
ESCAPED_KEYWORD=$(printf '%s\n' "$KEYWORD" | sed -e 's/[]\/$*.^[]/\\&/g');

# Now you can use it inside the original sed statement to replace text
sed "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/g"

N'oubliez pas que si vous utilisez un caractère autre que / comme délimiteur, vous devez remplacer la barre oblique dans les expressions ci-dessus par le caractère que vous utilisez. Voir le commentaire de PeterJCLaw pour l'explication.

Édité : En raison de certains cas particuliers non pris en compte auparavant, les commandes ci-dessus ont été modifiées plusieurs fois. Consultez l'historique des modifications pour plus de détails.

27 votes

Il convient de noter que vous pouvez éviter d'avoir à échapper les barres obliques en ne les utilisant pas comme délimiteurs. La plupart (toutes ?) des versions de sed vous permettent d'utiliser n'importe quel caractère, tant qu'il correspond au motif : $ echo 'foo/bar' | sed s_/_:_ # foo:bar

0 votes

@PeterJCLaw : Bon point. Je crois que c'est vrai pour toutes les versions de sed. Il n'y a que deux barres obliques échappées ci-dessus, donc cela ne ferait pas beaucoup de différence, mais cela compte si vous utilisez un autre délimiteur dans l'expression sed dans laquelle cette sortie est insérée. J'ai ajouté quelques informations pour refléter cela.

3 votes

sed -e 's/(\/\||) \\\ |&)/ \\ &/g' n'a pas fonctionné pour moi sous OSX mais ceci fonctionne : sed 's/([[ \\\ /&])/ \\ &/g' et c'est légèrement plus court.

52voto

Ben Blank Points 21786

Les trois seuls caractères littéraux qui font l'objet d'un traitement particulier dans la clause de remplacement sont les suivants / (pour fermer la clause), \ (pour l'échappement des caractères, la référence arrière, etc.), et & (pour inclure le match dans le remplacement). Par conséquent, tout ce que vous devez faire est d'échapper ces trois caractères :

sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"

Exemple :

$ export REPLACE="'\"|\\/><&!"
$ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
foo'"|\/><&!bar

1 votes

Et aussi un saut de ligne, je pense. Comment puis-je échapper à un saut de ligne ?

2 votes

Faites attention au comportement par défaut d'echo en ce qui concerne les backslashes. En bash, echo n'interprète pas par défaut les échappements de backslash, ce qui est le cas ici. En dash (sh), d'un autre côté, echo interprète les échappements de backslash et n'a aucun moyen, pour autant que je sache, de le supprimer. Donc, en dash (sh), au lieu de echo $x, faites printf '%s \n ' $x.

0 votes

De plus, utilisez toujours l'option -r lors d'une lecture pour traiter les antislashs dans les entrées utilisateur comme des littéraux.

35voto

Gurpartap Singh Points 1451

En me basant sur les expressions régulières de Pianosaurus, j'ai créé une fonction bash qui échappe à la fois le mot clé et le remplacement.

function sedeasy {
  sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}

Voici comment l'utiliser :

sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf

3 votes

merci ! si quelqu'un d'autre obtient une erreur de syntaxe en essayant de l'utiliser, comme moi, rappelez-vous juste de l'exécuter en utilisant bash, pas sh

2 votes

Existe-t-il une fonction permettant d'échapper une chaîne de caractères pour sed au lieu d'envelopper sed ?

1 votes

Hey, juste un avertissement général concernant le démarrage des tuyaux avec un écho comme celui-ci : Certaines (la plupart ?) des implémentations de echo prennent des options (cf. man echo ), ce qui fait que le pipe se comporte de manière inattendue lorsque votre argument $1 commence par un tiret. Au lieu de cela, vous pouvez commencer votre pipe par printf '%s\n' "$1" .

19voto

user2460464 Points 87

Il est un peu tard pour répondre... mais il existe un moyen beaucoup plus simple de le faire. Il suffit de changer le délimiteur (c'est-à-dire le caractère qui sépare les champs). Ainsi, au lieu de s/foo/bar/ vous écrivez s|bar|foo .

Et, voici le moyen le plus simple de le faire :

sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'

La sortie résultante est dépourvue de cette clause DEFINER désagréable.

12 votes

Non, & et `` doivent toujours être échappés, de même que le délimiteur, quel que soit celui qui est choisi.

3 votes

Cela a résolu mon problème, car j'avais des caractères "/" dans une chaîne de remplacement. Merci, mec !

0 votes

fonctionne pour moi. Ce que je fais, c'est essayer de m'échapper $ dans la chaîne de caractères qui va être modifiée, et maintenir la signification de $ dans la chaîne de remplacement. disons que je veux changer $XXX à la valeur de la variable $YYY , sed -i "s|\$XXX|$YYY|g" file fonctionne bien.

0voto

Alex Points 1848

Voici un exemple d'un AWK que j'ai utilisé il y a quelque temps. Il s'agit d'un AWK qui imprime de nouveaux AWKS. AWK et SED étant similaires, cela peut être un bon modèle.

ls | awk '{ print "awk " "'"'"'"  " {print $1,$2,$3} " "'"'"'"  " " $1 ".old_ext > " $1 ".new_ext"  }' > for_the_birds

Cela semble excessif, mais d'une manière ou d'une autre, cette combinaison de guillemets fonctionne pour que les ' soient imprimés comme des littéraux. Ensuite, si je me souviens bien, les variables sont juste entourées de guillemets comme ceci : "$1". Essayez-le et faites-moi savoir comment il fonctionne avec SED.

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