É possível combinar vários números de linha específicos (não intervalo) com sed?

9

Considerar:

echo -e "a\nb\nc\nd\ne\nf\ng\nh" | sed '3,5a test'

Isso corresponderá às linhas 3, 4 e 5.

Mas estou tentando corresponder apenas às linhas 3 e 5 (não 4). E acrescente 'teste' depois deles.

Aquarius Power
fonte

Respostas:

9
echo ... | sed -e '3a test' -e '5a test'

Se a operação for mais complexa do que neste caso, você poderá usar uma estrutura como esta:

sed 'b pattern; : action; a \
lalala
b end; : pattern; 3b action; 5b action; : end'

Ou seja, você coloca todos os comandos necessários entre b pattern;e b end;.

E você adiciona todos os seus padrões (números de linha ou o que for) depois : pattern;.

O que acontece é o seguinte:

  1. O primeiro comando salta sobre a parte da ação (talvez seja mais fácil ler se os padrões estão no início e b end;diretamente antes da parte da ação).

  2. Se um padrão corresponder, a execução pulará para a parte da ação. Após a execução da parte da ação, salta para o final.

Eu tento arrumar isso:

sed '3b action; 5b action; b end; : action; a \
lalala
: end'

Em uma única linha seria como:

sed "3b idAction; 5b idAction; b; : idAction; a test"

Portably, você precisa escrevê-lo:

sed '
   3b action
   5b action
   b
   : action
   a\
   lalala'

( bsem ramificações de rótulo até o final, para que você não precise de um endrótulo explícito , ;é válido um caractere em um rótulo nas sedimplementações padrão ).

Hauke ​​Laging
fonte
4
Uau, sub-rotinas sed. Arrefecer
glenn jackman
Uau, de fato, você poderia expandir isso? Eles parecem realmente úteis!
terdon
aquele com 'padrão' e 'ação' parece muito bom, já que não preciso repetir 'teste' (ou 'lalala'), consegui entender melhor ainda! :)
Poder de Aquário
@terdon O que eu poderia?
Hauke ​​Laging
Eu quis dizer que o segundo parece ótimo, mas é difícil de entender e, se você tiver tempo, agradeceria uma explicação.
terdon
5

Com sed(consulte a resposta de @ HaukeLaging )

Com awk:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | awk 'NR==3 || NR==5{$0=$0"\ntest"}1;'

Com perl:

$ echo -e "a\nb\nc\nd\ne\nf\ng\nh" | perl -pe '$_ .="test\n" if $. == 3 || $. == 5'
terdon
fonte
Can golf seu perl um pouco, usando o operador "inteligente jogo":perl -lnE 'say if $. ~~ @{[3,5]}'
Glenn Jackman
@glennjackman ah, sim, de fato, obrigado. Eu tenho que admitir que sou um pouco cauteloso ~~, não entendo tão bem quanto deveria. Além disso, ambos saye ~~precisam de perl v> = 5.10, certo?
terdon
Preciso acrescentar 'test' depois dessas linhas. Vejo agora que seria melhor esclarecer isso desde o início. Acabei de editar o OP.
Poder de Aquário
@AquariusPower, perl -pE 'say "test" if $. ~~ @{[4,6]}'- este prepends a linha: perl do -pfaz uma impressão implícita no final do programa dado.
Glenn Jackman
O @AquariusPower também vê a resposta atualizada.
terdon
3

Como o @HaukeLaging já disse, este comando faz o que você deseja:

sed -e'3a test' -e'5a test'

Agora, isso pode se tornar bastante complicado de digitar, se você quiser combinar, por exemplo, 20 linhas.

Nesses casos, supondo que seu shell suporte a expansão entre chaves , você pode usar este comando:

sed -e{3,5}'a test'

(Observe que os colchetes e a vírgula devem permanecer sem aspas.)

Como resultado, o shell transmitirá os argumentos para -e3a teste -e5a testpara sed, que é exatamente o que o primeiro comando faz.

Dennis
fonte