Como garantir que a string interpolada na substituição 'sed' escape de todos os metachars

17

Eu tenho um script que lê um fluxo de texto e gera um arquivo de comandos sed que é executado posteriormente com sed -f . Os comandos sed gerados são como:

s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g

Suponha que o script que gera os comandos sed seja algo como:

while read cid fileid
do
    cidpat="$(echo $cid | sed -e s/\./\\./g)"
    echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done

Como posso melhorar o script para garantir que todos os metacaracteres de expressão regular na string cid sejam escapados e interpolados corretamente?

    
por dan 12.05.2014 / 16:26

1 resposta

21

Para escapar de variáveis a serem usadas no lado esquerdo e no lado direito de um comando s em sed (aqui $lhs e $rhs respectivamente), você faria:

escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\/.^$*]:\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\/&]:\&:g;$!s/$/\/')

sed "s/$escaped_lhs/$escaped_rhs/"

Observe que $lhs não pode conter um caractere de nova linha.

Ou seja, no LHS, escape de todos os operadores regexp ( ][.^$* ), do próprio caractere de escape ( \ ) e do separador ( / ).

No RHS, você só precisa escapar de & , o separador, a barra invertida e o caractere de nova linha (o que você faz inserindo uma barra invertida no final de cada linha, exceto a última ( $!s/$/\/ )). / p>

Isso pressupõe que você use / como separador nos comandos sed s e que você não habilita REs estendidos com -r (GNU sed / ssed / ast / busybox sed ) ou -E (BSDs, ast , GNU recente, busybox recente) ou PCREs com -R ( ssed ) ou Aumentada REs com -A / -X ( ast ), todos com operadores RE extras.

Algumas regras básicas ao lidar com dados arbitrários:

  • Não use echo
  • cite suas variáveis
  • considere o impacto da localidade (especialmente seu conjunto de caracteres: é importante que os comandos escape sed sejam executados na mesma localidade que o comando sed usando escapou strings (e com o mesmo comando sed ) por exemplo)
  • não se esqueça do caractere de nova linha (aqui você pode querer verificar se $lhs contém algum e executar uma ação).

Outra opção é usar perl em vez de sed e passar as strings no ambiente e usar os operadores \Q / \E perl regexp para obter strings literalmente:

A=lhs B=rhs perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'

perl (por padrão) não será afetado pelo conjunto de caracteres do local, pois, acima, considera apenas as cadeias como matrizes de bytes sem se importar com quais caracteres (se houver) eles podem representar para o usuário. Com sed , você pode conseguir o mesmo, fixando a localidade em C com LC_ALL=C para todos os comandos sed (embora isso também afete o idioma das mensagens de erro, se houver).

    
por 12.05.2014 / 16:46

Tags