classificar | uniq | Xargs Grep… onde as linhas contêm espaços
-
03-07-2019 - |
Pergunta
Eu tenho um arquivo delimitado por vírgula "myfile.csv" onde a quinta coluna é um carimbo de data/hora.(mm/dd/aaaa hh:mm). Preciso listar todas as linhas que contêm datas duplicadas (há muitas)
Estou usando um shell bash via cygwin para WinXP
$ cut -d, -f 5 myfile.csv | sort | uniq -d
retorna corretamente uma lista das datas duplicadas
01/01/2005 00:22
01/01/2005 00:37
[snip]
02/29/2009 23:54
Mas não consigo descobrir como alimentar isso no grep para me fornecer todas as linhas.Obviamente, não posso usar xargs
direto, pois a saída contém espaços.Eu pensei que poderia fazer uniq -z -d
mas por alguma razão, combinar esses sinalizadores faz com que o uniq (aparentemente) não retorne nada.
Então, dado que
$ cut -d, -f 5 myfile.csv | sort | uniq -d -z | xargs -0 -I {} grep '{}' myfile.csv
não funciona...O que posso fazer?
Eu sei que eu poderia fazer isso em perl
ou outra linguagem de script ...mas minha natureza teimosa insiste que eu deveria ser capaz de fazer isso em bash
usando ferramentas de linha de comando padrão como sort
, uniq
, find
, grep
, cut
, etc.
Ensine-me, oh gurus.Como posso obter a lista de linhas necessárias usando ferramentas CLI típicas?
Solução
- sort -k5,5 fará a classificação nos campos e evitará o corte;
- uniq -f 4 irá ignorar os primeiros 4 campos do uniq;
- Além disso, um -D no uniq fornecerá todas as linhas repetidas (vs -d, que fornecerá apenas uma);
- mas o uniq esperará delimitado por tabulações em vez de csv, então tr ' ' ',' para corrigir isso.
O problema é se você tiver campos após o número 5 diferentes.Suas datas têm todas a mesma duração?Você pode adicionar -w 16 (para incluir hora) ou -w 10 (apenas para datas) ao uniq.
Então:
tr '\t' ',' < myfile.csv | sort -k5,5 | uniq -f 4 -D -w 16
Outras dicas
O -z
opção de uniq
precisa que a entrada seja separada por NUL.Você pode filtrar a saída de cut
através:
tr '\n' '\000'
Para obter zero linhas separadas.Então sort
, uniq
e xargs
tenho opções para lidar com isso.Tente algo como:
cut -d, -f 5 myfile.csv | tr '\n' '\000' | sort -z | uniq -d -z | xargs -0 -I {} grep '{}' myfile.csv
Editar:a posição do tr
no cano estava errado.
Você pode dizer ao xargs para usar cada linha como argumento completo usando a opção -d.Tentar:
cut -d, -f 5 myfile.csv | sort | uniq -d | xargs -d '\n' -I '{}' grep '{}' myfile.csv
Tente escapar dos espaços com sed:
echo 01/01/2005 00:37 | sed 's/ /\\ /g'
cut -d, -f 5 myfile.csv | sort | uniq -d | sed 's/ /\\ /g' | xargs -I '{}' grep '{}' myfile.csv
(Outra maneira seria ler as linhas de data duplicadas em um array IFS=$' ' e iterar sobre ele em um loop for.)
Este é um bom candidato para awk:
BEGIN { FS="," }
{ split($5,A," "); date[A[0]] = date[A[0]] " " NR }
END { for (i in date) print i ":" date[i] }
- Defina o separador de campo como ',' (CSV).
- Divida o quinto campo no espaço, cole o resultado em A.
- Concatene o número da linha à lista do que já armazenamos para aquela data.
- Imprima os números das linhas para cada data.