Comment utiliser les variables LHS et RHS d'une substitution sed?

61

Je veux faire:

cat update_via_sed.sh | sed 's/old_name/new_name/' > new_update_via_sed.sh

dans mon programme.

Mais je veux utiliser des variables, par exemple

old_run='old_name_952'
new_run='old_name_953'

J'ai essayé de les utiliser mais la substitution n'a pas lieu (pas d'erreur). J'ai essayé:

cat update_via_sed.sh | sed 's/old_run/new_run/'
cat update_via_sed.sh | sed 's/$old_run/$new_run/'
cat update_via_sed.sh | sed 's/${old_run}/${new_run}/'
Michael Durrant
la source
2
Vous pouvez trouver la réponse dans Utiliser un paramètre dans un argument de commande .
manatwork

Réponses:

45

Vous pourriez faire:

sed "s/$old_run/$new_run/" < infile > outfile

Mais attention, cela $old_runserait pris comme une expression régulière et donc comme tout caractère spécial contenu dans la variable, tel que /ou .devant être échappé . De même, dans $new_run, les caractères &et \doivent être traités de manière spécifique et vous devez y échapper /.

Si le contenu de $old_runou $new_runn'est pas sous votre contrôle, il est essentiel d'exécuter cette échappement, sinon le code équivaut à une vulnérabilité d'injection de code .

Stéphane Chazelas
la source
3
J'utilisais des guillemets simples dans les paramètres sed, je passais aux guillemets doubles et le fichier fonctionnait. Impressionnant.
Aakash
ce n'est pas que sedseul n'utilisera pas regex mais le sed -Eferait?
Kiwy
@ Kiwy, non. -eest de spécifier un sed e Xpression , de sorte que vous pouvez passer plus d'un (comme dans sed -e exp1 -e exp2) ou des combinaisons de fichiers et expressions ( sed -f file -e exp). Vous pouvez être déroutant avec -Equoi avec certaines implémentations, dit à sed d'utiliser des ERE au lieu de BRE.
Stéphane Chazelas
Je me suis trompé, mais OK, cela explique pourquoi j’ai tant de problèmes à utiliser sed sans que -Eje ne maîtrise que la regex étendue, pas la régulière. Merci pour l'explication
Kiwy
@ Kiwy, voir également les questions / réponses liées pour plus d'options prises en charge par certaines sedimplémentations pour des syntaxes d'expression encore plus régulières.
Stéphane Chazelas
31

Cela a fonctionné:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/'

Comme je veux «réutiliser» le même fichier, je l’utilise en fait pour tous ceux qui souhaitent une approche similaire:

cat update_via_sed.sh | sed 's/'"$old_run"'/'"$new_run"'/' > new_update; mv -f new_update update_via_sed.sh

Ce qui a été créé ci-dessus crée un nouveau fichier puis supprime le fichier en cours et le renomme en fichier.

Michael Durrant
la source
4
Vous n'avez pas besoin de chat, de mv ou d'extra '' à moins que vous n'ayez des caractères spéciaux. Donc c'est sed -i s/"$old"/"$new"/ update_via_sed.sh. Cependant, sachez que cela ne fonctionne pour aucune valeur d'ancien ou de nouveau. Par exemple, il ne doit pas contenir / etc., car $ old est toujours une recherche et $ new un modèle de remplacement dans lequel certains caractères ont une signification particulière.
Frostschutz
1
sed -i "s/$old/$new/"est ok aussi (avec les précautions ci-dessus). sedconsidère généralement que le séparateur est le premier caractère après la scommande - c'est-à-dire que si vos variables contiennent des barres obliques /mais ne permettent pas de dire at ( @), vous pouvez utiliser "s @ $ old @ $ new @".
Peter
22

Vous pouvez utiliser des variables dans sed tant qu'il s'agit d'un guillemet double (pas d'un guillemet simple):

sed "s/$var/r_str/g" file_name >new_file

Si vous avez une barre oblique ( /) dans la variable, utilisez un séparateur différent, comme ci-dessous

sed "s|$var|r_str|g" file_name >new_file
mani
la source
1
+1 pour avoir mentionné la nécessité d'utiliser des guillemets doubles. C'était mon problème.
speckledcarp
1
Exactement ce que je cherchais, y compris l'utilisation de |comme dans mon cas, les variables contient un chemin il a /en elle
Yorch
Pour certaines raisons, le tuyau a |fonctionné pour moi sur MacOS 10.14, mais d'autres séparateurs l'aiment @ou #non. J'avais aussi des barres obliques dans mon env var.
davidenke
21

'in-place' sed (en utilisant le drapeau -i) était la réponse. Merci à Peterph.

sed -i "s@$old@$new@" file
Michael Durrant
la source
2
Vous avez les citations au mauvais endroit. les guillemets doivent être autour de variables, pas s@(ce qui n’est en aucun cas spécial pour le shell). Le mieux est de tout mettre entre guillemets sed -i "s@$old@$new@" file. Notez que ce -in'est pas une sedoption standard .
Stéphane Chazelas
comment puis-je m'échapper $dans cette commande. Comme, je vais changer $xxxdans son ensemble à la valeur d'une variable $JAVA_HOME. J'ai essayé sed -i "s@\$xxx@$JAVA_HOME@" file, mais l'erreur ditno previous regular expression
hakunami
3

man bash donne ceci à propos d'une seule citation

Le fait de placer des caractères entre guillemets simples préserve la valeur littérale de chaque caractère entre guillemets. Un seul guillemet ne peut pas apparaître entre guillemets, même s'il est précédé d'une barre oblique inverse.

Peu importe ce que vous tapez sur la ligne de commande, bash l'interprète puis il envoie le résultat au programme auquel il est censé être envoyé. Dans ce cas, si vous utilisez sed 's/$old_run/$new_run/', bash le voit en premier sed, il le reconnaît comme un exécutable présent dans la $PATHvariable . L' sedexécutable nécessite une entrée. Bash cherche l'entrée et trouve 's/$old_run/$new_run/'. Les guillemets simples disent bash de ne pas interpréter le contenu qu'ils contiennent et de les transmettre tels quels. Alors, bash les transmet ensuite à sed. Sed donne une erreur car $peut se produire uniquement en fin de ligne.

Au lieu de cela, si nous utilisons des guillemets doubles, ie "s/$old_run/$new_run/",, alors bash le verra et interprétera $old_runcomme un nom de variable et effectuera une substitution (cette phase est appelée développement de variable). C'est bien ce dont nous avions besoin.

Mais vous devez faire attention en utilisant les guillemets doubles, car ils sont interprétés d’abord par bash, puis donnés à sed. Donc, certains symboles comme `doivent être évités avant de les utiliser.

nitishch
la source
1

Risque de désinvolture - avez-vous réfléchi perl?

Ce qui, entre autres choses, est assez efficace pour effectuer des opérations de type "sed", mais constitue également un programme de programmation plus complet.

Dans votre exemple, il semble que vous essayez réellement d’augmenter un nombre.

Alors que diriez-vous de:

#!/usr/bin/env perl
use strict;
use warnings 'all'; 

while ( <DATA> ) {
    s/old_name_(\d+)/"old_name_".($1+1)/e;
    print;
}

__DATA__
old_name_932

ou comme une ligne - style sed:

perl -pe 's/old_name_(\d+)/"old_name_".($1+1)/e'
Sobrique
la source
1
$ echo $d1 | sed -i 's/zkserver1/'${d1}'/g' deva_file
sed: -e expression #1, char 26: unterminated `s' command 

En $d1stockant des valeurs

Je ne peux pas remplacer la valeur de la variable, alors j'ai essayé la commande suivante, ça marche

echo $d1 | sed -i 's/zkserver1/'"${d1}"'/g' deva_file

guillemet double manquant "${d1}", c’est l’erreur

deva
la source
-2

Assumé $old_runet $new_runsont déjà mis:

cat update_via_sed.sh | eval $(print "sed 's/$old_run/$new_run/g'")

Cela montre le changement à l'écran. Vous pouvez rediriger la sortie vers un fichier si nécessaire.

Orlando
la source
-2

Pour utiliser une variable dans sed, utilisez des guillemets doubles "" pour inclure la variable dans le modèle que vous utilisez. Par exemple: a = "Hi" Si vous souhaitez ajouter la variable 'a' à un modèle, vous pouvez utiliser le code suivant: sed -n "1, / modèle" $ {a} "modèle / p" nom de fichier

Sher
la source
2
Le développement de $an'est pas cité ici, ce qui signifie que tous les espaces de sa valeur invoqueront la division du mot dans le shell.
Kusalananda
-2

Vous pouvez également utiliser Perl:

perl -pi -e ""s/$val1/$val2/g"" file
Samba
la source
Considérons les variables qui peuvent avoir des espaces dans leurs valeurs. Les chaînes vides autour de l'expression Perl ( "") ne feront rien.
Kusalananda
-2

rompre la ficelle

bad => linux voir une chaîne $ old_run:

sed 's/$old_run/$new_run/'

bon => linux voir une variable $ old_run:

sed 's/'$old_run'/'$new_run'/'
Marcdahan
la source
1
Et si $old_runou $new_runcontient des espaces?
Kusalananda