Converter linhas em colunas


10

Eu tenho um arquivo que inclui detalhes sobre VMs em execução em um hipervisor. Executamos algum comando e redirecionamos a saída para um arquivo. E os dados estão disponíveis no formato abaixo.

Virtual Machine : OL6U5
        ID     : 0004fb00000600003da8ce6948c441bb
        Status : Running
        Memory : 65536
        Uptime : 17835 Minutes
        Server : MyOVS1.vmorld.com
        Pool   : HA-POOL
        HA Mode: false
        VCPU   : 16
        Type   : Xen PVM
        OS     : Oracle Linux 6
Virtual Machine : OL6U6
        ID     : 0004fb00000600003da8ce6948c441bc
        Status : Running
        Memory : 65536
        Uptime : 17565 Minutes
        Server : MyOVS2.vmorld.com
        Pool   : NON-HA-POOL
        HA Mode: false
        VCPU   : 16
        Type   : Xen PVM
        OS     : Oracle Linux 6
Virtual Machine : OL6U7
        ID     : 0004fb00000600003da8ce6948c441bd
        Status : Running
        Memory : 65536
        Uptime : 17835 Minutes
        Server : MyOVS1.vmorld.com
        Pool   : HA-POOL
        HA Mode: false
        VCPU   : 16
        Type   : Xen PVM
        OS     : Oracle Linux 6

Essa saída difere de hypervisor para hypervisor, pois em alguns hipervisores temos 50 + vms em execução. O arquivo acima é apenas um exemplo do hypervisor, onde temos apenas 3 VMs em execução e, portanto, o arquivo redirecionado deve conter informações sobre várias (N número de VMs)

Precisamos obter esses detalhes no formato abaixo usando awk / sed ou com um script de shell

Virtual_Machine  ID                                Status   Memory  Uptime  Server              Pool        HA     VCPU  Type     OS
OL6U5            0004fb00000600003da8ce6948c441bb  Running  65536   17835   MyOVS1.vmworld.com  HA-POOL     false  16    Xen PVM  Oracle Linux 6
OL6U6            0004fb00000600003da8ce6948c441bc  Running  65536   17565   MyOVS2.vmworld.com  NON-HA-POOL     false  16    Xen PVM  Oracle Linux 6
OL6U5            0004fb00000600003da8ce6948c441bd  Running  65536   17835   MyOVS1.vmworld.com  HA-POOL     false  16    Xen PVM  Oracle Linux 6

Respostas:


1

Se andar o arquivo duas vezes não for um problema (grande) (armazenará apenas uma linha na memória):

awk -F : '{printf("%s\t ", $1)}' infile
echo
awk -F : '{printf("%s\t ", $2)}' infile

Qual, para uma contagem geral de campos, seria (o que poderia ter várias etapas do arquivo):

#!/bin/bash
rowcount=2
for (( i=1; i<=rowcount; i++ )); do
    awk -v i="$i" -F : '{printf("%s\t ", $i)}' infile
    echo
done

Mas, para uma transposição realmente geral, isso funcionará:

awk '$0!~/^$/{    i++;
                  split($0,arr,":");
                  for (j in arr) {
                      out[i,j]=arr[j];
                      if (maxr<j){ maxr=j} # max number of output rows.
                  }
            }
    END {
        maxc=i                             # max number of output columns.
        for     (j=1; j<=maxr; j++) {
            for (i=1; i<=maxc; i++) {
                printf( "%s\t", out[i,j])  # out field separator.
            }
            printf( "%s\n","" )
        }
    }' infile

E para torná-lo bonito (usando tab \tcomo separador de campo):

./script | |column -t -s $'\t'

Virtual_Machine  ID                                Status   Memory  Uptime  Server              Pool     HA     VCPU  Type     OS
OL6U7            0004fb00000600003da8ce6948c441bd  Running  65536   17103   MyOVS1.vmworld.com  HA-POOL  false  16    Xen PVM  Oracle Linux 6

O código acima para uma transposição geral armazenará toda a matriz na memória.
Isso pode ser um problema para arquivos realmente grandes.


Atualização para novo texto.

Para processar o novo texto postado na pergunta, parece-me que duas passagens de awk são a melhor resposta. Uma passagem, tão curta quanto os campos, imprimirá os títulos dos campos do cabeçalho. A próxima passagem do awk imprimirá apenas o campo 2. Nos dois casos, adicionei uma maneira de remover espaços iniciais e finais (para melhor formatação).

#!/bin/bash
{
awk -F: 'BEGIN{ sl="Virtual Machine"}
         $1~sl && head == 1 { head=0; exit 0}
         $1~sl && head == 0 { head=1; }
         head == 1 {
             gsub(/^[ \t]+/,"",$1);   # remove leading  spaces
             gsub(/[ \t]+$/,"",$1);   # remove trailing spaces
             printf( "%s\t", $1)
         }
         ' infile
#echo
awk -F: 'BEGIN { sl="Virtual Machine"}
         $1~sl { printf( "%s\n", "") }
         {
             gsub(/^[ \t]+/,"",$2);   # remove leading  spaces
             gsub(/[ \t]+$/,"",$2);   # remove trailing spaces
             printf( "%s\t", $2)
         }
         ' infile
echo
} | column -t -s "$(printf '%b' '\t')"

O ambiente { ... } | column -t -s "$(printf '%b' '\t')"é formatar a tabela inteira de uma maneira bonita.
Observe que ele "$(printf '%b' '\t')"pode ser substituído por $'\t'ksh, bash ou zsh.


8

Se você tiver o rsutilitário (remodelar) disponível, faça o seguinte:

rs -Tzc: < input.txt

Isso fornece o formato de saída exatamente como especificado na pergunta, até as larguras dinâmicas da coluna.

  • -T Transpõe os dados de entrada
  • -z dimensiona as colunas adequadamente do máximo em cada coluna
  • -c: usa dois pontos como o separador de campos de entrada

Isso funciona para tabelas de tamanho arbitrário, por exemplo:

$ echo "Name:Alice:Bob:Carol
Age:12:34:56
Eyecolour:Brown:Black:Blue" | rs -Tzc: 
Name   Age  Eyecolour
Alice  12   Brown
Bob    34   Black
Carol  56   Blue
$ 

rsestá disponível por padrão no OS X (e provavelmente em outras máquinas BSD). Pode ser instalado no Ubuntu (e na família debian) com:

sudo apt-get install rs

6

EDIT: Extensível para qualquer número de linhas de saída, em um forloop simples de uma linha :

for ((i=1;i<=2;i++)); do cut -d: -f "$i" input | paste -sd: ; done | column -t -s:

Resposta original:

Você pode fazer isso como uma linha única usando a bashsubstituição do processo:

paste -sd: <(cut -d: -f1 input) <(cut -d: -f2 input) | column -t -s:

A -sopção para pastelidar com cada arquivo, um de cada vez. O :delimitador definido pasteé "capturado" pela -sopção columnno final, para aprimorar o formato, alinhando os campos.

Os cutcomandos nas duas substituições de processo extraem o primeiro campo e o segundo campo, respectivamente.

Se há ou não linhas em branco na entrada, não importa, pois column -t -s:a saída será limpa independentemente. (Havia linhas em branco na entrada original especificada na pergunta, mas elas foram removidas. O comando acima funciona independentemente das linhas em branco.)

Entrada - conteúdo do arquivo chamado "entrada" no comando acima:

Virtual_Machine:OL6U7

ID:0004fb00000600003da8ce6948c441bd

Status:Running

Memory:65536

Uptime:17103

Server:MyOVS1.vmworld.com

Pool:HA-POOL

HA:false

VCPU:16

Type:Xen PVM

OS:Oracle Linux 6

Resultado:

Virtual_Machine  ID                                Status   Memory  Uptime  Server              Pool     HA     VCPU  Type     OS
OL6U7            0004fb00000600003da8ce6948c441bd  Running  65536   17103   MyOVS1.vmworld.com  HA-POOL  false  16    Xen PVM  Oracle Linux 6

2
Isso funciona para duas linhas de saída, mas, para mais linhas, torna-se difícil.

2

Usando o awk, armazene a chave e o valor e imprima-os no final.

#!/usr/bin/awk -f
BEGIN {
  CNT=0
  FS=":"
}

{
  HDR[CNT]=$1;
  ENTRY[CNT]=$2;
  CNT++;
}

END {
  for (x = 0; x < CNT; x++)
    printf "%s\t",HDR[x]

  print""

  for (x = 0; x < CNT; x++)
    printf "%s\t",ENTRY[x]
  }

A apenas correr awk -f ./script.awk ./input.txt


A resposta foi dinâmica. Só requer que exista apenas 1 VM de dados por arquivo.
jecxjo

1
declare -a COLS
declare -a DATA
while IFS=':' read -ra fields; do
   COLS+=("${fields[0]}")
   DATA+=("${fields[1]}")
done < <( cat /path/to/input.txt)

HEADER=""
DATA=""
for i in $(seq 0 $((${#fields[@]}-1)); do
    HEADER="${HEADER}${COLS[$i]} "
    DATA="${DATA}${COLS[$i]} "
done
echo $HEADER
echo $DATA

1

Com gnu datamashe columnde util-linux:

datamash -t: transpose <infile | column -t -s:

Isso funciona com mais de duas colunas, mas assume que não há linhas vazias no seu arquivo de entrada; com linhas vazias no meio (como em sua amostra de entrada inicial), você receberá um erro como:

datamash: transpose input error: line 2 has 0 fields (previous lines had 2);

para evitar que você precise apertá-los antes de processar com datamash:

tr -s \\n <infile | datamash -t: transpose | column -t -s:

Caso contrário, neste caso específico (apenas duas colunas), com zshe o mesmo column:

list=(${(f)"$(<infile)"})
printf %s\\n ${(j;:;)list[@]%:*} ${(j;:;)list[@]#*:} | column -t -s:

(${(f)"$(<infile)"})lê as linhas em uma matriz; ${(j;:;)list[@]%:*}junta (com :) o primeiro campo de cada elemento e ${(j;:;)list[@]#*:}junta (novamente com :) o segundo campo de cada elemento; ambos são impressos, por exemplo, a saída é

Virtual_Machine:ID:Status:Memory:Uptime:Server:Pool:HA:VCPU:Type:OS
OL6U7:0004fb00000600003da8ce6948c441bd:Running:65536:17103:MyOVS1.vmworld.com:HA-POOL:false:16:Xen PVM:Oracle Linux 6

que é então canalizado para column -t -s:


0

cat <(head -n 11 virtual.txt | cut -d: -f1) <(sed 's/.*: //' virtual.txt) | xargs -d '\n' -n 11 | column -t

O número de linhas por máquina virtual é codificado nesse caso - 11. Será melhor contá-lo com antecedência e armazenar na variável, depois use essa variável no código.

Explicação

  1. cat <(command 1) <(command 2)- <()construção faz com que a commandsaída pareça um arquivo temporário. Portanto, catconcatena dois arquivos e os canaliza ainda mais.

    • comando 1 : head -n 11 virtual.txt | cut -d: -f1, dá-nos cabeçalhos das colunas futuras. A única entrada de Máquina Virtual são as primeiras onze linhas, o headcomando é usado para obtê-la. O cutdivide esta entrada em duas colunas e imprime a única primeira.
    • comando 2 : sed 's/.*: //' virtual.txt- nos fornece valores futuros da coluna. sedremove todo o texto desnecessário e deixa apenas valores.
  2. xargs -d '\n' -n 11. Cada item de entrada é finalizado por nova linha. Este comando obtém itens e os imprime 11 por linha.

  3. column -t- é necessário para telas bonitas de impressão. Ele exibe nossas linhas em forma de tabela. Caso contrário, cada linha terá largura diferente.

Resultado

Virtual  Machine                           ID       Status  Memory  Uptime   Server             Pool         HA     Mode  VCPU  Type  OS
OL6U5    0004fb00000600003da8ce6948c441bb  Running  65536   17835   Minutes  MyOVS1.vmorld.com  HA-POOL      false  16    Xen   PVM   Oracle  Linux  6
OL6U6    0004fb00000600003da8ce6948c441bc  Running  65536   17565   Minutes  MyOVS2.vmorld.com  NON-HA-POOL  false  16    Xen   PVM   Oracle  Linux  6
OL6U7    0004fb00000600003da8ce6948c441bd  Running  65536   17835   Minutes  MyOVS1.vmorld.com  HA-POOL      false  16    Xen   PVM   Oracle  Linux  6

0

Use datamashe sua transposeopção para trocar linhas e colunas em um arquivo.

datamash -t: transpose < infile.txt

Por padrão, a transposição verifica se a entrada tem o mesmo número de campos em cada linha e falha com um erro caso contrário, e você pode desativar seu modo estrito para permitir valores ausentes por --no-strict

datamash -t: --no-strict transpose < infile.txt

Além disso, você pode usar --fillerpara definir o valor do preenchimento do campo ausente:

datamash -t: --no-strict --filler " " transpose < infile.txt

derivado de datamash manual


-5

se seus dados estiverem em arquivos separados em um diretório, você poderá usar:

for file in $(ls $DIRECTORY)
do
  cat ${file} | while read line
  do
    value=$(echo $line | cut -d: -f2-)
    printf "%s\t" "${value}" >> bigfile
  done
  echo " " >> bigfile
done

pode ser necessário massagear o número de \tcaracteres (tab) na printflinha se os valores das variáveis ​​tiverem comprimentos diferentes.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.