Reconstruindo um registro separado por tabulação no bash não funciona

0

Eu preciso analisar um arquivo CSV separado por tabulação usando o bash, examinar o conteúdo do registro e, se o registro atender a determinados critérios, adicioná-lo a um array. Basicamente, eu quero filtrar registros de um arquivo CSV antes de fazer algo com eles.

Meu pensamento era pegar cada linha no arquivo, colocar cada campo em uma matriz. Eu poderia então olhar para o array para ver se o registro atende a certas condições (por exemplo, field3="value", etc). Se sim, eu iria "reconstruir" a linha separada por tabulações e anexá-la a uma nova matriz.

Quando isso parece falhar, é a linha onde eu crio record . Parece estar anexando um espaço em vez de uma guia porque, mais tarde, o tamanho de details é o mesmo que se o registro fosse delimitado por espaço em vez de tabulação.

datafile=path/to/data.csv
records=()
header=$(head -n 1 $datafile)
IFS=$'\t' read -r -a fields <<< "$header"

while IFS=$'\t' read -r -a documents; do

    # processing to determine if current row in csv file matches certain criteria
    # if it does, the following will happen

    for r in ${documents[@]}; do record+="$r"$'\t'; done #appending space instead?
    records+="$record"
done < $datafile

for r in "${records[@]}"; do
    IFS=$'\t' read -r -a details <<< "$r"

    # size of details here is as if record is separated by spaces instead of tabs

    for i in "${!fields[@]}" ; do
        echo "${fields[i]}: ${details[i]}"
    done
done

Exemplo: Se este registro for um processo:

Hello World  [TAB]  nice weather we are having today  [TAB]  do you agree?

O tamanho de details deve ser 3, mas estou recebendo 11 em vez disso. Por quê?

    
por Scribblemacher 13.04.2016 / 17:22

2 respostas

4

Seu problema é coberto Por que meu script de shell sufoca em espaços em branco ou outros caracteres especiais? . Vou apenas explicar brevemente o que está acontecendo aqui.

O culpado é for r in ${documents[@]} . Como a expansão da variável é deixada sem aspas, você está usando a operação “split + glob”: o valor de cada elemento da matriz é dividido em palavras de acordo com o valor de IFS e cada palavra é tratada como um padrão curinga. Como você só definiu IFS para a duração do read (consulte Por que é 'enquanto IFS = lê' usado com tanta freqüência, ao invés de 'IFS =; enquanto lê ..'? ), o valor de IFS neste momento é o padrão, que inclui espaços. Além disso, se você tivesse um campo contendo algo como foo * , veria os nomes dos arquivos no diretório atual. A solução é for r in "${documents[@]}" , que é a maneira padrão de iterar em uma matriz: as aspas duplas transformam isso em uma desreferência de variável reta sem divisão e globbing, e a [@] faz com que cada elemento da matriz seja colocado em uma palavra separada .

Enquanto a configuração de IFS=$'\t' para todo o script parece resolver o problema, na verdade só resolve metade do problema: ele não impede que ocorra globbing com ${documents[@]} . Embora você possa desativar o globbing com set -f , é mais claro usar aspas duplas.

    
por 14.04.2016 / 01:53
1

Aparentemente, o problema estava nas várias declarações de IFS=$'\t' . Removê-los e ter apenas uma declaração para IFS parece ter resolvido o problema.

(Embora para a vida de mim, eu não vejo por que isso foi um problema. Deve ter havido um erro de digitação sutil.)

    
por 13.04.2016 / 17:37

Tags