Converter linhas em colunas

10

Eu tenho um arquivo que inclui detalhes sobre VMs em execução em um hipervisor. Nós 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 hipervisor para hipervisor, pois em alguns hipervisores temos 50 + vms em execução. O arquivo acima é apenas um exemplo do hipervisor em que temos apenas 3 VMs em execução e, portanto, espera-se que o arquivo redirecionado contenha informações sobre vários (N número de VMs)

Precisamos obter esses detalhes no formato abaixo usando o 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
    
por IgniteLX 28.01.2016 / 20:46

9 respostas

1

Se andar duas vezes no arquivo 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 muitos passeios 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 a tabulação \t como separador de campos):

./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á a matriz inteira 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 do awk são a melhor resposta. Uma passagem, tão curta quanto os campos, imprimirá os títulos dos campos de cabeçalho. A próxima passagem awk irá imprimir único campo 2. Em ambos os casos, eu adicionei uma maneira de remover esquerda e à direita espaços (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 { ... } | column -t -s "$(printf '%b' '\t')" ao redor é formatar a tabela inteira de maneira bonita.
Por favor, note que o "$(printf '%b' '\t')" poderiam ser substituídos por $'\t' no ksh, bash, ou zsh.

    
por 28.01.2016 / 22:36
7

Se você tiver o utilitário rs (reshape) disponível, você pode fazer o seguinte:

rs -Tzc: < input.txt

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

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

Isso funciona para tabelas com tamanhos arbitrários, 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
$ 

rs está 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
    
por 29.01.2016 / 00:27
6

EDIT: Extensível a qualquer número de linhas de saída, em um simples loop de for de uma linha direta:

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 bash substituição de processo:

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

A opção -s para paste faz com que ele manipule cada arquivo, um de cada vez. O : delimitador definido em paste é "capturado" pela opção -s para column no final, para melhorar o formato ao alinhar os campos.

Os comandos cut nas duas substituições do processo extraem o primeiro campo e o segundo campo, respectivamente.

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

Input - conteúdo do arquivo chamado "input" 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

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
    
por 28.01.2016 / 21:27
2

Usando o awk, armazenar fora a chave e valor e imprimi-los 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

    
por 28.01.2016 / 21:09
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
    
por 28.01.2016 / 21:14
1

Com gnu datamash e column de util-linux :

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

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

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

para evitar que você precise espremê-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 zsh e 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[@]%:*} associa (com : ) o primeiro campo de cada elemento e ${(j;:;)list[@]#*:} associa (novamente com : ) o segundo campo de cada elemento; estes são ambos impressos, e. 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:

    
por 28.01.2016 / 22:50
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 neste caso - 11. Será melhor contá-lo de antemão e armazenar na variável e, em seguida, usar essa variável no código.

Explicação

  1. cat <(command 1) <(command 2) - <() construção torna command saída aparecendo como um arquivo temporário. Portanto, cat concatena dois arquivos e canaliza ainda mais.

    • comando 1 : head -n 11 virtual.txt | cut -d: -f1 , nos dá cabeçalhos de coluna futuros. A única entrada da Máquina Virtual é onze primeiras linhas, o comando head é usado para obtê-la. O cut divide essa entrada em duas colunas e imprime a primeira delas.
    • comando 2 : sed 's/.*: //' virtual.txt - nos dá valores de coluna futuros. sed remove 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 por 11 por linha.

  3. column -t - é necessário para telas bonitas. Exibe nossas linhas em um formulário de tabela. Caso contrário, cada linha terá uma 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
    
por 20.07.2017 / 20:32
0

Use a opção datamash e sua transpose para trocar linhas e colunas em um arquivo.

datamash -t: transpose < infile.txt

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

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

Você também pode usar --filler para definir o valor de preenchimento do campo em falta:

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

derivado de datamash manual

    
por 12.08.2017 / 10:16
-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

você pode precisar massagear o número de caracteres \t (tab) na linha printf se os valores das variáveis forem de diferentes comprimentos.

    
por 28.01.2016 / 21:04