GNU/Linux >> Tutoriels Linux >  >> Linux

Joindre plusieurs champs dans des fichiers texte sous Unix

Il est probablement plus simple de combiner les trois premiers champs avec awk :

awk '{print $1 "_" $2 "_" $3 " " $4}' filename

Ensuite, vous pouvez utiliser join normalement sur "champ 1"


vous pouvez essayer ceci

awk '{
 o1=$1;o2=$2;o3=$3
 $1=$2=$3="";gsub(" +","")
 _[o1 FS o2 FS o3]=_[o1 FS o2 FS o3] FS $0
}
END{ for(i in _) print i,_[i] }' file1 file2

sortie

$ ./shell.sh
foo 1 scaf  3 4.5
bar 2 scaf  3.3 1.00
foo 1 boo  2.3

Si vous souhaitez omettre les lignes inhabituelles

awk 'FNR==NR{
 s=""
 for(i=4;i<=NF;i++){ s=s FS $i }
 _[$1$2$3] = s
 next
}
{
  printf $1 FS $2 FS $3 FS
  for(o=4;o<NF;o++){
   printf $i" "
  }
  printf $NF FS _[$1$2$3]"\n"
 } ' file2 file1

sortie

$ ./shell.sh
foo 1 scaf 3  4.5
bar 2 scaf 3.3  1.00

Voici le correct réponse (en termes d'utilisation des coreutils GNU standard outils, et ne pas écrire de script personnalisé dans perl/awk vous l'appelez).

$ join -j1 -o1.2,1.3,1.4,1.5,2.5 <(<file1 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1) <(<file2 awk '{print $1"-"$2"-"$3" "$0}' | sort -k1,1)
bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5

OK, comment ça marche :

  1. Tout d'abord, nous allons utiliser un excellent outil join qui peut fusionner deux lignes. join a deux exigences :

    • Nous pouvons rejoindre uniquement par un seul champ.
    • Les deux fichiers doivent être triés par colonne clé !
  2. Nous devons générer des clés dans les fichiers d'entrée et pour cela on utilise un simple awk script :

    $ cat file1
    foo 1 scaf 3
    bar 2 scaf 3.3    
    
    $ <file1 awk '{print $1"-"$2"-"$3" "$0}'
    foo-1-scaf foo 1 scaf 3
    bar-2-scaf bar 2 scaf 3.3
    

    Vous voyez, nous avons ajouté la 1ère colonne avec une clé comme "foo-1-scaf ".Nous faisons la même chose avec fichier2 .D'AILLEURS. <file awk , est juste une façon élégante d'écrire awk file , ou cat file | awk .

    Nous devrions également trier nos fichiers par la clé, dans notre cas c'est la colonne 1, donc on ajoute à la fin de la commande le | sort -k1,1 (trier par texte de la colonne 1 à la colonne 1)

  3. À ce stade, nous pourrions simplement générer des fichiers file1.with.key et file2.with.key et rejoignez-les, mais supposons que ces fichiers soient volumineux, nous ne voulons pas les copier sur le système de fichiers. Au lieu de cela, nous pouvons utiliser quelque chose appelé bash substitution de processus pour générer une sortie dans un canal nommé (cela évitera toute création de fichier intermédiaire inutile). Pour plus d'informations, veuillez lire le lien fourni.

    Notre syntaxe cible est :join <( some command ) <(some other command)

  4. La dernière chose est d'expliquer les arguments de jointure fantaisistes :-j1 -o1.2,1.3,1.4,1.5,2.5

    • -j1 - joindre par clé dans la 1ère colonne (dans les deux fichiers)
    • -o - afficher uniquement les champs 1.2 (1er champ de fichier2), 1.3 (1er fichier colonne 3), etc.

      De cette façon, nous avons joint les lignes, mais join affiche uniquement les colonnes nécessaires.

Les leçons tirées de cet article devraient être :

  • vous devez maîtriser les coreutils package, ces outils sont très puissants lorsqu'ils sont combinés et vous n'en avez presque jamais besoin écrire un programme personnalisé pour faire face à de tels cas,
  • Les outils utilitaires de base sont également extrêmement rapides et testés de manière intensive, ils constituent donc toujours le meilleur choix.

La commande join est difficile à utiliser et ne joint que sur une colonne

Une expérimentation approfondie et un examen minutieux des pages de manuel indiquent que vous ne pouvez pas joindre directement plusieurs colonnes - et tous mes exemples de travail de jointure, assez curieusement, n'utilisent qu'une seule colonne de jointure.

Par conséquent, toute solution nécessitera que les colonnes à joindre soient concaténées en une seule colonne, d'une manière ou d'une autre. La commande de jointure standard nécessite également que ses entrées soient dans le bon ordre de tri - il y a une remarque dans la jointure GNU (info coreutils join) à ce sujet qui ne nécessite pas toujours de données triées :

Cependant, en tant qu'extension GNU, si l'entrée n'a pas de lignes non appariables, l'ordre de tri peut être n'importe quel ordre qui considère deux champs comme égaux si et seulement si la comparaison de tri décrite ci-dessus les considère comme égaux.

Une façon possible de le faire avec les fichiers donnés est :

awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file1 |
sort > sort1
awk '{printf("%s:%s:%s %s %s %s %s\n", $1, $2, $3, $1, $2, $3, $4);}' file2 |
sort > sort2
join -1 1 -2 1 -o 1.2,1.3,1.4,1.5,2.5 sort1 sort2

Cela crée un champ de tri composite au début, en utilisant ':' pour séparer les sous-champs, puis trie le fichier - pour chacun des deux fichiers. La commande join joint ensuite les deux champs composites, mais imprime uniquement les champs non composites (non jointifs).

La sortie est :

bar 2 scaf 3.3 1.00
foo 1 scaf 3 4.5

Tentatives infructueuses pour que la jointure fasse ce qu'elle ne fera pas

joindre -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 fichier1 fichier2

Sur MacOS X 10.6.3, cela donne :

$ cat file1
foo 1 scaf 3 
bar 2 scaf 3.3
$ cat file2
foo 1 scaf 4.5
foo 1 boo 2.3
bar 2 scaf 1.00
$ join -1 1 -2 1 -1 2 -2 2 -1 3 -2 3 -o 1.1,1.2,1.3,1.4,2.4 file1 file2
foo 1 scaf 3 4.5 
bar 2 scaf 3.3 4.5 
$

Il s'agit de rejoindre le champ 3 (uniquement) - ce qui n'est pas ce qui est souhaité.

Vous devez vous assurer que les fichiers d'entrée sont triés dans le bon ordre.


Linux
  1. Comment renommer plusieurs fichiers en une seule commande ou un script sous Unix ? ?

  2. Dd :plusieurs fichiers d'entrée ?

  3. Comment renommer plusieurs fichiers d'une extension à une autre sous Linux/Unix ?

  4. Comment renommer plusieurs fichiers en une seule commande ou un seul script sous Unix ?

  5. Tail plusieurs fichiers distants

Commande Grep sous Linux (Rechercher du texte dans des fichiers)

Renommer la commande sous Linux (renommer plusieurs fichiers)

Comment joindre/fusionner plusieurs fichiers audio en un seul sous Linux

Comment compresser plusieurs fichiers sous Linux

Rechercher du texte dans des fichiers sous Linux à l'aide de grep

Commande ls sous Linux/UNIX