extrai a quarta coluna de um arquivo csv usando o comando unix

6

Eu tenho um arquivo csv formatado como abaixo.

"col1","col2","col3","col4"
"1","text1","<p>big
      html
     text</p>
","4th column"
"2","text2","<p>big2
      html2
     text2</p>
","4th column2"

Eu quero extrair a quarta coluna usando. Eu acho que awk é a melhor ferramenta para isso (me avise se eu estiver errado). Eu tentei isso

awk -F, '{print $4}' myFile.csv 

mas falha. Eu acho que a terceira coluna é multilinha. Como posso usar awk ou qualquer outro comando unix para extrair a quarta coluna. Estou procurando uma solução eficiente, pois meu arquivo real é grande (> 2 GB)

    
por agstudy 20.12.2013 / 15:47

7 respostas

10

ATUALIZAÇÃO:

Na verdade, uma maneira muito mais fácil é definir o separador de registro em gawk :

$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4}' myFile.csv
"col4
"4th column
"4th column2

No entanto, isso removerá o " final do final de cada coluna. Para corrigir isso, você mesmo pode imprimi-lo:

$ gawk 'BEGIN{RS="\"\n"; FS=","}{print $4"\""}' myFile.csv
"col4"
"4th column"
"4th column2"

Se você não quiser as aspas, você pode definir o separador de campo como "," :

$ gawk 'BEGIN{RS="\"\n"; FS="\",\""}{print $3}' myFile.csv
col3
4th column
4th column2

A única maneira de pensar nisso Uma maneira de fazer isso é primeiro modificar o arquivo e depois analisá-lo. No seu exemplo, a nova linha que realmente separa dois registros sempre segue um " :

"col1","col2","col3","col4"   <-- here 
1,"text1","<p>big             <-- no "

Se esse for o caso de todo o arquivo, você poderá substituir todas as novas linhas que não sejam imediatamente após um " por um espaço reservado e, assim, ter tudo em uma única linha. Você pode então analisar normalmente com gawk e, finalmente, substituir o espaço reservado pela nova linha novamente. Usarei a string &%& como espaço reservado, pois é improvável que exista em seu arquivo:

$ perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv | awk -F, '{print $4}'
"col4"
"4th column"
"4th column2"

O sinal -p para perl significa print each line of the input file depois de aplicar o script fornecido por -e . Então há 3 comandos de substituição ( s/foo/bar/ ):

  • s/"\s*\n/"&%&/ : Isso localizará qualquer " , seguido por 0 ou mais caracteres em branco ( \s* ) e, em seguida, um caractere de nova linha ( \n ). Ele substituirá isso por "&%& . As aspas são adicionadas para preservar o formato e o &%& é apenas um espaço reservado aleatório, pode ser qualquer coisa que não apareça no seu arquivo.

  • s/\n//g; : como as novas linhas reais foram substituídas pelo marcador de posição, agora podemos remover com segurança todas as novas linhas restantes nesse registro. Isso significa que todas as linhas do registro atual foram agora concatenadas na linha atual.

  • s/&%&/\n/ : isso transforma o alocador de espaço de volta em uma nova linha normal.

Para entender a saída do comando, execute-o sem gawk :

$ perl -pe 's/"\s*\n/"&%&/; s/\n//g; s/&%&/\n/;' myFile.csv 
"col1","col2","col3","col4"
1,"text1","<p>big      html     text</p>","4th column"
2,"text2","<p>big2      html2     text2</p>","4th column2"

Então, agora você tem seus registros longos em linhas simples e isso é um alimento perfeito para gawk .

Você também pode fazer isso diretamente no Perl:

perl -ne '$/="\"\n"; chomp;@a=split(/,/);print "$a[3]\"\n"' myFile.csv
"col4"
"4th column"
"4th column2"

Isso está usando um pouco mais de magia Perl. A variável especial $/ é o separador de registro de entrada. Definindo-o como "\n , informamos ao Perl para dividir as linhas não em \n , mas apenas em "\n" , para que cada registro seja tratado como uma única linha. Quando isso for feito, chomp removerá a nova linha do final da linha (para impressão posterior) e split dividirá cada registro (em , ) e a salvará na matriz @a . Finalmente, imprimimos o 4º elemento da matriz (os arrays são numerados de 0, de modo que é $a[3] ), que é a quarta coluna.

E ainda mais mágica, ative a autocorreção ( -a ) e divida-a por vírgulas ( F"," ). Isso dividirá cada registro no array especial @F e você poderá imprimir o 4º elemento do array:

$ perl -F"," -ane '$/="\"\n";chomp;print "$F[3]"' myFile.csv
"col4"
"4th column"
"4th column2"
    
por 20.12.2013 / 16:16
6

Eu recomendaria usar um módulo de análise CSV testado em batalha. Por exemplo:

perl -MText::CSV -E '
    $csv = Text::CSV->new({binary=>1}); 
    while ($row = $csv->getline(STDIN)) {say $row->[3]}
' < file.csv
col4
4th column
4th column2

ou isso produz os mesmos resultados:

ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' file.csv
    
por 20.12.2013 / 16:30
5

Python:

python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" myfile.csv

Uma solução conservadora de memória para arquivos grandes que repassa pelo arquivo uma linha por vez, diferente da abordagem acima, que carrega o conteúdo do arquivo na memória por meio de uma lista

#!/usr/bin/env python
import sys
import csv
with open(sys.argv[1]) as f:
  for row in csv.reader(f):
    print(row[3])

RESULTADO DO TESTE de todas as soluções:

SO: Ubuntu 12.04

Dados públicos de CSV Download de: link

Detalhes das versões

root@ubuntu:~# python --version
Python 2.7.3
root@ubuntu:~# ruby --version
ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-linux]
root@ubuntu:~# perl --version

This is perl 5, version 14, subversion 2 (v5.14.2) built for i686-linux-gnu-thread-multi-64int

Resultado com time

root@ubuntu:~# time python -c "import csv,sys; print '\n'.join([ r[3] for r in csv.reader(open(sys.argv[1]))])" Master.csv > /tmp/python

real    0m1.112s
user    0m0.056s
sys     0m0.316s
root@ubuntu:~# time ruby -rcsv -e 'CSV.foreach(ARGV.shift) {|row| puts row[3]}' Master.csv > /tmp/ruby

real    0m24.582s
user    0m23.397s
sys     0m0.448s
root@ubuntu:~# time perl -MText::CSV -E '
>     $csv = Text::CSV->new({binary=>1});
>     while ($row = $csv->getline(STDIN)) {say $row->[3]}
> ' < Master.csv > /tmp/perl

real    0m7.049s
user    0m5.876s
sys     0m0.468s
    
por 20.12.2013 / 16:47
2

se for o estilo unix "\ n" terminou as linhas

tr -d "\n" < myfile.csv | awk 'BEGIN{RS=","} !(NR % 4)'

Alguns campos são multi-linhas o tr -d "\n" exclui todo o caractere de novas linhas, criando um fluxo de valores separados "". o awk é instruído para usar "," como separador de linha e para imprimir todas as vezes (o número de linha módulo 4) é 0.

Isso só funciona se o quarto campo for o último campo (como em sua amostra). Se não é esse o caso:

tr -d "\n" < myfile.csv | awk 'BEGIN{RS=","; last=12} (++c == 4) (c == last) {c=0}'

Ele conta as linhas, imprime a linha quando a contagem é 4 e redefine a contagem quando o último campo é atingido.

    
por 20.12.2013 / 17:06
1

Tente o seguinte:

while IFS=',' read -r a1 a2 a3 a4 
do 
echo "$a4" >> urname.csv
done < input.csv
    
por 17.02.2014 / 17:50
1

A forma mais simples de fazer isso foi usar o csvtool . Eu também tive outros casos de uso para usar o csvtool e ele pode manipular aspas ou delimitadores apropriadamente se eles aparecerem dentro dos próprios dados da coluna.

csvtool format '%(4)\n' input.csv

Substituir 4 pelo número da coluna extrairá efetivamente os dados da coluna que você está procurando.

    
por 25.10.2016 / 20:44
0

Obtenha os elementos 4 do seu CSV usando.

cut -d , -f 4 myFile.csv  

Salve em um arquivo com:

cut -d , -f 4 myFile.csv | cat >> my4thEltsFile.csv
    
por 03.03.2017 / 15:57