Eu usaria dois arrays:
awk -F, '{a[$0]=$2;if($3=="win")b[$2]++}END{for(i in a){if(b[a[i]])print i}}'
Eu tenho um arquivo de log com user_id e outra coluna com resultados do concurso. Eu gostaria de:
Exemplo:
log.csv
id,user_id,winner,page_id
1,user_1,win,1
2,user_1,,10
3,user_2,,1
4,user_2,,2
5,user_4,win,10
6,user_5,,2
7,user_5,win,3
Dado um arquivo de log como este, atualmente estou fazendo isso como um processo de duas etapas:
Etapa 1: retorne cada linha mencionando a palavra 'win'
/win/ {
FS=","
# To make unique, store user_id's in array
n[$2] = 0
}
# Print the unique array keys
END{
for (x in n)
print x
}
Isso produz:
user_1
user_4
user_5
Salvei esta saída no arquivo output.txt
Depois, passo esse arquivo e o arquivo de log original para outro arquivo awk:
NR == FNR{
n[$1] = 0 # Set the user ID to the array
next # Go to the next file
}
{
FS=","
if($2 in n){
print $0
}
}
Isso retorna a saída correta (todas as linhas para cada um dos user_ids que ganhou):
1,user_1,win,1
2,user_1,,10
5,user_4,win,10
6,user_5,,2
7,user_5,win,3
Existe uma maneira mais elegante de fazer isso? Com um único arquivo awk?
Is there a more elegant way to do this?
Sim, claro que existe. Basta executar o Awk duas vezes no mesmo arquivo (como você disse no seu título).
awk -F, '$3=="win"{won[$2]} FNR==NR{next} $2 in won' log.csv log.csv
Como eu preenho grep
é mais rápido que awk
Então se você tem GNU grep
com extensão perl você pode tentar
fgrep -f <(grep -oP "[^,]*(?=,win)" log.csv) log.csv
Sem perl.ext
, você terá que direcionar grep
de saída para cut
fgrep -f <(grep win log.csv | cut -d, -f2) log.csv
Ou use sed
(parece ser mais rápido do que acima de grep
| cut
)
fgrep -f <(sed -n '/win/s/^[^,]*,\([^,]*\).*//p' log.csv) log.csv
Mais tarde, mas para a posteridade, gostaria de salientar que você pode fazer isso:
awk '
BEGIN
{
while(getline < FILENAME)
{
# do first pass stuff
}
}
{
# do second pass stuff
}
' file
Se você quiser fazer mais passes, você pode close(FILENAME)
após o primeiro loop while, então faça um segundo.
Usando csvkit
, pode-se fazer
$ csvsql --query 'SELECT b.* FROM log AS a JOIN log AS b USING (user_id) WHERE a.winner="win"' log.csv
id,user_id,winner,page_id
1,user_1,win,1
2,user_1,,10
5,user_4,win,10
6,user_5,,2
7,user_5,win,3
Mas não tenho tanta certeza de que será muito rápido em um arquivo de entrada enorme. Por padrão, um banco de dados SQLite é criado, preenchido e consultado em segundo plano.
Você pode fazer isso no shell diretamente também:
$ join -t ',' -1 2 -2 2 log.csv log.csv | awk -v FS=',' -v OFS=',' '$3 == "win" { print $5,$1,$6,$7 }'
1,user_1,win,1
2,user_1,,10
5,user_4,win,10
6,user_5,,2
7,user_5,win,3
Mais uma vez, não tenho certeza de como isso se aplica a arquivos enormes. Além disso, o arquivo de entrada deve ser classificado na coluna 2 (que são os dados do exemplo).
Esta é uma solução completa de arquivo único do gnu awk você pode apenas executá-lo como: > awk -f singlestep.awk log.csv
BEGIN {
FS=",";
#you cannot use FILENAME , since in BEGIN section you are not processing any files and the FILENAME variable is empty
# So you need to use the ARGV
while(getline < ARGV[1])
{
if ($0 ~ /win/) {
# To make unique, store user_id's in array
n[$2] = 0;
}
}
}
{
if ($2 in n)
{
print $0;
}
}
Tags awk