sed -n '2762818,2853648p' /var/log/logfile > /var/log/output.txt
p
é para impressão
Eu tentei minha sorte com grep
e sed
, mas de alguma forma não consegui acertar.
Eu tenho um arquivo de log com cerca de 8 GB de tamanho. Eu 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á-lo em um arquivo separado. Como eu faria isso em uma máquina comum do CentOS?
Minha última tentativa foi essa, mas não funcionou. Estou com perda quando se trata de sed
e esses tipos de comandos.
sed -n '2762818,2853648w /var/log/output.txt' /var/log/logfile
Provavelmente, a melhor maneira de fazer isso é com o redirecionamento de shell, como outros já mencionaram. sed
, embora seja um favorito pessoal, provavelmente não o fará de forma mais eficiente do que o head
- que é projetado para capturar apenas tantas linhas de um arquivo.
Existem outras respostas neste site que mostram demonstravelmente que, para arquivos grandes, head -n[num] | tail -n[num]
superará sed
a cada vez, mas provavelmente ainda mais rápido do que isso é evitar completamente o pipe.
Eu criei um arquivo como:
echo | dd cbs=5000000 conv=block | tr \ \n >/tmp/5mil_lines
E eu corri:
{ head -n "$((ignore=2762817))" >&2
head -n "$((2853648-ignore))"
} </tmp/5mil_lines 2>/dev/null |
sed -n '1p;$p'
Eu usei apenas sed
para pegar apenas a primeira e última linha para mostrar a você ...
2762818
2853648
Isso funciona porque quando você agrupa comandos com { ... ; }
e redireciona a entrada para o grupo como ... ; } <input
, todos eles compartilharão a mesma entrada. A maioria dos comandos esgotará todo o arquivo enquanto ele estiver sendo lido, então, em um caso { cmd1 ; cmd2; } <infile
, geralmente cmd1
é lido da cabeça do arquivo para a cauda e cmd2
é deixado sem nenhum.
head
, no entanto, sempre buscará somente até agora através de seu infil como é instruído a fazer, e assim em um ...
{ head -n [num] >/dev/null
head -n [num]
} <infile
... caso o primeiro busque [num]
e copie sua saída para /dev/null
e o segundo seja deixado para começar sua leitura onde o primeiro saiu.
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, o shell inicialmente define as variáveis $n
e $d
como ...
$n
do meu arquivo de teste wc
/tmp/5mil_lines
, em que 43 é apenas um divisor selecionado arbitrariamente. Em seguida, ele faz um loop em $d
e reduziu $n/43
por until
para um valor menor que $n
. Ao fazer isso, ele salva sua contagem de divisão em $d
e usa esse valor no loop para incrementar o arquivo $d
de saída chamado $s
. O resultado é que ele lê um número igual de >
ewline campos delimitados em seu arquivo para um novo outfile para cada iteração - dividindo-o igualmente 43 vezes ao longo do loop. Ele o gerencia sem ter que ler seu arquivo mais de 2 vezes - a primeira vez é quando /tmp/[num].split
faz para contar suas linhas, e para o resto da operação ele lê apenas quantas linhas ele grava no arquivo de saída. Tempo.
Depois de executar, verifiquei meus resultados como ...
tail -n1 /tmp/*split | grep .
==> /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
Você provavelmente conseguiria isso com a ajuda das combinações de comandos head
e tail
, conforme 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
Tags command-line grep sed