extrai de uma linha para outra e salve em um arquivo separado

6

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
    
por koljanep 05.01.2015 / 16:14

3 respostas

6
sed -n '2762818,2853648p' /var/log/logfile > /var/log/output.txt

p é para impressão

    
por 05.01.2015 / 16:17
2

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 ...

  • %código%
    • A contagem de linhas conforme relatada por $n do meu arquivo de teste wc
  • %código%
    • O quociente de /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 .

OUTPUT:

==> /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 
    
por 05.01.2015 / 17:34
0

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
    
por 05.01.2015 / 18:01