Bash lido com novas linhas delimitadoras

2

Eu tenho um csv para o qual eu tenho que extrair um campo específico. O csv é delimitado por pipe ( | ), com aspas duplas ( " ) para proteger os campos de texto e as novas linhas (em alguns) do texto.

por exemplo,

"aaa"|"111"|"!!!"|""
"bbb"|"222"|"@@
@"|""
"ccc"|"333"|"###"|""

Eu gostaria de extrair o segundo campo de cada registro:

111
222
333

Estou usando um script bash para ler o arquivo, mas parece que o comando read para quando alcança uma nova linha, mesmo quando -d é especificado. Isso significa, no exemplo acima, que meu script processa 1 corretamente (eu uso read -d \| varname ), mas não 2, porque ele não reconhece a nova linha como parte do terceiro campo. Agora ele vê @"|"" como um novo registro e tudo fica confuso.

É possível usar read para isso, ou devo procurar alternativas?

Gastei DAYS brincando com as configurações de read e pesquisando na web. Engraçado, me deparei com alguém com um problema exatamente no mesmo tipo de arquivo de entrada que eu tenho, mas esse problema foi com o Excel.

    
por oxydog 10.05.2016 / 16:58

3 respostas

4

Para um shell com um read integrado que possa manipular o CSV, você pode usar ksh93 em vez de bash :

$ while IFS='|' read -rS a b c; do printf '%s\n' "$b"; done < file
111
222
333

Para converter esse formato em algo que bash ' read pode manipular, você pode:

< file ksh93 -c 'while IFS="|" read -rSA a; do
                   printf "%s|" "${a[@]//[\|]/\
$ while IFS='|' read -rS a b c; do printf '%s\n' "$b"; done < file
111
222
333
}" printf "
< file ksh93 -c 'while IFS="|" read -rSA a; do
                   printf "%s|" "${a[@]//[\|]/\%pre%}"
                   printf "%pre%"
                 done' |
       bash -c 'while IFS="|" read -d "" a b c; do
                  printf "%s\n" "$b"
                done'
" done' | bash -c 'while IFS="|" read -d "" a b c; do printf "%s\n" "$b" done'
    
por 10.05.2016 / 17:10
1

Você realmente deve estar usando um analisador de CSV adequado. Por exemplo, usando o que vem com o ruby:

ruby -rcsv -e 'CSV.foreach("file", :col_sep => "|") {|row| p row; puts row[1]}'

nós recebemos

["aaa", "111", "!!!", ""]
111
["bbb", "222", "@@\n@", ""]
222
["ccc", "333", "###", ""]
333

Você pode ver na segunda linha a nova linha incorporada. Remova p row para se livrar dessas linhas de "depuração".

    
por 10.05.2016 / 17:18
0

OK, então a melhor solução para mim (mas eu acho que é uma questão de gosto), estava usando fgetcsv do PHP, já que eu já tenho PHP nesse servidor. É uma pena que o comando bash read não manipule as novas linhas, assim como a função PHP. Ele reconhece automaticamente delimitadores adicionais (como "'s).

Amostra:

<?php
$row = 1;
if (($handle = fopen("test.csv", "r")) !== FALSE) {
    while (($data = fgetcsv($handle, 10000, "|")) !== FALSE) {
        $num = count($data);
        echo "$num fields in line $row:\n";
        $row++;
        for ($c=0; $c < $num; $c++) {
            echo $c + 1 . ": " . $data[$c] . "\n";
        }
    }
    fclose($handle);
}
?>

Saída (por exemplo, na minha pergunta original):

4 fields in line 1:
1: aaa
2: 111
3: !!!
4: 
4 fields in line 2:
1: bbb
2: 222
3: @@
@
4: 
4 fields in line 3:
1: ccc
2: 333
3: ###
4: 
    
por 11.05.2016 / 11:03