Assumindo as facilidades do GNU:
find . -mtime -1 -exec bash -c \
'for f; do tac "$f" | grep -m1 fileprefix; done' _ {} +
Eu preciso pesquisar vários arquivos de log (todos os arquivos gerados nas últimas 24 horas, todos mantidos no mesmo diretório) para encontrar a última ocorrência de uma string. Este é o comando que eu escrevi:
find . -mtime 1 | grep fileprefix | xargs grep 'search string' | tail -1
Mas isso retorna apenas a última linha de um arquivo. Alguma sugestão sobre como ajustar isso para obter todas as linhas?
Se tudo estiver em um único diretório, você poderia fazer:
for file in *fileprefix*; do
grep 'search string' "$file" | tail -1
done
Se forem arquivos grandes, pode valer a pena acelerar o processo usando tac
para imprimir o arquivo em ordem reversa (última linha primeiro) e, em seguida, grep -m1
para corresponder à primeira ocorrência. Dessa forma, você evita ter que ler o arquivo inteiro:
for file in *fileprefix*; do
tac file | grep -m1 'search string'
done
Ambos supõem que não há diretórios correspondentes a fileprefix
. Se houver, você receberá um erro que você pode simplesmente ignorar. Se isso for um problema, verifique apenas os arquivos:
for file in *fileprefix*; do
[ -f "$file" ] && tac file | grep -m1 'search string'
done
Se você também precisar imprimir o nome do arquivo, adicione -H
a cada grep
invocação. Ou, se o seu grep
não for compatível, peça para pesquisar também em /dev/null
. Isso não alterará a saída, mas como grep
recebe vários arquivos, sempre será impresso o nome de cada hit:
for file in *fileprefix*; do
grep 'search string' "$file" /dev/null | tail -1
done
find . ! -name . -prune -mtime 1 -name 'fileprefix*' \
-exec sed -se'/searchstring/h;$!d;x' {} +
... funcionará se você tiver o GNU sed
que suporta a opção -s
eparate files e um POSIX find
.
Você provavelmente deve adicionar os qualificadores ! -type d
ou -type f
, porque tentar ler um diretório não será muito útil, e estreitar ainda mais o intervalo para arquivos regulares pode evitar que uma leitura fique pendurada em um pipe ou arquivo de dispositivo serial .
A lógica é incrivelmente simples - sed
sobrescreve seu espaço h
old com uma cópia de qualquer linha de entrada que corresponda a searchstring
, então d
eletiza da saída todas as linhas de entrada, mas a última para cada arquivo de entrada. Quando chega à última linha, e x
altera seus espaços de retenção e padrão, e assim, se searchstring
for encontrado enquanto lê o arquivo, a última ocorrência será impressa automaticamente na saída, senão ele grava um espaço em branco. linha. (adicione /./!d
ao final do script sed
se isso não for desejado) .
Isso fará uma única invocação de sed
para alguns arquivos de entrada de 65k - ou seja qual for o limite de ARG_MAX
. Esta deve ser uma solução muito eficaz e é simplesmente implementada.
Se você também quiser os nomes dos arquivos, dado um% GNU sed
você pode escrevê-los em linhas separadas com o comando F
, ou então pode imprimi-los por find
em uma lista separada por lote anexando o -print
primary após +
.
Que tal:
find . -mtime -1 -name "fileprefix*" -exec sh -c \
'echo "$(grep 'search string' $1 | tail -n 1),$1"' _ {} \;
O texto acima apresenta uma boa saída com a última ocorrência de uma string de pesquisa em cada arquivo, seguida do respectivo nome de arquivo após a vírgula (modifique a parte "$ 1" no echo para alterar a formatação ou removê-la se desnecessário) . A saída de exemplo que procura por uma string de pesquisa '10' em arquivos com um prefixo de nome "file" é a seguinte:
[dmitry@localhost sourceDir]$ find . -mtime -1 -name "file*" -exec sh -c 'echo "$(grep '10' $1 | tail -n 1),$1"' _ {} \;
Another data 02 10,./file02.log
Some data 01 10,./file01.log
Yet another data 03 10,./file03.log
find . -mtime 1 -name 'fileprefix*' -exec grep -Hn 'search string' {} + |
sort -t: -k1,2 -n |
awk -F: '{key=$1 ; $1="" ; $2="" ; gsub(/^ /,"",$0); a[key]=$0}
END {for (key in a) { print key ":" a[key] }}'
Isso usa as opções grep
e -H
do GNU -n
para sempre imprimir o nome do arquivo e o linenumber de todas as correspondências, depois ordena o nome do arquivo e o linenumber e o canaliza para o awk, que armazena o arquivo. última correspondência para cada nome de arquivo em uma matriz e, eventualmente, imprime.
Um método de força bruta, mas funciona.