web-dev-qa-db-fra.com

Convertir des lignes en colonnes

J'ai un fichier qui inclut des détails sur les VMS exécutant dans un hyperviseur. Nous exécutons une commande et redirigeons la sortie dans un fichier. Et les données sont disponibles dans le format ci-dessous.

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

Cette sortie diffère de l'hyperviseur à l'hyperviseur depuis sur certains hyperviseurs que nous avons 50 + VMS fonctionnant. Le fichier ci-dessus est un exemple de l'hyperviseur où nous n'avons que 3 VMS fonctionnant et que le fichier redirigé devrait donc contenir des informations sur plusieurs (N Nombre de VMS).

Nous devons obtenir ces informations dans le format ci-dessous à l'aide de AWK/SED ou d'un script 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
10
IgniteLX

Si vous marchez deux fois le fichier n'est pas un (gros) problème (stockera une seule ligne en mémoire):

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

Qui, pour qu'un nombre général de champs serait (qui pourrait avoir de nombreuses promenades du fichier):

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

Mais pour une transposition vraiment générale, cela fonctionnera:

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

Et pour le faire jolie (à l'aide de l'onglet \t Comme séparateur de champ):

./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

Le code ci-dessus pour une transposition générale stockera toute la matrice en mémoire.
[.____] Cela pourrait être un problème pour des fichiers vraiment gros.


Mise à jour du nouveau texte.

Pour traiter le nouveau texte publié dans la question, il me semble que deux laissez-passer d'AWK sont la meilleure réponse. Un passage, aussi court que des champs existent, imprimera les titres de champ d'en-tête. Le prochain passe AWK n'empêchera que le champ 2. Dans les deux cas, j'ai ajouté un moyen de supprimer les espaces de début et de fin (pour une meilleure mise en forme).

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

L'environnement { ... } | column -t -s "$(printf '%b' '\t')" est de formater toute la table de manière jolie.
[.____] Veuillez noter que la fonction "$(printf '%b' '\t')" pourrait être remplacée par $'\t' Dans ksh, bash ou zsh.

1
user79743

Si vous avez le rs (Reshape) utilitaire disponible, vous pouvez procéder comme suit:

rs -Tzc: < input.txt

Cela donne au format de sortie exactement comme spécifié dans la question, même aux largeurs de colonne dynamiques.

  • -T Transpose les données d'entrée
  • -z tailles les colonnes de manière appropriée du maximum dans chaque colonne
  • -c: utilise le côlon comme séparateur de champ d'entrée

Cela fonctionne pour des tables de taille arbitraire, par exemple:

$ 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 disponible par défaut sur OS X (et probablement d'autres machines BSD). Il peut être installé sur Ubuntu (et la famille Debian) avec:

Sudo apt-get install rs
8
Digital Trauma

Edit : Extensible à n'importe quel nombre de lignes de sortie, dans une simple linger for boucle:

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

Réponse originale :

Vous pouvez le faire comme une doublure à utiliser bash Substitution de processus:

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

Les -s Option à paste permet de gérer chaque fichier un à la fois. Les : Délimiteur défini dans paste est "capturé" par le -s Option à column à la fin, à la fin du format en faisant la mise en place des champs.

Les commandes cut dans les deux substitutions de processus retirent le premier champ et le deuxième champ, respectivement.

S'il existe des lignes vierges dans l'entrée ou non, comme column -t -s: va nettoyer la production indépendamment. (Il y avait des lignes vierges dans l'entrée d'origine spécifiée dans la question, mais elles ont été supprimées depuis la retraite. La commande ci-dessus fonctionne quelles que soient les lignes vides.)

Entrée - Contenu du fichier nommé "entrée" dans la commande ci-dessus:

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

Sortir:

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
6
Wildcard

En utilisant AWK, stockez la touche et la valeur et imprimez-les à la fin.

#!/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]
  }

Le juste gérer awk -f ./script.awk ./input.txt

2
jecxjo
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
DopeGhoti

Avec gnu datamash et column de util-linux:

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

Cela fonctionne avec plus de deux colonnes mais suppose qu'il n'y a pas de lignes vides dans votre fichier d'entrée; Avec des lignes vides entre les deux (comme dans votre exemple d'entrée initiale), vous obtiendrez une erreur comme:

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

afin d'éviter que vous ayez à les presser avant de traiter avec datamash:

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

Sinon, dans ce cas particulier (seulement deux colonnes), avec zsh et le même column:

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

(${(f)"$(<infile)"}) lit les lignes dans un tableau; ${(j;:;)list[@]%:*} joint (avec :) Le premier champ de chaque élément et ${(j;:;)list[@]#*:} joint (à nouveau avec :) le deuxième champ de chaque élément; Celles-ci sont toutes deux imprimées, par ex. La sortie est

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

qui est ensuite pipi à column -t -s:

1
don_crissti

Utilisez datamash et son option transpose option pour échanger des lignes et des colonnes dans un fichier.

datamash -t: transpose < infile.txt

Par défaut, transpose vérifie l'entrée comporte le même nombre de champs dans chaque ligne et échoue avec une erreur sinon et vous pouvez désactiver son mode strict pour permettre aux valeurs manquantes de --no-strict

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

Aussi, vous pouvez utiliser --filler Pour définir la valeur de remplissage de champ manquant:

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

dérivé de datamash manual

0
αғsнιη