
Tentei a sorte grep
e sed
mas de alguma forma não consigo acertar.
Eu tenho um arquivo de log com cerca de 8 GB de tamanho. Preciso analisar um período de 15 minutos de atividade suspeita. Localizei a parte do arquivo de log que preciso examinar e estou tentando extrair essas linhas e salvá-las em um arquivo separado. Como eu faria isso em uma máquina CentOS normal?
Minha última tentativa foi essa, mas não funcionou. Estou perdido quando se trata sed
desse tipo de comando.
sed -n '2762818,2853648w /var/log/output.txt' /var/log/logfile
Responder1
sed -n '2762818,2853648p' /var/log/logfile > /var/log/output.txt
p
é para impressão
Responder2
Provavelmente a melhor maneira de fazer isso é redirecionando o shell, como outros mencionaram. sed
embora, embora seja um favorito pessoal, provavelmente não fará isso com mais eficiência do que o fará head
- que foi projetado para capturar apenas algumas linhas de um arquivo.
Existem outras respostas neste site que mostram comprovadamente que, para arquivos grandes, head -n[num] | tail -n[num]
o desempenho será sed
sempre superior, mas provavelmente ainda mais rápido do que evitar completamente o canal.
Criei um arquivo como:
echo | dd cbs=5000000 conv=block | tr \ \\n >/tmp/5mil_lines
E eu analisei:
{ head -n "$((ignore=2762817))" >&2
head -n "$((2853648-ignore))"
} </tmp/5mil_lines 2>/dev/null |
sed -n '1p;$p'
Eu só usei sed
lá para pegar apenas a primeira e a última linha para mostrar a vocês ...
2762818
2853648
Isso funciona porque quando você agrupa comandos { ... ; }
e redireciona a entrada para o grupo, ... ; } <input
todos eles compartilharão a mesma entrada. A maioria dos comandos esgotará todo o infile durante a leitura, portanto, em um { cmd1 ; cmd2; } <infile
caso, geralmente cmd1
lê do início ao final do infile e cmd2
não fica sem nenhum.
head
, no entanto, sempre buscará apenas até onde for instruído a fazer, e assim em um...
{ head -n [num] >/dev/null
head -n [num]
} <infile
...caso o primeiro procure [num]
e despeje sua saída /dev/null
e o segundo seja deixado para iniciar sua leitura onde o primeiro o deixou.
Você pode fazer...
{ head -n "$((ignore=2762817))" >/dev/null
head -n "$((2853648-ignore))" >/path/to/outfile
} <infile
Essa construção também funciona com outros tipos de comandos compostos. Por exemplo:
set "$((n=2762817))" "$((2853648-n))"
for n do head "-n$n" >&"$#"; shift
done <5mil_lines 2>/dev/null |
sed -n '1p;$p'
...que imprime...
2762818
2853648
Mas também pode funcionar como:
d=$((( n=$(wc -l </tmp/5mil_lines))/43 )) &&
until [ "$(((n-=d)>=(!(s=143-n/d))))" -eq 0 ] &&
head "-n$d" >>"/tmp/${s#1}.split"
do head "-n$d" > "/tmp/${s#1}.split" || ! break
done </tmp/5mil_lines
Acima do shell inicialmente define as variáveis $n
e $d
como ...
$n
- A contagem de linhas conforme relatada por
wc
para meu arquivo de teste/tmp/5mil_lines
- A contagem de linhas conforme relatada por
$d
- O quociente de
$n/43
onde 43 é apenas um divisor selecionado arbitrariamente.
- O quociente de
Em seguida, ele faz um loop until
que diminuiu $n
para $d
um valor less $d
. Ao fazer isso, ele salva sua contagem dividida $s
e usa esse valor no loop para incrementar o >
arquivo de saída nomeado chamado /tmp/[num].split
. O resultado é que ele lê um número igual de \n
campos delimitados por ewline em seu arquivo de entrada para um novo arquivo de saída para cada iteração - dividindo-o igualmente 43 vezes ao longo do loop. Ele gerencia isso sem ter que ler seu arquivo de entrada mais de 2 vezes - a primeira vez é quando wc
ele conta suas linhas e, no resto da operação, ele lê apenas quantas linhas escreve no arquivo de saída a cada vez.
Depois de executá-lo, verifiquei meus resultados como ...
tail -n1 /tmp/*split | grep .
SAÍDA:
==> /tmp/01.split <==
116279
==> /tmp/02.split <==
232558
==> /tmp/03.split <==
348837
==> /tmp/04.split <==
465116
==> /tmp/05.split <==
581395
==> /tmp/06.split <==
697674
==> /tmp/07.split <==
813953
==> /tmp/08.split <==
930232
==> /tmp/09.split <==
1046511
==> /tmp/10.split <==
1162790
==> /tmp/11.split <==
1279069
==> /tmp/12.split <==
1395348
==> /tmp/13.split <==
1511627
==> /tmp/14.split <==
1627906
==> /tmp/15.split <==
1744185
==> /tmp/16.split <==
1860464
==> /tmp/17.split <==
1976743
==> /tmp/18.split <==
2093022
==> /tmp/19.split <==
2209301
==> /tmp/20.split <==
2325580
==> /tmp/21.split <==
2441859
==> /tmp/22.split <==
2558138
==> /tmp/23.split <==
2674417
==> /tmp/24.split <==
2790696
==> /tmp/25.split <==
2906975
==> /tmp/26.split <==
3023254
==> /tmp/27.split <==
3139533
==> /tmp/28.split <==
3255812
==> /tmp/29.split <==
3372091
==> /tmp/30.split <==
3488370
==> /tmp/31.split <==
3604649
==> /tmp/32.split <==
3720928
==> /tmp/33.split <==
3837207
==> /tmp/34.split <==
3953486
==> /tmp/35.split <==
4069765
==> /tmp/36.split <==
4186044
==> /tmp/37.split <==
4302323
==> /tmp/38.split <==
4418602
==> /tmp/39.split <==
4534881
==> /tmp/40.split <==
4651160
==> /tmp/41.split <==
4767439
==> /tmp/42.split <==
4883718
==> /tmp/43.split <==
5000000
Responder3
Você provavelmente poderia conseguir isso com a ajuda de combinações de head
comandos tail
como abaixo.
head -n{to_line_number} logfile | tail -n+{from_line_number} > newfile
Substitua from_line_number
e to_line_number
pelos números de linha desejados.
Teste
cat logfile
This is first line.
second
Third
fourth
fifth
sixth
seventh
eighth
ninth
tenth
##I use the command as below. I extract from 4th line to 10th line.
head -n10 logfile | tail -n+4 > newfile
fourth
fifth
sixth
seventh
eighth
ninth
tenth