Faça o sed ignorar as linhas não correspondentes


91

Como posso sedfiltrar as linhas correspondentes de acordo com alguma expressão, mas ignorar as linhas não correspondentes, em vez de permitir que sejam impressas?

Como um exemplo real, quero executar scalac(o compilador Scala) em um conjunto de arquivos e ler de sua -verbosesaída os .classarquivos criados. scalac -verboseproduz um monte de mensagens, mas estamos interessados ​​apenas nas do formulário [wrote some-class-name.class]. O que estou fazendo atualmente é isso ( |&é a maneira do bash 4.0 de canalizar o stderr para o próximo programa):

$ scalac -verbose some-file.scala ... |& sed 's/^\[wrote \(.*\.class\)\]$/\1/'

Isso extrairá os nomes dos arquivos das mensagens nas quais estamos interessados, mas também permitirá que todas as outras mensagens passem sem alterações! Claro que poderíamos fazer em vez disso:

$ scalac -verbose some-file.scala ... |& grep '^\[wrote .*\.class\]$' |
  sed 's/^\[wrote \(.*\.class\)\]$/\1/'

que funciona, mas se parece muito com o problema real, que é como instruir seda ignorar as linhas não correspondentes da entrada. Então, como fazemos isso?


2
A resposta aceita deve ser a de mouviciel: stackoverflow.com/a/1665574/869951
Oliver

Respostas:


89

Outra maneira com o sed simples:

sed -e 's/.../.../;t;d'

s///é uma substituição, tsem qualquer rótulo, pula condicionalmente todos os comandos seguintes e dexclui a linha.

Não há necessidade de perl ou grep.

(editado após a sugestão de Nicholas Riley)


3
No OS X 10.8.2 eu tive que separar txe dcom uma nova linha em vez de um ponto e vírgula como estava começando undefined label 'x;d;:x'.
davidchambers

6
Ainda melhor: sed -e 's/.../.../' -e 'tx' -e 'd' -e ':x'(sugerido em um comentário sobre uma questão semelhante ).
davidchambers

1
't' irá transferir para o fim do script, se nenhum rótulo for fornecido, de modo mais simples: sed -e 's/.../.../' -e 't' -e 'd'.
Nicholas Riley,

As pessoas não sabem o significado de -eopção, então não mencione sobre isso em geral.
Fredrick Gauss

242

Se você não quiser imprimir linhas que não correspondam, você pode usar a combinação de

  • -n opção que diz ao sed para não imprimir
  • p flag que diz ao sed para imprimir o que é correspondido

Isto dá:

sed -n 's/.../.../p'

3
Uma desvantagem dessa abordagem é que, se você tiver várias expressões correspondentes, o resultado também será impresso várias vezes. Por exemplo: echo foo | sed -n -e 's/foo/bar/p' -e 's/bar/oof/p'produzirá ambos bare oofem linhas separadas. Embora a variedade goto-label não possa lidar com vários padrões, pois excluirá a linha se o primeiro padrão não corresponder.
Rapsey

@Rapsey é porque você está dizendo para imprimir duas vezes. No único comando sed que você disse para imprimir duas vezes, cada instância é impressa in-situ (ou talvez armazenada em buffer). Você teria que canalizar em vez de -e ou apenas colocar 'p' no último -e.
microbiano de

@Rapsey: veja minha resposta sobre este ponto relevante
Amessihel

@microbial, não funcionará, pois o último sinalizador p funcionará em cada linha correspondida pela última expressão de substituição.
Amessihel

1

Use Perl:

... |& perl -ne 'print "$1\n" if /^\[wrote (.*\.class)\]$/'


0

Rapsey levantou um ponto relevante sobre expressões de múltiplas substituições.

  • Primeiro, citando uma resposta Unix SE , você pode "prefixar a maioria dos comandos sed com um endereço para limitar as linhas às quais eles se aplicam".
  • Em segundo lugar, você pode agrupar comandos entre chaves {}(separados por ponto e vírgula ;ou uma nova linha)
  • Terceiro, adicione o sinalizador de impressão p na última substituição

Sintaxe:

sed -n -e '/^given_regexp/ {s/regexp1/replacement1/flags1;[...];s/regexp1/replacement1/flagsnp}'

Exemplo (veja aqui o documento para mais detalhes):

  • Código:

    sed -n -e '/^ha/ {s/h/k/gp;s/a/e/g}' <<SAMPLE
    haha
    hihi
    SAMPLE
    
  • Resultado:

    kaka
    
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.