Como esse comando de substituição 'sed' com muitos sinais @ funciona?

8

Alguém pode explicar como esse sedcomando funciona?

sed 's@+@ @g;s@%@\\x@g' | xargs -0 printf "%b"
Raj
fonte
3
A maneira normal de fazer isso é usar barras, mas isso pode se tornar complicado se você pesquisar e substituir algo por barras. Esse não é o caso aqui, portanto, mesmo estando perfeitamente bem, confunde futuros mantenedores como você.
Thorbjørn Ravn Andersen
2
… E os leva a aprender algo novo sobre sedesse caminho! :)
dessert

Respostas:

15

No sed, comandos substitutos geralmente são escritos como s/pattern/replacement/options. No entanto, não é necessário usar /- você pode usar outros caracteres, se for conveniente, para que possa ser s@pattern@replacement@optionsou s:foo:bar:g. s@+@ @gé como s/+/ /g- substitua tudo +por espaços. Da mesma forma, s@%@\\x@gsubstitui todos %por \x(uma única barra invertida é um caractere de escape no sed, então você precisa de dois para obter uma barra invertida real).

Uma string como foo+%2Fbarse tornará foo \x2Fbar. printf "%b"expandirá as seqüências com escape de barra invertida como \x2F(o caractere ASCII cujo valor hexadecimal é 2F, o que é /) para finalmente fornecer a você foo /bar.

muru
fonte
2
Em resumo, um decodificador de URL-> nome do arquivo.
Thorbjørn Ravn Andersen 04/10
10

O comando que você está perguntando para decodificar +es e %sequências de URLs não é apenas um sedcomando, é um pipeline que processa a entrada sede a canaliza xargspara processamento adicional. Primeiro vamos olhar para o sedcomando:

sed 's@+@ @g;s@%@\\x@g'

Você pode estar mais acostumado a vê-lo /do que @como separador, o que poderia facilmente ter sido feito aqui sem complicações, pois não /aparece nos padrões de pesquisa nem nos textos de substituição. Este comando é equivalente:

sed 's/+/ /g;s/%/\\x/g'

Como /, @é um caractere de pontuação perfeitamente bom para sed.

Em cada linha de entrada:

  1. s@+@ @g( s/+/ /g) substitui ( s) ocorrências de +por um espaço. Isso afeta todos os +es em uma linha ( g), não apenas o primeiro.

  2. ; finaliza a ação ("comando") e permite que você especifique outra no mesmo "script".

  3. s@%@\\x@g( s/%/\\x/g) substitui ( s) ocorrências de %com \x. Como antes, ele atua em todos, e não apenas no primeiro de cada linha ( g).

    \\xNo \\representa apenas um \, porque \tem um significado especial para sed. Seu significado especial é, na verdade, o personagem que você usa para remover o significado especial de outro personagem que vem depois dele que, de outra forma, teria um significado especial. Portanto, deve ser escapado como \\.


Agora vamos dar uma olhada no xargscomando, cujo objetivo é executar printf.

xargsconstrói linhas de comando. Se você executar , onde houver uma ou mais palavras, será executado com argumentos adicionais da linha de comando lidos a partir de sua entrada. Nesse caso, a entrada para é a saída de , por causa do pipe ( ). Normalmente interpreta qualquer espaço em branco em sua entrada para significar que o texto antes e depois constitui argumentos separados, mas a opção faz com que ele divida argumentos em ocorrências do caractere nulo .xargs command...command...xargscommand...xargssed|xargs-0

No uso pretendido do seu comando, um caractere nulo não aparecerá e xargsserá executado printf %bcom apenas um argumento de linha de comando adicional, a saída do sedcomando. Portanto, embora não seja equivalente em geral, nesse caso, o pipeline inteiro pode ter sido escrito assim usando substituição de comando em vez de xargs:

printf '%b\n' "$(sed 's/+/ /g;s/%/\\x/g')"

Quanto ao que printfse pretende fazer aqui, como muru diz que o %bespecificador de formato consome e imprime um argumento (como %s), mas causa escapes de barra invertida - do tipo que o sedcomando no lado esquerdo do pipe foi escrito para gerar - a ser traduzido nos personagens que eles representam .

Suponha que eu execute esse comando e passe http://foldoc.org/debugging%20by%20printfcomo entrada. Recebo http://foldoc.org/debugging by printfcomo saída, porque as %20seqüências são traduzidas em espaços.

Eliah Kagan
fonte
3

Essa é a beleza de sed, ela aplica seus paradigmas a si mesma ... Após o comando (como sou trou nada), o próximo caractere é considerado o separador.

Você deve escolher sabiamente evitar interferência com o shell e o próprio comando, e manter a legibilidade, mas é perfeitamente válido escrever algo tão horrível quanto:

echo 'arrival' | sed srarbrg

... e, brrivblcomo resultado, é o que você espera. Você pode se divertir, tornando-o realmente enigmático, como em:

echo 'arrival' | sed s\fa\fb\fg   # \f is form feed, chr(12)

O uso comum é usar a barra como delimitador, mas quando sua expressão contém o delimitador, fica mais fácil capturar qual é a intenção. Seu delimitador pode ser qualquer coisa no intervalo ASCII8 (delimitadores multibyte, como £provocam um erro).

Lembre-se de que o objetivo é tornar as coisas mais fáceis, não mais enigmáticas.

Marabiloso
fonte
Correndo com a idéia enigmática, este é um comando sed válido, embora ele não faz nada de útil:sed "snack is an apple or something" <<< "I sed your snack is an apple or something"
wjandrea
Agradável! Sim, você também pode usar sedcomandos como quebra-cabeças, o quão nerd é isso?
Marabiloso