Como passar uma variável contendo barras para o sed

100

Como você passa uma variável contendo barras como um padrão para sed?

Por exemplo, se eu tiver a seguinte variável:

var="/Users/Documents/name/file"

Eu quero passar para sed:

sed "s/$var/replace/g" "$file"

No entanto, recebo erros. Como posso contornar o problema?

buydadip
fonte

Respostas:

193

Use um delimitador regex alternativo, pois sedpermite que você use qualquer delimitador ( incluindo caracteres de controle ):

sed "s~$var~replace~g" $file
anubhava
fonte
2
Não funciona. Eu tento sed -i "s ~ blah ~ $ var ~ g file" e obtenho: "sed -e expression # 1, char 70: unterminated` s 'command ". Eu confirmei que o culpado é uma barra em $ var. Variantes dessa solução estão por toda parte e nenhuma delas funciona. O sed foi atualizado recentemente? Estou na v4.2.2 no Ubuntu 14.04.4 LTS.
Paul Ericson
Isso depende do valor de$var
anubhava 01 de
1
Não funciona com ~ como @PaulEricson observou, mas ainda funciona com |
Kenny Ho
1
O último "~" (após g) parece não ser necessário, pelo menos para AWS Amazon Linux (baseado em CentOS)
Saúl Martínez Vidals
1
Você salvou meu dia
usuário7131571
62

Uma resposta puramente bash: use a expansão do parâmetro para fazer o escape de qualquer barra invertida na variável:

var="/Users/Documents/name/file"
sed "s/${var//\//\\/}/replace/g" $file
glenn jackman
fonte
1
Essa é uma boa maneira, porque você nem sempre sabe quais caracteres contém a variável. Portanto, você sempre pode escapar do delimitador sed se houver uma dúvida. Por exemplo: sed "s: $ {var //: / \\:}: replace: g" $ file
Stephane L
Lindo. Tornei alguns scripts de renomeação meus muito mais claros.
Nuvem
Aprendi algo lindo hoje, obrigado. Deve ser a resposta aceita.
Steve Kehlet
6
Alguma clareza sobre o ser padrão usado aqui: ${parameter/pattern/string}. Portanto, neste caso, o parâmetro é var, o padrão é /\/e a string é \\/. Todas as instâncias do padrão são substituídas porque o padrão começa com a /.
Josh
1
@josh e, portanto, a barra inicial do padrão não faz parte do padrão a ser substituído.
glenn jackman
32

Outra maneira de fazer isso, embora mais feia do que a resposta de anubhava, é escapar de todas as barras invertidas em var usando outro sedcomando:

var=$(echo "$var" | sed 's/\//\\\//g')

então, isso vai funcionar:

sed "s/$var/replace/g" $file
buydadip
fonte
12

Usar /em sed como um delimitador entrará em conflito com as barras na variável quando substituída e, como resultado, você obterá um erro. Uma maneira de contornar isso é usar outro delimitador que seja exclusivo de todos os caracteres dessa variável.

var="/Users/Documents/name/file"

você pode usar o caractere octotorpe que se adapte à ocasião (ou qualquer outro caractere que não seja /para facilidade de uso)

sed "s#$var#replace#g" 

ou

sed 's#$'$var'#replace#g'

isto é adequado quando a variável não contém espaços

ou

sed 's#$"'$var'"#replace#g'

É mais sensato usar o acima, uma vez que estamos interessados ​​em substituir o que quer que esteja nessa variável apenas em comparação com aspas duplas de todo o comando, o que pode fazer com que seu shell interprete qualquer caractere que possa ser considerado um caractere especial de shell para o shell.

repzero
fonte
Não funciona. Eu tento sed -i "s ~ blah ~ $ var ~ g file" e obtenho: "sed -e expression # 1, char 70: unterminated` s 'command ". Eu confirmei que o culpado é uma barra em $ var. Variantes dessa solução estão por toda parte e nenhuma delas funciona. O sed foi atualizado recentemente? Estou na v4.2.2 no Ubuntu 14.04.4 LTS.
Paul Ericson
@PaulEricson Não consigo reproduzir isso em nenhum Ubuntu disponível para mim. Se o seu $varcontém ~, obviamente você precisará escolher um delimitador diferente. O erro "não terminado" parece que $varcontém uma nova linha; você pode consertá-lo fazendo escape de barra invertida a cada nova linha no valor, mas não acho que isso seja totalmente portátil. Em geral, isso não está relacionado ao problema de barras no valor.
tripleee
@PaulEricson Ah, e sua citação está incorreta, você não deve colocar o nome do arquivo entre aspas. sed -i "s~blah~$var~g" filecom aspas apenas em torno do sedscript real .
tripleee
5

Esta é uma pergunta antiga, mas nenhuma das respostas aqui discute as operações, a não ser s/from/to/com muitos detalhes.

A forma geral de uma seddeclaração é

*address* *action*

onde o endereço pode ser um intervalo regex ou um intervalo de número de linha (ou vazio, caso em que a ação é aplicada a cada linha de entrada). Então por exemplo

sed '1,4d' file

irá deletar as linhas 1 a 4 (o endereço é o intervalo do número da linha 1,4e a ação é o dcomando deletar); e

sed '/ick/,$s/foo/bar/' file

irá substituir a primeira ocorrência de foopor barem qualquer linha entre a primeira correspondência na regex icke o final do arquivo (o endereço é o intervalo /ick/,$e a ação é o scomando substituto s/foo/bar/).

Neste contexto, se ickveio de uma variável, você poderia fazer

sed "/$variable/,\$s/foo/bar/"

(observe o uso de aspas duplas em vez de simples, para que o shell possa interpolar a variável e a necessidade de colocar o cifrão literal entre aspas duplas), mas se a variável contiver uma barra, você obterá um erro de sintaxe. (O shell expande a variável e, em seguida, passa a string resultante para sed; portanto, sedvê apenas o texto literal - não tem nenhum conceito das variáveis ​​do shell.)

A solução é usar um delimitador diferente (onde obviamente você precisa ser capaz de usar um caractere que não pode ocorrer no valor da variável), mas ao contrário do s%foo%bar%caso, você também precisa de uma barra invertida antes do delimitador se quiser usar um delimitador do que o padrão /:

sed "\\%$variable%,\$s/foo/bar/" file

(entre aspas simples, uma única barra invertida seria obviamente suficiente); ou você pode escapar separadamente de cada barra no valor. Esta sintaxe específica é apenas Bash:

sed "/${variable//\//\\/}/,\$s/foo/bar/" file

ou se você usar um shell diferente, tente

escaped=$(echo "$variable" | sed 's%/%\\/%g')
sed "s/$escaped/,\$s/foo/bar/" file

Para maior clareza, se $variablecontiver a string 1/2, os comandos acima seriam equivalentes a

sed '\%1/2%,$s/foo/bar/' file

no primeiro caso, e

sed '/1\/2/,$s/foo/bar/' file

no segundo.

triplo
fonte
4

Use Perl, onde as variáveis ​​são cidadãos de primeira classe, não apenas macros de expansão:

var=/Users/Documents/name/file perl -pe 's/\Q$ENV{var}/replace/g' $file
  • -p lê a entrada linha por linha e imprime a linha após o processamento
  • \Qcita todos os metacaracteres na seguinte string (não necessária para o valor apresentado aqui, mas necessária se o valor contiver [ou alguns outros valores especiais para expressões regulares)
choroba
fonte
A única resposta geralmente útil para dezenas de perguntas "usando variáveis ​​na expressão sed" no Stack Exchange. Todo o resto é efetivamente "escapar de qualquer coisa que seja especial para sed", o que é praticamente inútil dada a variabilidade das variáveis ​​e do sed.
Heath Raftery