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?

RomanM
fonte
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"'@'
Norman Ramsey
fonte
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.

Jeach
fonte
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
Paulo Fidalgo
fonte
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|"
Thales Ceolin
fonte
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.

Eddie
fonte
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>
yurenchen
fonte
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.

adeger
fonte
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

PsychoData
fonte
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
Mamadou Lamine Diatta
fonte
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
aniskop
fonte