Substituição da variável de ambiente no sed


202

Se eu executar esses comandos a partir de um script:

#my.sh
PWD=bla
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
xxx
bla

está bem.

Mas, se eu correr:

#my.sh
sed 's/xxx/'$PWD'/'
...
$ ./my.sh
$ sed: -e expression #1, char 8: Unknown option to `s' 

Eu li em tutoriais que para substituir variáveis ​​de ambiente do shell, é necessário parar e 'citar' a $varnameparte para que não seja substituída diretamente, o que eu fiz e que funcionará apenas se a variável for definida imediatamente antes.

Como posso o sed reconhecer uma $varvariável de ambiente conforme definida no shell?


4
$ PWD contém um / que está finalizando o comando substitute.
— Derobert

@derobert: tnx. Uma das soluções aborda esta ...
— RomanM

4
Use set -xno shell para fazer com que o shell ecoe cada comando antes de executá-los. Isso pode esclarecer muita confusão. (Além disso, muitas vezes eu uso set -upara fazer de-referenciando variáveis unset um erro de disco (Veja. set -eTambém).)
— bobbogo

Eu esperava encontrar uma maneira de o sed manipular as variáveis ​​de ambiente para não vazar os valores na tabela de processos, parece que o sed é a ferramenta errada para instalar segredos de acordo com todas as respostas neste tópico
— ThorSummoner

Respostas:


302

Seus dois exemplos parecem idênticos, o que dificulta o diagnóstico de problemas. Problemas potenciais:

  1. Você pode precisar de aspas duplas, como em sed 's/xxx/'"$PWD"'/'

  2. $PWDpode conter uma barra, caso em que você precisa encontrar um caractere não contido $PWDpara usar como delimitador.

Para resolver os dois problemas ao mesmo tempo, talvez

sed 's@xxx@'"$PWD"'@'

mas, qual caractere posso usar, sei que com certeza não aparecerá no nome de um caminho?
— 257 RomanM

5
Você pode ter vários candidatos como @ #%! e verifique com uma expressão de caso para descobrir se $ PWD os possui. Por exemplo, caso "$ PWD" de @ ) ;; *) delim = "@" ;; esac; repita até que $ delim não esteja vazio.
— Norman Ramsey

Há outra alternativa em vez de usar aspas duplas. Veja minha resposta abaixo.
— Thales Ceolin

Você pode usar outro delimitador para sed. Você não precisa usar / pode usar também se a sua variável de ambiente for um URL.
— Guchelkaben

123

Além da resposta de Norman Ramsey, gostaria de acrescentar que você pode citar duas vezes a sequência inteira (o que pode tornar a declaração mais legível e menos propensa a erros).

Portanto, se você quiser procurar por 'foo' e substituí-lo pelo conteúdo de $ BAR, poderá colocar o comando sed entre aspas duplas.

sed 's/foo/$BAR/g'
sed "s/foo/$BAR/g"

No primeiro, $ BAR não será expandido corretamente, enquanto no segundo $ BAR será expandido corretamente.


4
Isso é mais limpo do que mexer com aspas duplas, aspas simples etc.
— Vladislavs Dovgalecs

é isso que eu tinha que usar para obter a variável de ambiente para expandir corretamente neste comando: -i "/ 127.0.0.1 / 127.0.0.1 localhost $ HOSTNAME s /" hosts sed
— izikandrw

2
Isso é legal, mas não funciona quando você deseja fazer substituições mais complexas, como "2,$s/^/$foo/"as $sinterpretadas como variáveis ​​também e não deve.
— Mjp

59

Você pode usar outros caracteres além de "/" em substituição:

sed "s#$1#$2#g" -i FILE

No meu caso específico, $ 2 era um caminho de arquivo, assim sedcomo vomitar devido à interpretação /do conteúdo de $ 2. Era exatamente isso que eu precisava para superar isso. Obrigado por uma ótima dica!
— precisa saber é o seguinte

54

Outra alternativa fácil:

Como $PWDgeralmente contém uma barra /, use em |vez de /para a instrução sed:

sed -e "s|xxx|$PWD|"

4
Você disse "uma alternativa ao uso de aspas duplas" e, no entanto, seu exemplo usa aspas duplas?
— Jeach

Eu acho que o ponto é que ele não usa aspas duplas diretamente em torno de $PWD...?
— Christian

14

Com a sua edição de perguntas, vejo seu problema. Digamos que o diretório atual seja /home/yourname ... neste caso, seu comando abaixo:

sed 's/xxx/'$PWD'/'

será expandido para

sed `s/xxx//home/yourname//

o que não é válido. Você precisa colocar um \personagem na frente de cada um /no seu $ PWD, se quiser fazer isso.


mas PWD é definido pelo shell ... se eu vou echo $ PWD i obter o pwd
— RomanM

1
Então, como você escapa das variáveis ​​de ambiente?
— einpoklum

1
A resposta selecionada descreve uma solução ... não use barra para o delimitador
— Eddie

O que se tornará um problema assim que seu diretório de trabalho atual contiver esse outro caractere.
— blubberdiblub

6

mau caminho: alterar delimitador

sed 's/xxx/'"$PWD"'/'
sed 's:xxx:'"$PWD"':'
sed 's@xxx@'"$PWD"'@'

talvez essa não seja a resposta final,

você não pode saber em que caractere ocorrerá $PWD, / :OU @.

o bom caminho é substituir o caractere especial $PWD.

bom caminho: delimitador de escape

por exemplo: tente substituir URLcomo $ url (tem : /conteúdo)

x.com:80/aa/bb/aa.js

na string $ tmp

<a href="URL">URL</a>

uma. use /como delimitador

echo ${url//\//\\/}
x.com:80\/aa\/bb\/aa.js

echo ${url//\//\/}
x.com:80/aa/bb/aa.js

echo "${url//\//\/}"
x.com:80\/aa\/bb\/aa.js

echo $tmp | sed "s/URL/${url//\//\\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

echo $tmp | sed "s/URL/${url//\//\/}/"
<a href="x.com:80/aa/bb/aa.js">URL</a>

OU

b. use :como delimitador (mais legível que /)

echo ${url//:/\:}
x.com:80/aa/bb/aa.js

echo "${url//:/\:}"
x.com\:80/aa/bb/aa.js

echo $tmp | sed "s:URL:${url//:/\:}:g"
<a href="x.com:80/aa/bb/aa.js">x.com:80/aa/bb/aa.js</a>

3

Na verdade, a coisa mais simples (pelo menos no gnu sed) é usar um separador diferente para o comando sed substitution (s). Portanto, em vez de s / pattern / '$ mypath' / ser expandido para s / pattern // my / path / que obviamente confundirá o comando s, use s! Pattern! '$ Mypath'! que será expandido para s! pattern! / my / path! Eu usei o caractere bang (!) (Ou use o que você quiser) que evita a barra dianteira usual, mas por nenhum meio, sua única escolha como separador.


3
VAR=8675309
echo "abcde:jhdfj$jhbsfiy/.hghi$jh:12345:dgve::" |\
sed 's/:[0-9]*:/:'$VAR':/1' 

onde VAR contém o que você deseja substituir o campo


3

Lidando com VARIÁVEIS dentro do sed

[root@gislab00207 ldom]# echo domainname: None > /tmp/1.txt

[root@gislab00207 ldom]# cat /tmp/1.txt

domainname: None

[root@gislab00207 ldom]# echo ${DOMAIN_NAME}

dcsw-79-98vm.us.oracle.com

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: ${DOMAIN_NAME}/g'

 --- Below is the result -- very funny.

domainname: ${DOMAIN_NAME}

 --- You need to single quote your variable like this ... 

[root@gislab00207 ldom]# cat /tmp/1.txt | sed -e 's/domainname: None/domainname: '${DOMAIN_NAME}'/g'


--- The right result is below 

domainname: dcsw-79-98vm.us.oracle.com

Eu estava lutando para fazer isso funcionar nos pipelines do Azure DevOps YAML. Este comentário me ajudou a usar com sucesso esse truque para tokenizar alguns arquivos de configuração.
— andov 18/02

1

Eu tive um problema semelhante, eu tinha uma lista e preciso criar um script SQL com base no modelo (que continha @INPUT@como elemento a ser substituído):

for i in LIST 
do
    awk "sub(/\@INPUT\@/,\"${i}\");" template.sql >> output
done
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.