Eu uso muita classificação grep awk no meu shell unix para trabalhar com arquivos de texto de coluna separados por tabulação de tamanho médio (em torno de 10M a 100M linhas). A esse respeito, o shell unix é minha planilha.
Mas eu tenho um grande problema, que é selecionar registros com uma lista de IDs.
Tendo table.csv
arquivo com formato id\tfoo\tbar...
e ids.csv
arquivo com lista de IDs, selecione apenas os registros table.csv
com ID presente em ids.csv
.
tipo /programming/13732295/extract-all-lines-from-text-file-based-on-a-given-list-of-ids mas com shell, não perl.
grep -F
obviamente produz falsos positivos se os IDs tiverem largura variável.
join
é um utilitário que eu nunca consegui descobrir. Antes de tudo, requer classificação alfabética (meus arquivos geralmente são numerados), mas mesmo assim não consigo fazê-lo funcionar sem reclamar de ordem incorreta e pular alguns registros. Então eu não gosto disso. grep -f no arquivo com ^id\t
-s é muito lento quando o número de IDs é grande.
awk
é complicado.
Existem boas soluções para isso? Alguma ferramenta específica para arquivos separados por tabulação? Funcionalidades extras também serão bem-vindas.
UPD: corrigido sort
->join
grep -f
for muito lento, manter essa estratégia parecerá mais problemático do que vale a pena - as variações provavelmente serão afetadas pelos mesmos problemas de desempenho O (N * M). Talvez o seu tempo seria melhor gasto aprendendo a usar um normalizada SQL DB ...awk
.sort
pode fazer todos os tipos de classificação, numéricos, alfabéticos e outros. Vejaman sort
.Respostas:
Eu acho que você quis dizer
grep -f
não,grep -F
mas você realmente precisa de uma combinação de ambos e-w
:A razão pela qual você estava obtendo falsos positivos é (acho que você não explicou) porque se um ID pode estar contido em outro, ambos serão impressos.
-w
remove esse problema e-F
garante que seus padrões sejam tratados como seqüências de caracteres, não como expressões regulares. Deman grep
:Se seus falsos positivos forem porque um ID pode estar presente em um campo sem ID, faça um loop no seu arquivo:
ou, mais rápido:
Pessoalmente, eu faria isso
perl
:fonte
^
com -F, não pode segmentar especificamente a primeira coluna.^id\t
bit do OP implica queid
pode ocorrer em outra coluna. Caso contrário, isso não importa.O
join
utilitário é o que você deseja. Requer que os arquivos de entrada sejam classificados lexicamente.Supondo que seu shell seja bash ou ksh:
Sem precisar classificar, a solução awk usual é
fonte
join
não é um engano: suas palavras foram que você não conseguiu entender. Abra sua mente e aprenda. Que resultado você obteve e como isso difere do que você espera?join
.awk
solução aqui é muito rápida e eficiente para meus propósitos (estou extraindo subconjuntos de algumas centenas de arquivos com ~ 100 milhões de linhas)As respostas para essa pergunta SO me ajudaram a contornar os problemas com a junção. Essencialmente, quando você classifica o arquivo em preparação para enviá-lo para a associação, precisa se certificar de que está classificando com base na coluna na qual está ingressando. Portanto, se esse é o primeiro, você precisa dizer qual é o caractere separador no arquivo e que deseja classificá-lo no primeiro campo (e somente no primeiro campo). Caso contrário, se o primeiro campo tiver larguras variáveis (por exemplo), seus separadores e possivelmente outros campos poderão começar a afetar a ordem de classificação.
Portanto, use a opção -t de classificação para especificar seu caractere de separação, e use a opção -k para especificar o campo (lembrando que você precisa de um campo de início e fim - mesmo que seja o mesmo) - ou ele classificará esse caractere até o final da linha).
Portanto, para um arquivo separado por tabulação, como nesta pergunta, o seguinte deve funcionar (com agradecimentos à resposta de glenn para a estrutura):
join -t$'\t' <(sort -d ids.csv) <(sort -d -t$'\t' -k1,1 table.csv) > output.csv
(Para referência, o sinalizador -d significa classificação do dicionário. Você também pode usar o sinalizador -b para ignorar os espaços em branco à esquerda, consulte
man sort
eman join
).Como um exemplo mais geral, suponha que você esteja juntando dois arquivos separados por vírgula -
input1.csv
na terceira coluna einput2.csv
na quarta. Você poderia usarjoin -t, -1 3 -2 4 <(sort -d -t, -k3,3 input2.csv) <(sort -d -t, -k4,4 input2.csv) > output.csv
Aqui, as opções
-1
e-2
especificam em quais campos juntar no primeiro e no segundo arquivos de entrada, respectivamente.fonte
Você também pode usar o ruby para fazer algo semelhante:
fonte