Convertir des lignes en colonnes

10

J'ai un fichier qui contient des détails sur les machines virtuelles exécutées dans un hyperviseur. Nous exécutons une commande et redirige la sortie vers 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 d'hyperviseur à hyperviseur car sur certains hyperviseurs, nous avons 50 + vms en cours d'exécution. Le fichier ci-dessus est juste un exemple de l'hyperviseur où nous n'avons que 3 machines virtuelles en cours d'exécution et donc le fichier redirigé devrait contenir des informations sur plusieurs (nombre N de machines virtuelles)

Nous devons obtenir ces détails dans le format ci-dessous en utilisant awk / sed ou avec 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
IgniteLX
la source
2
Conversion
αғsнιη

Réponses:

1

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

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

Ce qui, pour un nombre général de champs serait (qui pourrait avoir de nombreuses promenades dans le 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 rendre joli (en utilisant tab \tcomme 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 la matrice entière en mémoire.
Cela pourrait être un problème pour les très gros fichiers.


Mise à jour pour le nouveau texte.

Pour traiter le nouveau texte affiché dans la question, il me semble que deux passes de awk sont la meilleure réponse. Une passe, aussi courte que les champs existent, imprimera les titres des champs d'en-tête. La prochaine passe awk n'imprimera que le champ 2. Dans les deux cas, j'ai ajouté un moyen de supprimer les espaces de début et de fin (pour un meilleur formatage).

#!/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 la table entière d'une manière jolie.
Veuillez noter que le "$(printf '%b' '\t')"pourrait être remplacé par $'\t'dans ksh, bash ou zsh.


la source
8

Si vous disposez de l' rsutilitaire (remodeler) , vous pouvez effectuer les opérations suivantes:

rs -Tzc: < input.txt

Cela donne le format de sortie exactement comme spécifié dans la question, même jusqu'à la largeur des colonnes dynamiques.

  • -T Transpose les données d'entrée
  • -z dimensionne les colonnes de manière appropriée à partir du maximum dans chaque colonne
  • -c: utilise deux points comme séparateur de champ de saisie

Cela fonctionne pour les 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
$ 

rsest disponible par défaut sur OS X (et probablement sur d'autres machines BSD). Il peut être installé sur Ubuntu (et la famille Debian) avec:

sudo apt-get install rs
Traumatisme numérique
la source
6

EDIT: extensible à n'importe quel nombre de lignes de sortie, dans une simple forboucle à une ligne :

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 en une ligne en utilisant la bashsubstitution de processus:

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

L' -soption permettant de pastegérer chaque fichier un par un. Le :délimiteur défini dans pasteest "capturé" par l' -soption columnà la fin, pour améliorer le format en alignant les champs.

Les cutcommandes des deux substitutions de processus extraient respectivement le premier champ et le deuxième champ.

Qu'il y ait ou non des lignes vides dans l'entrée n'a pas d'importance, car column -t -s:cela nettoiera la sortie malgré tout. (Il y avait des lignes vides dans l'entrée d'origine spécifiée dans la question, mais elles ont depuis été supprimées. La commande ci-dessus fonctionne indépendamment des lignes vides.)

Input - contenu du fichier nommé "input" 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

Production:

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
Caractère générique
la source
2
Cela fonctionne pour deux lignes de sortie, mais pour plus de lignes, cela devient peu maniable.
2

En utilisant awk, stockez la clé 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]
  }

La course juste awk -f ./script.awk ./input.txt

jecxjo
la source
Changé la réponse pour être dynamique. Exige simplement qu'il n'y ait qu'une seule machine virtuelle de données par fichier.
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
la source
1

Avec gnu datamashet columnde 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 échantillon d'entrée initial), vous obtiendrez une erreur comme:

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

donc pour éviter de devoir les presser avant de les traiter avec datamash:

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

Sinon, dans ce cas particulier (seulement deux colonnes), avec zshet pareil column:

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

(${(f)"$(<infile)"})lit les lignes d'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; ils sont tous deux imprimés, par exemple 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 canalisé vers column -t -s:

don_crissti
la source
0

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

Le nombre de lignes par machine virtuelle est codé en dur dans ce cas - 11. Mieux vaut le compter à l'avance et le stocker dans la variable, puis utiliser cette variable dans le code.

Explication

  1. cat <(command 1) <(command 2)- la <()construction fait commandapparaître la sortie comme un fichier temporaire. Par conséquent, catconcatène deux fichiers et le redirige.

    • commande 1 : head -n 11 virtual.txt | cut -d: -f1, nous donne les en- têtes de colonnes futures. La seule entrée de la machine virtuelle est onze premières lignes, la headcommande est utilisée pour l'obtenir. Le cutdivise cette entrée en deux colonnes et imprime la seule première.
    • commande 2 : sed 's/.*: //' virtual.txt- nous donne les valeurs futures des colonnes. sedsupprime tout le texte inutile et ne laisse que des valeurs.
  2. xargs -d '\n' -n 11. Chaque élément d'entrée est terminé par une nouvelle ligne. Cette commande récupère les éléments et les imprime par 11 par ligne.

  3. column -t- est nécessaire pour les jolis tirages. Il affiche nos lignes sous forme de tableau. Sinon, chaque ligne aura une largeur différente.

Production

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
la source
0

Utilisez datamashet son transposeoption pour permuter des lignes et des colonnes dans un fichier.

datamash -t: transpose < infile.txt

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

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

Vous pouvez également utiliser --fillerpour définir la valeur de remplissage de champ manquant:

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

dérivé de datamash manual

αғsнιη
la source
-5

si vos données sont dans des fichiers séparés dans un répertoire, vous pouvez utiliser:

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

vous devrez peut-être masser le nombre de \t(tabulation) caractères sur la printfligne si vos valeurs variables sont de longueurs différentes.

MelBurslan
la source