Tenho um arquivo com o seguinte formato, cada coluna separada por tabs:
C1 C2 C3
a b,c d
e f,g,h i
j k l
...
Agora preciso ter o número de linhas de acordo com o número de valores separados por vírgulas (se for o caso) na 2ª coluna. As linhas devem ter um dos valores e não os outros. O resultado seria este:
C1 C2 C3
a b d
a c d
e f i
e g i
e h i
j k l
...
...
Como isso é devido ao trabalho o mais rápido possível, acabei de fazer um script não faça isso em casa , lendo linha por linha com um while
, devido à minha falta de habilidade no awk
, ou não explorar outras soluções possíveis com outras ferramentas. O roteiro é o seguinte:
Estou revisando o roteiro enquanto isso
# DON'T DO THIS AT HOME SCRIPT
> duplicados.txt
while IFS= read -r line; do
# get the value of the column of interest
cues="$(echo "$line" | awk -F'\t' '{ print $18 }')"
# if the column has commas then it has multiple values
if [[ "$cues" =~ , ]]; then
# count the commas
c=$(printf "%s" "$cues" | sed 's/[^,]*//g' | wc -c)
# loop according to the number of commas
for i in $(seq $(($c + 1))); do
# get each value of the column of interest according to the position
cue="$(echo "$cues" | awk -F',' -v c=$i '{ print $c; ++c }')"
# save the line to a file substituting the whole column for the value
echo "$line" | sed "s;$cues;$cue;" >> duplicados.txt
done
continue
fi
# save the single value lines
echo "$line" >> duplicados.txt
done < inmuebles.txt
Com isso obtenho o resultado desejado (até onde sei). Como você pode imaginar o script é lento e muito ineficiente. Como eu poderia fazer isso com awk
ou outras ferramentas?
Uma amostra dos dados reais é assim, sendo a coluna de interesse o número 18:
1409233 UNION VIAMONTE Estatal Provincial DGEP 3321 VIAMONTE -33.7447365;-63.0997115 Rural Aglomerado 140273900 140273900-ESCUELA NICOLAS AVELLANEDA
1402961 UNION SAN MARCOS SUD Estatal Provincial DGEA, DGEI, DGEP 3029, 3311, Z11 SAN MARCOS SUD -32.629557;-62.483976 / -32.6302699949582;-62.4824499999125 / -32.632417;-62.484932 Urbano 140049404, 140164000, 140170100, 140173100 140049404-C.E.N.M.A. N° 201 ANEXO SEDE SAN MARCOS SUD, 140164000-C.E.N.P.A. N° 13 CASA DE LA CULTURA(DOC:BERSANO), 140170100-ESCUELA HIPOLITO BUCHARDO, 140173100-J.DE INF. HIPOLITO BUCHARDO
1402960 UNION SAN ANTONIO DE LITIN Estatal Provincial DGEA, DGEI, DGETyFP 3029, TZONAXI, Z11 SAN ANTONIO DE LITIN 3601300101020009 360102097366 0250347 SI / SI -32.212126;-62.635999 / -32.2122558;-62.6360432 / -32.2131931096409;-62.6291815804363 Rural Aglomerado 140049401, 140313000, 140313300, 140483400, 140499800 140049401-C.E.N.M.A. N° 201 ANEXO SAN ANTONIO DE LITIN, 140313000-I.P.E.A. Nº 214. MANUEL BELGRANO, 140313300-J.DE INF. PABLO A. PIZZURNO, 140483400-C.E.N.P.A. DE SAN ANTONIO DE LITIN, 140499800-C.E.N.P.A. B DE SAN ANTONIO DE LITIN
Você pode fazer isso
awk
dividindo a coluna composta,
e fazendo um loop sobre o resultado:Talvez de forma mais clara, você poderia fazer isso com Miller - em particular, usando o verbo nest :
Mais compactamente
--explode --values --across-records --nested-fs ','
pode ser substituído por--evar ','
Como você também marcou a pergunta com
sed
, sinto-me convidado a adicionar umased
solução:(Nota: Para legibilidade eu usei
\n
para nova linha e\t
para tab como você pode fazer com GNUsed
. Para uma solução portátil, use uma barra invertida com uma nova linha real em vez de\n
e uma tabulação real para\t
, inserido ctrlVseguido por tab)As linhas com uma vírgula são copiadas para o espaço de espera, uma cópia é impressa com o que está antes da vírgula, a outra cópia vai para o próximo ciclo com a parte após a vírgula. Em detalhe:
s//\n/
h
espaço antigo antes de estragar a linhas/[^\t]*\n//
remove a parte até a primeira vírgulax
mudamos os bufferss/\n[^\t]*//p
remove a parte a partir da vírgula e a imprimeG
acrescenta o espaço de espera ao espaço de padrão. Isso pode conter vírgulas de adição, entãoD
remove a primeira linha (já impressa) e recomeça com o resto da linhaawk
(ouperl
noawk
modo) é provavelmente a melhor solução padrão, mas você pode fazer isso de forma razoavelmente eficiente na maioria dos shells, especialmente aqueles com arrays (ksh
,bash
,zsh
):Para shells antigos/limitados sem arrays, use os parâmetros posicionais como (pode variar):
Usando perl
perl
opções usadas:-perl
variáveis internas usadas: -Código Perl:-
Usando GNU sed no modo regex estendido (-E):
Esta é uma operação de progresso em um registro do que o sed está fazendo:
com bourne shell embutido usando array de parâmetros posicionais
resultado