Você pode usar a função gsub()
do awk para substituir todas as ocorrências de "
e "
(citações seguidas por espaço E espaço seguidos por uma aspa) por algum separador arbitrário, definir FS para esse separador e extrair o que você quer. Observe que, se você alterar FS, a numeração dos campos também será alterada. Você também precisará redefinir o FS de volta ao seu valor original para processar corretamente a próxima linha de entrada.
No seu caso, você também quer extrair alguns dados (data e hora) dos campos antes de alterar o FS.
por exemplo. se ./file
contiver 5 linhas, cada uma cópia exata da linha de amostra fornecida por você:
$ grep -i 'logged in' ./file | tail | awk '
{ d=$1;
t=$2; sub(/\..*/,"",t);
FS="XXX";
gsub(/" | "/,"XXX",$0);
print $2,"logged in at", t, d;
FS="[[:space:]]+"
}'
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21
sarah the princes logged in at 21:54:01 2017-12-21
Eu usei XXX como o separador de campo porque ele não aparece em nenhum lugar na entrada. Um caractere de tabulação teria funcionado tão bem para este exemplo, mas isso não teria demonstrado que os separadores de campo não precisam ser um único caractere - o que será importante se você não puder (ou não puder facilmente) determinar um único caractere que não é usado em nenhuma parte da entrada.
Fica mais complicado se você precisar extrair dados de campo de após os campos com aspas duplas (por exemplo, o endereço IP ou os campos da porta udp) - não é possível extraí-los antes do gsub
porque você não pode ter certeza de qual será o número do seu campo. Eu estaria inclinado a usar perl
neste ponto (ou talvez até sed
como na resposta @ Wildcard), mas uma maneira de fazer isso com awk
é expandir a expressão regular da chamada de função gsub
para se adequar . por exemplo. substituindo o script awk
por:
$ grep -i 'logged in' ./file | tail | awk '
{ d=$1;
t=$2;
sub(/\..*/,"",t);
FS="XXX";
gsub(/" | "|address: |, /,"XXX",$0);
sub(/ .*/,"",$8); # get rid of trailing junk after udp port
print $2,"logged in at", t, d, "as" ,$4, "from", $6":"$8;
FS="[[:space:]]+"
}'
produziria resultados assim:
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
sarah the princes logged in at 21:54:01 2017-12-21 as guest from 111111111:udp
Para completar, aqui está uma maneira de fazer isso em perl
usando o módulo de núcleo perl Text::ParseWords
:
#!/usr/bin/perl
use strict;
use Text::ParseWords;
my $keep=1; # keep " chars in output. set to 0 to strip them.
while(<>) {
my @F = quotewords('\s+', $keep, $_);
$F[1] =~ s/\..*//; # strip decimal fraction from time field
$F[10] =~ s/,//; # strip trailing comma from IP address field
# remember: perl array indices start at zero, not one.
printf "%s logged in at %s %s as %s from %s:%s\n", @F[5,1,0,7,10,13];
}
Isso usa a função quotewords()
de Text::Parsewords
para dividir cada linha de entrada em campos (armazenados em uma matriz chamada @F
), faz algumas pequenas limpezas em alguns dos campos e, em seguida, imprime os campos necessários com printf
.
Como um one-liner, seria escrito como:
grep -i 'logged in' ./file | tail | perl -MText::ParseWords -n -e '
@F = quotewords(q/\s+/, 1, $_);
$F[1] =~ s/\..*//;
$F[10] =~ s/,//;
printf "%s logged in at %s %s as %s from %s:%s\n", @F[5,1,0,7,10,13]'
Perceba como mudei '/s+'
para q/\s+/
- perl tem ótimos operadores de cotação que pode ser usado para evitar a pergunta simples dentro do problema de aspas simples .