Como posso usar variáveis ​​no LHS e RHS de uma substituição sed?

61

Eu quero fazer:

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

no meu programa.

Mas eu quero usar variáveis, por exemplo

old_run='old_name_952'
new_run='old_name_953'

Eu tentei usá-los, mas a substituição não acontece (sem erro). Eu tentei:

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
fonte
2
Você pode encontrar a resposta em Usar um parâmetro em um argumento de comando .
manatwork 25/03

Respostas:

45

Você poderia fazer:

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

Mas cuidado, isso $old_runseria considerado uma expressão regular e, portanto, qualquer caractere especial que a variável contenha, como /ou .teria que ser escapado . Da mesma forma, em $new_run, os caracteres &e \precisariam ser tratados especialmente e você teria que escapar dos /caracteres e nova linha.

Se o conteúdo $old_runou $new_runnão estiver sob seu controle, é essencial executar essa fuga ou o código equivale a uma vulnerabilidade de injeção de código .

Stéphane Chazelas
fonte
3
Eu estava usando aspas simples em parâmetros par, mudei para aspas duplas e funcionou arquivo. Impressionante.
Aakash 28/07
não é que por sedsi só não use regex, mas sed -Eusaria?
kiwy
@Kiwy, não. -eé especificar um sed e xpression , para que possa passar mais de um (como em sed -e exp1 -e exp2) ou combinações de arquivos e expressões ( sed -f file -e exp). Você pode estar confuso com o -Eque, com algumas implementações, diz ao sed para usar EREs em vez de BREs.
Stéphane Chazelas
Eu digitei errado, mas tudo bem, isso explica por que tenho tantos problemas usando o sed sem que -Eeu domine apenas o regex estendido, não o regular. Obrigado pela explicação.
Kiwy
@Kiwy, consulte também as perguntas e respostas vinculadas para obter mais opções suportadas por algumas sedimplementações para obter ainda mais sintaxes de expressão regular.
Stéphane Chazelas
31

Isso funcionou:

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

Como eu quero 'reutilizar' o mesmo arquivo, eu realmente o uso para qualquer pessoa que deseje uma abordagem semelhante:

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

O exemplo acima criou um novo arquivo e exclui o arquivo atual e renomeia o novo arquivo para o arquivo atual.

Michael Durrant
fonte
4
Você não precisa de gato, mv ou extra '', a menos que tenha caracteres especiais neles. Então é isso sed -i s/"$old"/"$new"/ update_via_sed.sh. No entanto, esteja ciente de que isso não funciona para nenhum valor do antigo e do novo. por exemplo, ele não deve conter / etc., pois $ old ainda é uma pesquisa e $ new um padrão de substituição, onde alguns caracteres têm significados especiais.
22133 frostschutz
11
sed -i "s/$old/$new/"também está ok (com as precauções acima). sednormalmente considera o separador o primeiro caractere após o scomando - ou seja, se suas variáveis ​​contêm barras, /mas não digamos em ( @), você pode usar "s @ $ old @ $ new @".
Peterph
22

Você pode usar variáveis ​​no sed desde que entre aspas duplas (não aspas simples):

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

Se você tiver uma barra ( /) na variável, use um separador diferente, como abaixo

sed "s|$var|r_str|g" file_name >new_file
mani
fonte
11
+1 por mencionar a necessidade de usar aspas duplas. Esse foi o meu problema.
Speckledcarp
11
Exatamente o que eu estava procurando, incluindo o uso de |como no meu caso, as variáveis contém um caminho para que ele tenha /nele
Yorch
Por algumas razões, o pipe |funcionou para mim no MacOS 10.14, mas outros separadores gostam @ou #não. Eu também tinha barras no meu env var.
Davidenke 22/1018
21

'in-place' sed (usando a bandeira -i) foi a resposta. Graças a Peter.

sed -i "s@$old@$new@" file
Michael Durrant
fonte
2
Você tem as aspas no lugar errado. aspas devem estar em torno de variáveis, não s@(o que não é especial para o shell de forma alguma). O melhor é colocar tudo dentro de aspas, como sed -i "s@$old@$new@" file. Observe que -inão é uma sedopção padrão .
Stéphane Chazelas 26/03
como posso escapar $neste comando. Como, eu vou mudar $xxxcomo um todo para o valor de uma variável $JAVA_HOME. Eu tentei sed -i "s@\$xxx@$JAVA_HOME@" file, mas erro dizno previous regular expression
hakunami
3

man bash dá isso sobre aspas simples

Incluir caracteres entre aspas simples preserva o valor literal de cada caractere dentro das aspas. Uma aspas simples pode não ocorrer entre aspas simples, mesmo quando precedida por uma barra invertida.

Tudo o que você digita na linha de comando, o bash o interpreta e, em seguida, envia o resultado ao programa para o qual ele deve ser enviado.Neste caso, se você usar sed 's/$old_run/$new_run/', o bash primeiro vê o sed, o reconhece como um executável presente na $PATHvariável . O sedexecutável requer uma entrada. Bash procura a entrada e encontra 's/$old_run/$new_run/'. Aspas simples dizem ao bash para não interpretar o conteúdo nelas e passá-las como estão. Então, o bash os passa para o sed. Sed dá um erro porque $pode ocorrer apenas no final da linha.

Em vez disso, se usarmos aspas duplas, isto é, o "s/$old_run/$new_run/"bash vê isso e interpreta $old_runcomo um nome de variável e faz uma substituição (essa fase é chamada de expansão variável). Isso é realmente o que exigimos.

Porém, você deve ter cuidado ao usar aspas duplas, pois elas são interpretadas primeiro pelo bash e depois fornecidas ao sed. Portanto, alguns símbolos como `devem ser escapados antes de serem usados.

nitishch
fonte
1

Em risco de ser irreverente - você já considerou perl.

O que - entre outras coisas - é bastante bom para executar operações 'estilo sed', mas também uma coisa de programação com mais recursos.

Ele olha como no seu exemplo, o que você está realmente tentando fazer é incrementar um número.

Então, que tal:

#!/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 como um estilo único:

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

Em $d1Estou armazenar valores

Como não posso substituir o valor da variável, tentei o seguinte comando: ele está funcionando

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

faltando aspas duplas "${d1}", esse foi o erro

deva
fonte
-2

Assumido $old_rune $new_runjá está definido:

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

Isso mostra a alteração na tela. Você pode redirecionar a saída para o arquivo, se necessário.

Orlando
fonte
-2

Para usar uma variável no sed, use aspas duplas "", para incluir a variável no padrão que você está usando. Por exemplo: a = "Oi" Se você quiser acrescentar a variável 'a' a um padrão, use o seguinte: sed -n "1, / padrão" $ {a} "padrão / p" nome do arquivo

Sher
fonte
2
A expansão de $anão é citada aqui, o que significa que qualquer espaço em seu valor invocará a divisão de palavras no shell.
Kusalananda
-2

Você também pode usar Perl:

perl -pi -e ""s/$val1/$val2/g"" file
Samba
fonte
Considere variáveis ​​que podem ter espaços em seus valores. As cadeias vazias ao redor da expressão Perl ( "") não farão nada.
Kusalananda
-2

quebrar a corda

bad => linux veja uma string $ old_run:

sed 's/$old_run/$new_run/'

good => linux veja uma variável $ old_run:

sed 's/'$old_run'/'$new_run'/'
marcdahan
fonte
11
E se $old_runou $new_runcontém espaços?
Kusalananda