Come posso usare le variabili in LHS e RHS di una sostituzione sed?


61

Voglio fare:

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

nel mio programma.

Ma voglio usare le variabili, ad es

old_run='old_name_952'
new_run='old_name_953'

Ho provato a usarli ma la sostituzione non avviene (nessun errore). Ho provato:

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}/'

2
Puoi trovare la risposta in Usa un parametro in un argomento di comando .
arte

Risposte:


45

Potresti fare:

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

Ma attenzione che $old_runverrebbe preso come un'espressione regolare e quindi qualsiasi carattere speciale che contiene la variabile, come /o .dovrebbe essere evaso . Allo stesso modo, in $new_run, i personaggi &e \dovrebbero essere trattati in modo speciale e dovresti sfuggire ai /personaggi e newline in esso.

Se il contenuto di $old_runo $new_runnon è sotto il tuo controllo, è fondamentale eseguire tale escape o altrimenti tale codice equivale a una vulnerabilità di iniezione di codice .


3
Stavo usando virgolette singole in sed params, sono passato a virgolette doppie e ha funzionato file. Eccezionale.
Aakash,

non è che sedda solo non userà regex ma lo sed -Efarebbe?
Kiwy,

@Kiwy, no. -eè specificare una sed e xpression , in modo da poter passare più di una (come in sed -e exp1 -e exp2) o combinazioni di file ed espressioni ( sed -f file -e exp). Potresti essere confuso con -Equale con alcune implementazioni, dice a sed di usare ERE invece di BRE.
Stéphane Chazelas,

Ho sbagliato a scrivere, ma OK, questo spiega perché ho così tanti problemi con sed senza che -Eio solo padroneggi il regex esteso non quello normale. Grazie per l'esplosione.
Kiwy,

@Kiwy, vedi anche le domande e risposte collegate per ulteriori opzioni supportate da alcune sedimplementazioni per sintassi di espressioni ancora più regolari.
Stéphane Chazelas,

31

Questo ha funzionato:

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

Dato che voglio "riutilizzare" lo stesso file, lo uso effettivamente per chiunque desideri un approccio simile:

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

Quanto sopra ha creato un nuovo file quindi elimina il file corrente anziché rinominare il nuovo file come file corrente.


4
Non hai bisogno di cat, mv o extra '' a meno che tu non abbia personaggi speciali. Quindi lo è sed -i s/"$old"/"$new"/ update_via_sed.sh. Tuttavia, tenere presente che questo non funziona per alcun valore di vecchio e nuovo. ad esempio, non deve contenere / ecc., poiché $ old è ancora una ricerca e $ new un modello sostitutivo in cui alcuni caratteri hanno significati speciali.
frostschutz,

1
sed -i "s/$old/$new/"va bene anche (con le precauzioni sopra). seddi solito considera il separatore come il primo carattere dopo il scomando - cioè se le variabili contengono barre /ma non diciamo a ( @), puoi usare "s @ $ old @ $ new @".
peterph

22

Puoi usare le variabili in sed purché sia ​​tra virgolette doppie (non tra virgolette singole):

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

Se hai una barra ( /) nella variabile, usa un separatore diverso come sotto

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

1
+1 per menzionare la necessità di utilizzare virgolette doppie. Questo era il mio problema.
speckledcarp

1
Esattamente quello che stavo cercando, incluso l'uso di |come nel mio caso, le variabili contengono un percorso che ha /in sé
yorch

Per alcuni motivi la pipa ha |funzionato per me su MacOS 10.14, ma altri separatori piacciono @o #no. Avevo anche delle barre rovesciate nel mio ambiente.
davidenke,

21

'sed sul posto' (usando la bandiera -i) era la risposta. Grazie a Peter.

sed -i "s@$old@$new@" file

2
Hai le virgolette nel posto sbagliato. le virgolette devono essere intorno alle variabili, non s@(che non è speciale per la shell in alcun modo). La cosa migliore è mettere tutto tra virgolette sed -i "s@$old@$new@" file. Nota che -inon è sedun'opzione standard .
Stéphane Chazelas,

come posso fuggire $in questo comando. Ad esempio, cambierò $xxxnel suo insieme al valore di una variabile $JAVA_HOME. Ho provato sed -i "s@\$xxx@$JAVA_HOME@" file, ma l'errore diceno previous regular expression
hakunami il

3

man bash dà questo su virgolette singole

Racchiudere i caratteri tra virgolette singole conserva il valore letterale di ciascun carattere tra virgolette. Tra virgolette singole non può verificarsi una virgoletta, anche se preceduta da una barra rovesciata.

Qualunque cosa digiti sulla riga di comando, bash lo interpreta e quindi invia il risultato al programma a cui dovrebbe essere inviato.In questo caso, se lo usi sed 's/$old_run/$new_run/', bash prima vede il sed, lo riconosce come un eseguibile presente in $PATHvariabile . L' sedeseguibile richiede un input. Bash cerca l'input e trova 's/$old_run/$new_run/'. Le virgolette singole dicono bash di non interpretare il contenuto in esse e passarle così come sono. Quindi, bash quindi li passa a sed. Sed dà un errore perché $può verificarsi solo alla fine della riga.

Invece se usiamo le virgolette doppie, cioè, "s/$old_run/$new_run/"quindi bash vede questo e interpreta $old_runcome un nome di variabile e fa una sostituzione (questa fase è chiamata espansione variabile). Questo è davvero ciò che abbiamo richiesto.

Ma devi fare attenzione usando le doppie virgolette perché, prima vengono interpretate da bash e poi date a sed. Quindi, alcuni simboli come `devono essere sfuggiti prima di usarli.


1

A rischio di essere irriverente - hai preso in considerazione perl.

Che - tra le altre cose - è abbastanza bravo a fare operazioni 'sed style', ma anche un programma di programmazione più completo.

Essa appare come nel tuo esempio, cosa si sta realmente cercando di fare è incrementare un numero.

Che ne dici di:

#!/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

o come una fodera - stile sed:

perl -pe 's/old_name_(\d+)/"old_name_".($1+1)/e'

1
$ echo $d1 | sed -i 's/zkserver1/'${d1}'/g' deva_file
sed: -e expression #1, char 26: unterminated `s' command 

In $d1Sto memorizzando valori

Non riesco a sostituire il valore dalla variabile, quindi ho provato il seguente comando che sta funzionando

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

mancano le virgolette doppie in "${d1}", quello era l'errore


-2

Presupposto $old_rune $new_runsono già impostati:

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

Questo mostra la modifica sullo schermo. È possibile reindirizzare l'output su file se necessario.


-2

Per usare una variabile in sed, usa le virgolette doppie "", per racchiudere la variabile, nel modello che stai usando. Ad esempio: a = "Ciao" Se si desidera aggiungere la variabile 'a' a un modello, è possibile utilizzare il seguente: sed -n "1, / pattern" $ {a} "pattern / p" nomefile


2
L'espansione di $anon è quotata qui, il che significa che qualsiasi spazio nel suo valore invocherà la divisione delle parole nella shell.
Kusalananda

-2

Puoi anche usare Perl:

perl -pi -e ""s/$val1/$val2/g"" file

Considera le variabili che possono avere spazi nei loro valori. Le stringhe vuote attorno all'espressione Perl ( "") non faranno nulla.
Kusalananda

-2

rompere la corda

bad => linux vede una stringa $ old_run:

sed 's/$old_run/$new_run/'

good => linux vede una variabile $ old_run:

sed 's/'$old_run'/'$new_run'/'

1
E se $old_runo $new_runcontiene spazi?
Kusalananda
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.