Dê uma olhada em GNU datamash , que pode ser usado como datamash transpose
.
Uma versão futura também suportará tabulação cruzada (tabelas dinâmicas)
Eu tenho um arquivo com as linhas abaixo.
title1:A1
title2:A2
title3:A3
title4:A4
title5:A5
title1:B1
title2:B2
title3:B3
title4:B4
title5:B5
title1:C1
title2:C2
title3:C3
title4:C4
title5:C5
title1:D1
title2:D2
title3:D3
title4:D4
title5:D5
Como posso conseguir isso?
title1 title2 title3 title4
A1 A2 A3 A4
B1 B2 B3 B4
C1 C2 C3 C4
D1 D2 D3 D4
Dê uma olhada em GNU datamash , que pode ser usado como datamash transpose
.
Uma versão futura também suportará tabulação cruzada (tabelas dinâmicas)
Você pode usar awk
para processar os dados, em seguida, paste
e column
para formatá-los.
Aqui, assumo que title1
é apenas um exemplo em sua postagem e que os dados não contêm :
, exceto como separador entre cabeçalho + dados.
n
significa quantas colunas devem ser impressas (deve corresponder a traços em paste
).
awk -F":" -v n=4 \
'BEGIN { x=1; c=0;}
++c <= n && x == 1 {print $1; buf = buf $2 "\n";
if(c == n) {x = 2; printf buf} next;}
!/./{c=0;next}
c <=n {printf "%s\n", $2}' datafile | \
paste - - - - | \
column -t -s "$(printf "\t")"
Se você quiser torná-lo mais flexível e fácil de manter, poderá escrevê-lo como um script. Aqui está um exemplo usando o wrapper bash para awk
e canalizado para column
. Dessa forma, você também pode fazer mais verificações de dados, como por exemplo certificando-se de que os cabeçalhos estão corretos em todas as linhas, etc.
Usado normalmente como:
$ ./trans -f data -c 4
title one title two title three title four
A1 A2 A3 A4
B1 B2 B3 B4
C1 C2 C3 C4
D1 D2 D3 D4
Se os cabeçalhos forem sempre mais curtos que os dados, você também poderá salvar as larguras dos cabeçalhos, em seguida, printf
com %-*s
e ignorar column
todos juntos.
#!/bin/bash
trans()
{
awk -F":" -v ncol="$1" '
BEGIN {
level = 1 # Run-level.
col = 1 # Current column.
short = 0 # If requested to many columns.
}
# Save headers and data for row one.
level == 1 {
head[col] = $1
data[col] = $2
if (++col > ncol) { # We have number of requested columns.
level = 2
} else if ($0 == "") { # If request for more columns then available.
level = 2
ncol = col - 2
short = 1
} else {
next
}
}
# Print headers and row one.
level == 2 {
for (i = 1; i <= ncol; ++i)
printf("%s\t", head[i])
print ""
for (i = 1; i <= ncol; ++i)
printf("%s\t", data[i])
level = 3
col = ncol + 1
if (!short)
next
}
# Empty line, new row.
! /./ { print ""; col = 1; next }
# Next cell.
col > ncol {next}
{
printf "%s%s", $2, (col <= ncol) ? "\t" : ""
++col
}
END {print ""}
' "$2"
}
declare -i ncol=4 # Columns defaults to four.
file="" # Data file (or pipe).
while [[ -n "$1" ]]; do
case "$1" in
"-c") ncol="$2"; shift;;
"-f") file="$2"; shift;;
*) printf "Usage: %s [-c <columns>] [-f <file> | pipe]\n" \
"$(basename $0)" >&2;
exit;;
esac
shift
done
trans "$ncol" "$file" | column -t -s "$(printf "\t")"
Aqui está uma maneira rápida de colocar o arquivo no formato desejado:
$ grep -Ev "^$|title5" sample.txt | sed 's/title[0-9]://g' | paste - - - -
A1 A2 A3 A4
B1 B2 B3 B4
C1 C2 C3 C4
D1 D2 D3 D4
Se você quiser os cabeçalhos das colunas:
$ grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t'; \
echo ""; \
grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -
title1 title2 title3 title4
A1 A2 A3 A4
B1 B2 B3 B4
C1 C2 C3 C4
D1 D2 D3 D4
grep -Ev "^$|title5" sample.txt | sed 's/:.*//' | sort -u | tr '\n' '\t';
colocando um retorno após o banner
echo
imprimindo as linhas de dados
grep -Ev "^$|title5" a | sed 's/title[0-9]://g' | paste - - - -
Fora da rolagem de uma solução personalizada para transpor linhas com colunas de uma linha de comando, a única ferramenta que eu já vi que pode fazer isso é uma ferramenta chamada ironicamente transpose
.
Infelizmente, não está em nenhum repositório, então você precisará fazer o download e compilá-lo. Isso é bastante simples, pois não possui bibliotecas adicionais das quais depende. Isso pode ser feito da seguinte forma:
$ gcc transpose.c -o transpose
Ele pode lidar com arquivos de texto simples com facilidade. Por exemplo:
$ cat simple.txt
X column1 column2 column3
row1 0 1 2
row2 3 4 5
row3 6 7 8
row4 9 10 11
Pode ser transposto usando este comando:
$ transpose -t --fsep " " simple.txt
X row1 row2 row3 row4
column1 0 3 6 9
column2 1 4 7 10
column3 2 5 8 11
Este comando é transpose
para transpor ( -t
) e o separador de campo para usar é um espaço ( --fsep " "
).
Como seus dados de amostra estão em um formato um pouco mais complexo, eles precisam ser tratados em duas fases. Primeiro, precisamos traduzi-lo para um formato com o qual o transpose
possa lidar.
A execução deste comando colocará os dados em um formato mais horizontalmente amigável:
$ sed 's/:/ /; /^$/d' sample.txt \
| sort | paste - - - - -
title1 A1 title1 B1 title1 C1 title1 D1 title2 A2
title2 B2 title2 C2 title2 D2 title3 A3 title3 B3
title3 C3 title3 D3 title4 A4 title4 B4 title4 C4
title4 D4 title5 A5 title5 B5 title5 C5 title5 D5
Agora só precisamos remover as ocorrências secundárias do title1, title2, etc.:
$ sed 's/:/ /; /^$/d' sample.txt \
| sort | paste - - - - - | sed 's/\ttitle[0-9] / /g'
title1 A1 B1 C1 D1 A2
title2 B2 C2 D2 A3 B3
title3 C3 D3 A4 B4 C4
title4 D4 A5 B5 C5 D5
Agora ele está em um formato com o qual transpose
pode lidar. O seguinte comando fará toda a transposição:
$ sed 's/:/ /; /^$/d' sample.txt \
| sort | paste - - - - - | sed 's/\ttitle[0-9] / /g' \
| transpose -t --fsep " "
title1 title2 title3 title4
A1 B2 C3 D4
B1 C2 D3 A5
C1 D2 A4 B5
D1 A3 B4 C5
A2 B3 C4 D5
Há provavelmente uma maneira mais sucinta de formular isso, mas isso parece conseguir o efeito geral:
[jadavis84@localhost ~]$ sed 's/^title[2-9]://g' file.txt | tr '\n' '\t' | sed 's/title1:/\n/g' ; echo
A1 A2 A3 A4 A5
B1 B2 B3 B4 B5
C1 C2 C3 C4 C5
D1 D2 D3 D4 D5
[jadavis84@localhost ~]$
Múltiplas invocações de sed
não parecem corretas (e eu tenho certeza que sed também pode fazer a nova tradução de linha), então provavelmente não é a maneira mais direta de fazer isso. Além disso, isso retira os cabeçalhos em potencial, mas você pode gerá-los manualmente depois de ter as linhas / campos formatados corretamente.
Uma resposta melhor provavelmente destilaria esse efeito apenas usando sed
ou awk
para fazer isso para que você tenha apenas uma coisa acontecendo de cada vez. Mas eu estou cansada, então é isso que eu pude montar.
paste
é provavelmente sua melhor aposta. Você pode extrair os bits relevantes com cut
, grep
e awk
assim:
(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile)
Se a quinta coluna for eliminada, anexe awk 'NR%5'
da seguinte forma:
(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5'
Agora colabore com paste
:
(awk 'NR==1' RS= infile | cut -d: -f1; cut -sd: -f2 infile) | awk 'NR%5' | paste - - - -
Saída:
title1 title2 title3 title4
A1 A2 A3 A4
B1 B2 B3 B4
C1 C2 C3 C4
D1 D2 D3 D4
Para apenas a parte de transposição, tive um problema semelhante recentemente e usei:
awk -v fmt='\t%4s' '{ for(i=1;i<=NF;i++){ a[i]=a[i] sprintf(fmt, $i); } } END { for (i in a) print a[i]; }'
Ajuste o fmt conforme necessário. Para cada linha de entrada, ela concatena cada campo em um elemento de matriz. Note que a concatenação de strings awk é implícita: acontece quando você escreve duas coisas sem nenhum operador.
Amostra de E / S:
i mark accep igna utaal bta
-22 -10 -10 -20 -10 -10
-21 -10 -10 -20 -10 -10
-20 -10 -10 -20 -10 -10
-19 -10 0 -10 -10 -10
-18 0 0 -10 0 0
-12 0 0 -10 0 0
-11 0 0 -10 0 0
-10 0 0 -10 0 0
saída:
i -22 -21 -20 -19 -18 -12 -11 -10
mark -10 -10 -10 -10 0 0 0 0
accep -10 -10 -10 0 0 0 0 0
igna -20 -20 -20 -10 -10 -10 -10 -10
utaal -10 -10 -10 -10 0 0 0 0
bta -10 -10 -10 -10 0 0 0 0
A coisa mais simples que você pode fazer é usar cut
para cortar os campos e usar tr
se estiver transpondo linhas para colunas substituindo o caractere de nova linha por um caractere de tabulação: link
cat file.txt | cut -d':' | tr '\n' '\t'
Tags text-processing columns