Comment passer une variable contenant des barres obliques à sed

100

Comment passer une variable contenant des barres obliques comme motif à sed?

Par exemple, si j'ai la variable suivante:

var="/Users/Documents/name/file"

Je veux le transmettre sedcomme tel:

sed "s/$var/replace/g" "$file"

Cependant, j'obtiens des erreurs. Comment puis-je contourner le problème?

buydadip
la source

Réponses:

193

Utilisez un autre délimiteur de regex car sedvous permet d'utiliser n'importe quel délimiteur ( y compris les caractères de contrôle ):

sed "s~$var~replace~g" $file
anubhava
la source
2
Ça ne marche pas. J'essaye sed -i "s ~ blah ~ $ var ~ g file" et j'obtiens: "sed -e expression # 1, char 70: commande` s 'non terminée ". J'ai confirmé que le coupable était une barre oblique dans $ var. Des variantes de cette solution à cela sont partout et aucune d'elles ne fonctionne. Sed a-t-il été mis à jour récemment? Je suis sur v4.2.2 sur Ubuntu 14.04.4 LTS.
Paul Ericson
Cela dépend de la valeur de$var
anubhava
1
Ne fonctionne pas avec ~ comme @PaulEricson l'a observé, mais fonctionne toujours avec |
Kenny Ho
1
Le dernier "~" (après g) ne semble pas être nécessaire, du moins pour AWS Amazon Linux (basé sur CentOS)
Saúl Martínez Vidals
1
Vous avez sauvé ma journée
user7131571
62

Une réponse pure bash: utilisez l' expansion des paramètres pour échapper à la barre oblique inverse des barres obliques dans la variable:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file
Glenn Jackman
la source
1
C'est un bon moyen, car vous ne savez pas toujours quels caractères contiennent la variable. Ainsi, vous pouvez toujours échapper au délimiteur sed en cas de doute.Par exemple: sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L
Belle. J'ai rendu certains de mes scripts de renommage beaucoup plus propres.
Cloud
J'ai appris quelque chose de beau aujourd'hui, merci. Devrait être la réponse acceptée.
Steve Kehlet
6
Un peu de clarté sur l'être de modèle utilisé ici: ${parameter/pattern/string}. Donc, dans ce cas, le paramètre est var, le modèle est /\/et la chaîne est \\/. Toutes les instances du modèle sont remplacées car le modèle commence par un /.
Josh
1
@josh, et par conséquent, la barre oblique du motif ne fait pas partie du motif à remplacer.
glenn jackman
32

Une autre façon de le faire, bien que plus moche que la réponse d'anubhava, consiste à échapper toutes les barres obliques inverses en varutilisant une autre sedcommande:

var=$(echo "$var" | sed 's/\//\\\//g')

alors, cela fonctionnera:

sed "s/$var/replace/g" $file
buydadip
la source
12

L'utilisation de /in sed comme délimiteur entrera en conflit avec les barres obliques de la variable lorsqu'elle sera remplacée et, par conséquent, vous obtiendrez une erreur. Une façon de contourner ce problème consiste à utiliser un autre délimiteur unique parmi tous les caractères de cette variable.

var="/Users/Documents/name/file"

vous pouvez utiliser le caractère octothorpe qui convient à l'occasion (ou tout autre caractère qui n'est pas un /pour la facilité d'utilisation)

sed "s#$var#replace#g" 

ou

sed 's#$'$var'#replace#g'

cela convient lorsque la variable ne contient pas d'espaces

ou

sed 's#$"'$var'"#replace#g'

Il est plus sage d'utiliser ce qui précède car nous sommes intéressés à substituer ce qui se trouve dans cette variable uniquement par rapport à la double citation de la commande entière, ce qui peut amener votre shell à interpréter tout caractère qui pourrait être considéré comme un caractère spécial du shell.

repzero
la source
Ça ne marche pas. J'essaye sed -i "s ~ blah ~ $ var ~ g file" et j'obtiens: "sed -e expression # 1, char 70: commande` s 'non terminée ". J'ai confirmé que le coupable était une barre oblique dans $ var. Des variantes de cette solution à cela sont partout et aucune d'elles ne fonctionne. Sed a-t-il été mis à jour récemment? Je suis sur v4.2.2 sur Ubuntu 14.04.4 LTS.
Paul Ericson
@PaulEricson Je ne peux pas reproduire cela sur aucun Ubuntu à ma disposition. Si votre $varcontient ~, vous devrez évidemment choisir un autre délimiteur. L'erreur "non terminée" sonne comme si votre $varcontenait une nouvelle ligne; vous pourrez peut-être le réparer en échappant à chaque nouvelle ligne dans la valeur, mais je ne pense pas que ce soit entièrement portable. Cela n'a généralement aucun rapport avec le problème des barres obliques dans la valeur.
tripleee
@PaulEricson Oh et votre citation est incorrecte, vous ne devriez pas avoir le nom de fichier entre guillemets. sed -i "s~blah~$var~g" fileavec des guillemets uniquement autour du sedscript réel .
tripleee
5

C'est une vieille question, mais aucune des réponses ici ne traite des opérations autrement qu'en s/from/to/détail.

La forme générale d'une seddéclaration est

*address* *action*

où l' adresse peut être une plage de regex ou une plage de numéros de ligne (ou vide, auquel cas l' action est appliquée à chaque ligne d'entrée). Donc par exemple

sed '1,4d' file

supprimera les lignes 1 à 4 (l' adresse est la plage de numéros de ligne 1,4et l' action est la dcommande de suppression); et

sed '/ick/,$s/foo/bar/' file

remplacera la première occurrence de foopar barsur n'importe quelle ligne entre la première correspondance sur l'expression régulière icket la fin du fichier (l' adresse est la plage /ick/,$et l' action est la scommande de substitutions/foo/bar/ ).

Dans ce contexte, s'il ickprovenait d'une variable, vous pourriez faire

sed "/$variable/,\$s/foo/bar/"

(notez l'utilisation de guillemets doubles au lieu de simples, afin que le shell puisse interpoler la variable, et la nécessité de citer le signe dollar littéral entre guillemets doubles) mais si la variable contient une barre oblique, vous obtiendrez une erreur de syntaxe. (Le shell développe la variable, puis passe la chaîne résultante à sed; donc sedne voit que le texte littéral - il n'a pas de concept des variables du shell.)

Le remède consiste à utiliser un délimiteur différent (où vous devez évidemment pouvoir utiliser un caractère qui ne peut pas apparaître dans la valeur de la variable), mais contrairement au s%foo%bar%cas, vous avez également besoin d'une barre oblique inverse avant le délimiteur si vous souhaitez utiliser un autre délimiteur que la valeur par défaut /:

sed "\\%$variable%,\$s/foo/bar/" file

(entre guillemets simples, une seule barre oblique inverse suffirait évidemment); ou vous pouvez échapper séparément chaque barre oblique de la valeur. Cette syntaxe particulière est uniquement Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

ou si vous utilisez un autre shell, essayez

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Pour plus de clarté, si $variablela chaîne contenait la chaîne, 1/2les commandes ci-dessus seraient équivalentes à

sed '\%1/2%,$s/foo/bar/' file

dans le premier cas, et

sed '/1\/2/,$s/foo/bar/' file

dans la seconde.

tripleee
la source
4

Utilisez Perl, où les variables sont des citoyens de première classe, pas seulement des macros en expansion:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p lit l'entrée ligne par ligne et imprime la ligne après traitement
  • \Qcite tous les métacaractères dans la chaîne suivante (non nécessaire pour la valeur présentée ici, mais nécessaire si la valeur contenue [ou certaines autres valeurs spéciales pour les expressions régulières)
choroba
la source
La seule réponse généralement utile à des dizaines de questions sur «l'utilisation de variables dans l'expression sed» dans Stack Exchange. Tout le reste est effectivement "échapper à tout ce qui est spécial à sed", ce qui est pratiquement inutile étant donné la variabilité des variables et de sed.
Heath Raftery