O resultado é o comando que segue:
head -1 arquivo.csv | \
grep -Po ';' | \
cat -n | \
grep -Po '\d+' | \
xargs -I'{}' bash -c "cut -d';' -f'{}' arquivo.csv | \
awk 'length(\$0) > max { max=length(\$0) } END { print max }'"
Os passos são:- Pegar a primeira linha (o cabeçalho);
- Elimina todos os caracteres exceto o separadores (para contar as colunas);
- Numera as colunas;
- Elimina os separadores para deixar apenas os números das colunas;
- Para cada coluna, executa um comando composto que retira a enésima coluna e imprime a largura do valor mais largo.
Então, para um cabeçalho do tipo COL1;COL2;COL3, os comandos de 1 a 4 produzem o seguinte:
% head -1 arquivo.csv | grep -Po ';' | cat -n | grep -Po '\d+'
1
2
3
Depois, o xargs vai executar os seguintes comandos:
bash -c cut -d';' -f'1' arquivo.csv | \
awk 'length($0) > max { max=length($0) } END { print max }'
bash -c cut -d';' -f'2' arquivo.csv | \
awk 'length($0) > max { max=length($0) } END { print max }'
bash -c cut -d';' -f'3' arquivo.csv | \
awk 'length($0) > max { max=length($0) } END { print max }'
E o resultado final será uma lista de larguras:
10
25
100
Para facilitar a leitura, dá para adicionar o número da coluna com um echo bem posicionado:
head -1 arquivo.csv | \
grep -Po ';' | \
cat -n | \
grep -Po '\d+' | \
xargs -I'{}' bash -c "echo -n '{}: '; cut -d';' -f'{}' arquivo.csv | \
awk 'length(\$0) > max { max=length(\$0) } END { print max }'"
E o resultado sairá assim:
1: 10
2: 25
3: 100
Se faltar uma coluna, basta adicionar uma ao primeiro comando:
bash -c "echo -n ';' && head -1 arquivo.csv"
Ou, sendo mais prático, basta usar o número de colunas e evitar a contagem:
seq 1 3 | \
xargs -I'{}' bash -c "cut -d';' -f'{}' arquivo.csv | \
awk 'length(\$0) > max { max=length(\$0) } END { print max }'"
O cabeçalho pode gerar problemas, quando suas colunas forem maiores que os dados propriamente ditos. A solução é usar o tail para pular a primeira linha.
seq 1 3 | \
xargs -I'{}' bash -c "tail -n +2 arquivo.csv | \
cut -d';' -f'{}' | \
awk 'length(\$0) > max { max=length(\$0) } END { print max }'"
Isso vai falhar se o arquivo tiver campos com quebra de linha. Então, uma solução mais robusta pode ser obtida com um pouco de perl.
#!/usr/bin/perl
use Text::CSV_PP;
use List::Util qw(max);
my @max=();
my $csv=Text::CSV_PP->new({sep_char=>';',auto_diag=>1,binary=>1});
open(my $fh, '<:encoding(UTF-8)', $ARGV[0]) or die "Can't read file '$file' [$!]\n";
<$fh>; #Ignore header
while (my $line = $csv->getline($fh)) {
my @fields=@$line;
$max[$_]=max(length($fields[$_]),$max[$_]) for 0..$#fields;
};
print "@max\n";
Esse script recebe um único parâmetro: o nome de um arquivo. Ele percorre todas as linhas, exceto a primeira (ignorando o cabeçalho).