Usando uma referência a uma variável de string bash em sed

2

Sou um novato relativo no Linux. Suponha que eu tenha um arquivo de texto a.txt que contenha o seguinte texto:

A
B
C

Agora, se eu quiser alterar o texto na linha 2 (que contém B ), posso usar o seguinte comando sed :

sed -i '2s/^.*$/X/' a.txt

para alterar o B para X , por exemplo.

Mas agora suponha que eu queira escrever um comando sed que altere o texto na linha 2 para algo que inclua uma variável de sequência bash. Eu quero mudar o texto na linha 2 para fname="myaddress" , onde as aspas são incluídas explicitamente, e onde myaddress é uma variável bash (definida em outro lugar) ). Para tentar fazer isso, criei um script de shell bash chamado mytest.sh e contendo o seguinte:

#!/bin/bash

myaddress="/home/"
echo fname=\"$myaddress\"

head a.txt
sed -i '2s/^.*$/fname=\"$myaddress\"/' a.txt
head a.txt

Agora, echo fname=\"$myaddress\" imprime fname="/home/" , que é o texto que quero , para a linha 2 de a.txt . No entanto, sed imprime fname="$myaddress" para a linha 2 de a.txt , provavelmente porque está dentro dos apóstrofos adjacentes. Em outras palavras, a saída do script de shell bash acima é:

fname="/home/"
A
B
C

A
fname="$myaddress"
C

Minha pergunta é: como posso obter sed para realmente imprimir o valor armazenado em myaddress e referenciado por $myaddress ?

    
por Andrew 19.08.2013 / 22:56

3 respostas

1

Existem dois problemas: Primeiro, como já foi observado por Kevin, as variáveis do shell precisam ser colocadas entre aspas duplas, se quiserem ser expandidas pelo shell. Segundo, a string de substituição contém um / , portanto, você não pode usar / ao mesmo tempo que um delimitador para sed . Se você souber que algum caractere, digamos , , definitivamente não ocorrerá na string de substituição, você poderia usar esse caractere como sed delimiter, para obter

sed -i '2s,^.*$,fname="'"$myaddress"'",' a.txt
    
por 19.08.2013 / 23:28
5

Se você deseja que a substituição funcione, qualquer que seja o conteúdo da variável, é necessário excluir os caracteres da variável que são especiais no lado direito de um comando s em sed , que são:

  • o próprio delimitador (acima, você usou / )
  • & , que é substituído pela string correspondente
  • nova linha que precisa ser escapada
  • \ , que é usado para escapar de todos os itens acima e introduzir , ...

Como:

escaped_var=$(printf '%s\n' "$var" | sed 's:[/&\]:\&:g;s/$/\/')
escaped_var=${escaped_var%?}

sed "s/regexp/$escaped_var/g"

Como isso é um pouco complicado, você pode usar perl :

v=$var perl -pe 's/regexp/$ENV{v}/g'

O equivalente a sed ' 2s seria:

v=$var perl -pe 's/regexp/$ENV{v}/g if $. == 2'

Embora no seu caso, é claro, você prefere escrevê-lo:

v=$myaddress perl -pe '$_ = "fname=\"$ENV{v}\"\n" if $. == 2'

A outra vantagem é que ele também funciona com dados binários e você pode usar a opção -i para edição no local enquanto com sed , você só pode fazer isso em sistemas onde sed é o GNU sed .

Observe também que o caractere NUL não pode ser armazenado em uma variável de ambiente.

    
por 20.08.2013 / 13:00
2

O shell não interpreta variáveis entre aspas simples. Você precisa alterá-los para aspas duplas. Como sua variável possui barras, você deve usar um delimitador diferente:

sed -i "2s|^.*\$|fname=\"$myaddress\"|" a.txt

Observe que escapei do% de fim de linha$, de modo que ele não seja interpretado pelo shell. Nesse caso, não seria, mas você deve ter o hábito de escapar $ s que deseja manter entre aspas duplas. Você também pode fazer isso usando alguns pares de citações:

sed -i '2s|^.*$|fname="'"$myaddress"'"|' a.txt
    
por 19.08.2013 / 23:02