Grep para uma string no arquivo sem usar pipe

1

Eu quero buscar uma palavra em um arquivo nas últimas n linhas sem usar o pipe.

grep <string> filename

permite procurar o nome do arquivo por uma string. Mas, eu quero procurar uma string nas últimas N linhas do arquivo. Qualquer comando para procurar por isso sem usar o pipe?

    
por wolfpack_user 09.07.2015 / 04:23

4 respostas

6

Se o seu shell for compatível ( zsh , bash , algumas implementações de ksh ), você poderá utilizar substituição de processo

grep <pattern> <(tail -n5 yourfile.txt)

Onde -n5 significa obter as cinco últimas linhas.

Da mesma forma,

grep <pattern> <(head -n5 yourfile.txt)

procuraria pelas 5 primeiras linhas do seu arquivo.txt.

Explicação

Simplesmente falando, o processo substituído finge ser um arquivo, que é o que o grep está esperando. Uma vantagem com a substituição de processo é que você pode fornecer saída de vários comandos como entrada para outros comandos, como diff neste exemplo.

diff -y <(brew leaves) <(brew list)

This gets rid of the pipe (|) character, but each substitution is in fact creating a pipe1.

1 Observe que com ksh93 no Linux, pelo menos, | não usa um canal, mas um par de soquetes, enquanto processo de substituição usa um pipe (como não é possível para open a socket):

$ ksh93 -c 'readlink <(:)'
pipe:[620224]
$ ksh93 -c ': | readlink /proc/self/fd/0'
socket:[621301]

    
por 09.07.2015 / 04:55
3
n=$some_num
{   head -n"$(($(wc -l <in)-n))" >/dev/null
    grep 'match your string'
}   <in

Infelizmente, isso requer a leitura do arquivo inteiramente por meio de w / wc para obter uma contagem de linhas, porque não está claro o contrário quantas linhas estão no arquivo ou qual é o tamanho $n . Além disso, essa deve ser uma solução com muito desempenho, desde que <in seja um arquivo regular, lseek() able .

Primeiramente, obtemos nossa contagem de linha e subtraímos $n dela. head lê muitas linhas de stdin e grava os resultados em /dev/null . O que sobra depois são $n -count linhas de entrada em stdin e apenas seu grep e seu padrão.

Tecnicamente isso engana - há um pipe na substituição de comando para wc . É minha esperança que você possa ignorar isso.

A propósito, outra maneira de fazer isso pode parecer:

{   grep "-m$n" 'some pattern near yours' >/dev/null
    grep 'your pattern'
}   <in

... com um% GNUgrep. Se você puder grep $n ocorrências de outro padrão que o colocará na vizinhança de seu padrão de destino, você poderá realmente fazê-lo sem um pipe.

Eu tentei ficar com grep , mas aqui está uma solução sed de qualquer maneira. Os canais abaixo são apenas para entrada - e grep não está envolvido em todos os trechos para prefixar números de linha para que você possa ver quais números eles são. Tudo isso é apenas para o caso de exemplo. Você pode usar o script sed sozinho com um arquivo nomeado ou stdin de qualquer tipo e definir $pat e $n apropriadamente e ele funcionará.

Eu na verdade apenas reescrevi isso porque eu não gostava de não ser capaz de ancorar as partidas. Isso é um pouco mais lento - não notavelmente, e ainda é muito rápido, mas para cada linha intermediária em buffer ele corta todo o espaço padrão e isola a primeira linha no buffer. Dessa forma, todas as expressões âncoras normais funcionam como esperado.

pat=man n=40
man man   |
grep -n ''|
sed -e:B -e'${/^\n/D'  \
    -eh  -e's/\n.*//'  \
         -e"/$pat/p;x" \
    -e\} -e'$D;N;$bB'  \
         -e"$n,$ D;bB"
648:       /etc/man_db.conf
649:              man-db configuration file.
651:       /usr/share/man
652:              A global manual page hierarchy.
654:       /usr/share/man/index.(bt|db|dir|pag)
657:       /var/cache/man/index.(bt|db|dir|pag)
661:       apropos(1), groff(1), less(1), manpath(1),  nroff(1),  troff(1),  whatis(1),
662:       zsoelim(1),  setlocale(3),  manpath(5),  ascii(7),  latin1(7),  man(7), cat-
663:       man(8), mandb(8), the man-db package manual, FSSTND
680:       developing and maintaining man-db.

Aqui está outro exemplo, mas em um arquivo:

pat=. n=15
seq 100 >nums
sed -e:B -e'${/^\n/D'  \
    -eh  -e's/\n.*//'  \
         -e"/$pat/p;x" \
    -e\} -e'$D;N;$bB'  \
<nums    -e"$n,$ D;bB"
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    
por 09.07.2015 / 07:08
2

Por que você quer evitar canos?

Se você realmente quiser evitar o pipe, terá que executar dois comandos:

tail -N filename > filename.tmp
grep "string" filename.tmp

(when N is the last number of lines)
    
por 09.07.2015 / 04:45
2

Você pode fazer isso com awk e um pouco de ajuda:

$ N=8
$ awk -v start_line="$(( $(wc -l < alphabet) - N + 1 ))" 'NR>=start_line  &&  /e/' alphabet
sierra
whiskey
yankee
$

encontra todas as linhas contendo e nas últimas oito linhas de o alfabeto fonético . Isso tem a desvantagem de ler todo o arquivo de entrada duas vezes.

    
por 09.07.2015 / 05:04