Mesclando colunas em um arquivo usando o Awk

5

Entrada:

ABC,SSSD,12345,NSS,12345,xxx,TS11
,,,,,,TS21
,,,,,,TS22
,,,,,,BS26
,,,,,,GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11
,,,,,,TS21
,,,,,,TS22
,,,,,,GPRS
,,,,,,BS26

Saída:

ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26

Eu já tentei escrevendo o script abaixo. Mas é um desempenho intensivo:

#!/bin/bash
head -2 $1 | tail -1 >> $1"_output"
sed '1,2d' $1 > temp$1.txt
cp temp$1.txt $1
rm -f temp$1.txt
while IFS='' read -r line || [[ -n $line ]]; do
    #    echo "main line -- "$line
    #    echo "prev line -- "$prevLine
    service='echo $line | cut -d "," -f1'
    value='echo $line | cut -d "," -f7'
    if [ "$service" != "" ]
    then
        if [ "$prevLine" != "" -a "$mvalue" != "" ]
        then
            echo $prevLine$mvalue >> $1"_output"
        fi
        prevLine=$line
        mvalue=""
    else
        #echo $value
        mvalue=$mvalue"|"$value
        #echo $mvalue
    fi
done < "$1"
echo $prevLine$mvalue >> $1"_output"

Alguém pode sugerir uma maneira melhor de fazer isso usando o awk ou qualquer método melhor?

    
por Vineeth 19.08.2015 / 09:06

5 respostas

5

Se você não é muito exigente com o espaço em branco, você faz isso com duas regras do awk como esta:

 awk '$1 { printf "\n" $0 } !$1 { printf OFS $7 }' FS=, OFS='|' infile

Isso deixa uma linha vazia na cabeça da saída e erra uma no final. Para corrigir isso, adicione if e END cláusulas:

awk '$1 { if(NR>1) printf "\n"; printf $0 } !$1 { printf OFS $7 } END { printf "\n" }' FS=, OFS='|' infile

Saída:

ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26

Editar - observe sobre% em $ 0

Como Glenn observou nos comentários, se suas linhas contiverem % , será mais seguro chamar printf com uma string de formato, por exemplo:

awk '$1 { if(NR>1) printf "\n"; printf "%s", $0 } !$1 { printf OFS"%s", $7 } END { printf "\n" }' FS=, OFS='|' infile
    
por 19.08.2015 / 09:29
3

Contanto que a quantidade de linhas a serem mescladas seja constante, awk simplesmente pode decidir o tipo de linha que está olhando o número da linha:

$ awk -F, '1==NR%5 { printf $0 "|";next } { printf $7 (NR%5?"|":"\n") }' DATA
ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26
    
por 19.08.2015 / 09:38
2

se é sempre o mesmo número de linhas (5), uma combinação de paste e sed pode fazer:

paste -d',' - - - - - <file | sed 's/,\{2,\}/|/g'

irá 1) paste 5 linhas para uma usando , como delimitador, 2) substituindo várias (2 ou mais) vírgulas por um canal

    
por 19.08.2015 / 10:45
2

Outro sed :

sed ':;N;s/\n,,,*/|/;t;P;D' file
    
por 19.08.2015 / 13:34
2

Resposta semelhante à de Thor, mas armazena a linha até a conclusão:

awk -F, '
    $1 != "" {if (record) print record; record = $0} 
    $1 == "" {record = record "|" $NF} 
    END {if (record) print record}
' file

Se você quiser usar o bash, então você pode fazer

lines=()
while IFS= read -r line; do 
    if [[ ${line:0:1} == "," ]]; then   # the line starts a comma
        lines[-1]+="|${line##*,}"       # strip the commas and append to
                                        # the last element of the array
    else
        lines+=("$line")                # append the line to the array
    fi
done <<"$1"
printf "%s\n" "${lines[@]}"
ABC,SSSD,12345,NSS,12345,xxx,TS11|TS21|TS22|BS26|GPRS
ABC,SSSD,12356,NSS,12356,xxx,TS11|TS21|TS22|GPRS|BS26
    
por 19.08.2015 / 14:39