Como faço para analisar uma string no bash em variáveis usando uma tab como delimitador e preservar espaços?

3

Eu estou no processo de escrever um script bash para exibir várias estatísticas, algumas das quais vêm de um banco de dados MySQL. O código pertinente com o qual estou tendo dificuldade é:

read min max rows <<< $(mysql $dbDatabase -u $dbUser -p$dbPass -B -N -e \
    "SELECT MIN(\'DateTime\'), MAX(\'DateTime\'), COUNT(*) FROM $dbTable")
echo "Min Date:" $min
echo "Max Date:" $max
echo "Total Rows:" $rows

O resultado da sua execução é:

Min Date: 2013-03-18
Max Date: 20:30:00
Total Rows: 2014-07-31 14:30:00 11225139

O que obviamente não é o que pretendo (o valor de DateTime foi dividido)

De acordo com o Google, $IFS deve ser a resposta para o meu problema. Infelizmente, ainda não consegui analisar os resultados corretamente.

IFS=$'\t' read min max rows <<< ...

O resultado é:

Min Date: 2013-03-18 20:30:00 2014-07-31 14:30:00 11225139
Max Date:
Total Rows:

Acho isso estranho, pois msyql -B | tr $'\t' 'X' prova visualmente que os campos são separados por guias.

Como solução alternativa, posso fazer algo como o seguinte para produzir a saída desejada:

read min max rows <<< $(mysql $dbDatabase -u $dbUser -p$dbPass -B -N -e \
    "SELECT MIN(\'DateTime\'), MAX(\'DateTime\'), COUNT(*) FROM $dbTable" \
    | tr ' ' '.')
min=$(tr '.' ' ' <<< $min)
max=$(tr '.' ' ' <<< $max)

echo "Min Date:" $min
echo "Max Date:" $max
echo "Total Rows:" $rows

Mas isso dificilmente parece elegante ou o "jeito unix".

Alguém sabe o que estou fazendo de errado e porque $IFS não está funcionando para mim, e qual é o uso correto de código limpo (não-hacky) e compreensível?

    
por Timothy 31.07.2014 / 21:33

1 resposta

2

Você precisa de aspas em torno do resultado da substituição do comando, por exemplo, "$(command)" , por exemplo, (simulando sua consulta com um echo ):

$ IFS=$'\t' read min max rows <<< "$(echo -e "Wed Jul 30 15:40:38 EDT 2014\tThu Jul 31 15:40:38 EDT 2014\t27")"
$ echo "$min"; echo "$max"
Wed Jul 30 15:40:38 EDT 2014
Thu Jul 31 15:40:38 EDT 2014

ou use uma (mais direta) substituição de processo em vez

$ IFS=$'\t' read min max rows < <(echo -e "Wed Jul 30 15:40:38 EDT 2014\tThu Jul 31 15:40:38 EDT 2014\t27")
$ echo "$min"; echo "$max"
Wed Jul 30 15:40:38 EDT 2014
Thu Jul 31 15:40:38 EDT 2014
    
por 31.07.2014 / 21:50