Convertir filas en columnas

10

Tengo un archivo que incluye detalles sobre las máquinas virtuales que se ejecutan en un hipervisor. Ejecutamos algún comando y redirigimos la salida a un archivo. Y los datos están disponibles en el siguiente formato.

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

Esta salida difiere de hipervisor a hipervisor ya que en algunos hipervisores tenemos más de 50 vms en ejecución. El archivo anterior es solo un ejemplo del hipervisor en el que solo tenemos 3 máquinas virtuales en ejecución y, por lo tanto, se espera que el archivo redirigido contenga información sobre varias (N número de máquinas virtuales)

Necesitamos obtener estos detalles en el siguiente formato usando awk / sed o con un 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
IgniteLX
fuente
2
Posible duplicado de Filas a columna de conversión de archivo
αғsнιη

Respuestas:

1

Si recorrer el archivo dos veces no es un problema (grande) (almacenará solo una línea en la memoria):

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

Lo cual, para un recuento general de campos sería (que podría tener muchos recorridos del archivo):

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

Pero para una transposición realmente general, esto 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

Y para hacerlo bonito (usando la pestaña \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

El código anterior para una transposición general almacenará toda la matriz en la memoria.
Eso podría ser un problema para archivos realmente grandes.


Actualización para nuevo texto.

Para procesar el nuevo texto publicado en la pregunta, me parece que dos pasos de awk son la mejor respuesta. Una pasada, tan corta como existan campos, imprimirá los títulos de los campos de encabezado. El próximo pase awk imprimirá solo el campo 2. En ambos casos, agregué una forma de eliminar los espacios iniciales y finales (para un mejor formato).

#!/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')"

El entorno { ... } | column -t -s "$(printf '%b' '\t')"es formatear toda la tabla de una manera bonita.
Tenga en cuenta que "$(printf '%b' '\t')"podría reemplazarse por $'\t'ksh, bash o zsh.


fuente
8

Si tiene la rsutilidad (remodelar) disponible, puede hacer lo siguiente:

rs -Tzc: < input.txt

Esto proporciona el formato de salida exactamente como se especifica en la pregunta, incluso hasta los anchos de columna dinámicos.

  • -T Transpone los datos de entrada.
  • -z dimensiona las columnas adecuadamente desde el máximo en cada columna
  • -c: usa dos puntos como separador de campo de entrada

Esto funciona para tablas de tamaño arbitrario, por ejemplo:

$ 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á disponible de forma predeterminada en OS X (y probablemente en otras máquinas BSD). Se puede instalar en Ubuntu (y la familia Debian) con:

sudo apt-get install rs
Trauma digital
fuente
6

EDITAR: Extensible a cualquier número de filas de salida, en un simple forbucle de una línea :

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

Respuesta original:

Puede hacer esto como una línea usando la bashsustitución de procesos:

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

La -sopción pastehace que maneje cada archivo de uno en uno. El :delimitador establecido pasteestá "atrapado" por la -sopción al columnfinal, para mejorar el formato haciendo que los campos se alineen.

Los cutcomandos en las dos sustituciones de proceso extraen el primer campo y el segundo campo, respectivamente.

Si hay líneas en blanco en la entrada o no, no importa, ya column -t -s:que limpiará la salida independientemente. (Había líneas en blanco en la entrada original especificada en la pregunta, pero desde entonces se han eliminado. El comando anterior funciona independientemente de las líneas en blanco).

Entrada: contenido del archivo llamado "entrada" en el comando anterior:

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

Salida:

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
Comodín
fuente
2
Esto funciona para dos filas de salida, pero para más filas se vuelve difícil de manejar.
2

Usando awk, guarde la clave y el valor e imprímalos al 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]
  }

El solo corre awk -f ./script.awk ./input.txt

jecxjo
fuente
Cambió la respuesta para que sea dinámica. Solo requiere que haya solo 1 VM de datos por archivo.
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
DopeGhoti
fuente
1

Con gnu datamashy columndesde util-linux:

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

Esto funciona con más de dos columnas, pero supone que no hay líneas vacías en su archivo de entrada; con líneas vacías en el medio (como en su muestra de entrada inicial) obtendría un error como:

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

para evitar tener que apretarlos antes de procesar con datamash:

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

De lo contrario, en este caso particular (solo dos columnas), con zshy lo mismo column:

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

(${(f)"$(<infile)"})lee las líneas en una matriz; ${(j;:;)list[@]%:*}une (con :) el primer campo de cada elemento y ${(j;:;)list[@]#*:}une (nuevamente con :) el segundo campo de cada elemento; ambos están impresos, por ejemplo, la salida es

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 luego se canaliza a column -t -s:

revs don_crissti
fuente
0

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

El número de líneas por máquina virtual está codificado en este caso: 11. Será mejor contarlo de antemano y almacenarlo en la variable, luego usar esta variable en el código.

Explicación

  1. cat <(command 1) <(command 2)- la <()construcción hace que la commandsalida aparezca como un archivo temporal. Por lo tanto, catconcatena dos archivos y los canaliza aún más.

    • comando 1 : head -n 11 virtual.txt | cut -d: -f1nos da futuros encabezados de columna. La entrada de una máquina virtual son las primeras once líneas, el headcomando se usa para obtenerla. El cutdivide esta entrada a dos columnas e imprimir la única primera.
    • comando 2 : sed 's/.*: //' virtual.txtnos da valores de columna futuros. sedelimina todo el texto innecesario y deja solo valores.
  2. xargs -d '\n' -n 11. Cada elemento de entrada termina en nueva línea. Este comando obtiene elementos y los imprime en 11 por línea.

  3. column -t- es necesario para pantallas bonitas de impresión. Muestra nuestras líneas en forma de tabla. De lo contrario, cada línea tendrá un ancho diferente.

Salida

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
MiniMax
fuente
0

Use datamashy su transposeopción para intercambiar filas y columnas en un archivo.

datamash -t: transpose < infile.txt

De forma predeterminada, la transposición verifica que la entrada tiene el mismo número de campos en cada línea y, de lo contrario, falla con un error y puede deshabilitar su modo estricto para permitir valores perdidos por --no-strict

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

También puede usar --fillerpara establecer el valor de relleno de campo faltante:

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

derivado de datamash manual

αғsнιη
fuente
-5

si sus datos están en archivos separados en un directorio, puede 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

Es posible que necesite masajear el número de caracteres \t(tabulación) en la printflínea si sus valores variables son de diferentes longitudes.

MelBurslan
fuente