web-dev-qa-db-fra.com

Comment joindre deux fichiers CSV, mais uniquement lorsque le deuxième fichier correspond?

J'ai 2 fichiers CSV:

Fichier d'objets:

IP,MASK,DESCRIPTION
10.10.3.94,255.255.255.255,Rob
10.10.3.95,255.255.255.255,Mark
10.10.3.96,255.255.255.255,John

Fichier de services:

DESCRIPTION,OrgIP,Service
Rob,1.1.1.1,Purple
John,2.2.2.2,Green
Mark,3.3.3.3,Yellow

Le fichier d’objets a 3000 lignes, le fichier de service environ 500.

Je souhaite créer un nouveau fichier contenant toutes les lignes des services avec les champs des objets ajoutés là où une correspondance est trouvée dans la description. Donc, le résultat souhaité ressemblerait à ceci:

DESCRIPTION,OrgIP,Service,IP,MASK
Rob,1.1.1.1,Purple,10.10.3.94,255.255.255.255
John,2.2.2.2,Green,10.10.3.96,255.255.255.255
Mark,3.3.3.3,Yellow,10.10.3.95,255.255.255.255
2
MattyM

Utiliser join:

join --header -t, -11 -23 -a1 <(awk 'NR > 1 {print | "sort -t, -k1"; next} 1' services) <(awk 'NR > 1 {print | "sort -t, -k3"; next} 1' objects)
join --header -t, -11 -23 -a1 <(
    awk '
        NR > 1 {
            print |
                "sort -t, -k1";
            next
        }
        1
    ' services
) <(
    awk '
        NR > 1 {
            print |
                "sort -t, -k3";
            next
        }
        1
    ' objects
)
  • --header: traite la première ligne de chaque fichier comme des en-têtes de champ, en les imprimant sans essayer de les lier
  • -t,: définit , comme séparateur de champs d'entrée et de sortie
  • -11: rejoint le champ n ° 1 de services
  • -23: rejoint le champ n ° 3 de objects
  • -a1: imprime également les lignes imparables de services
  • <(awk 'NR > 1 {print | "sort -t, -k1"; next} 1' services): trie services sur la colonne 1 en excluant l'en-tête
  • <(awk 'NR > 1 {print | "sort -t, -k3"; next} 1' objects): trie objects sur la colonne 3 en excluant l'en-tête
% cat objects 
IP,MASK,DESCRIPTION
10.10.3.94,255.255.255.255,Rob
10.10.3.95,255.255.255.255,Mark
10.10.3.96,255.255.255.255,John
% cat services 
DESCRIPTION,OrgIP,Service
Rob,1.1.1.1,Purple
John,2.2.2.2,Green
Mark,3.3.3.3,Yellow
% join --header -t, -11 -23 -a1 <(awk 'NR > 1 {print | "sort -t, -k1"; next} 1' services) <(awk 'NR > 1 {print | "sort -t, -k3"; next} 1' objects)
DESCRIPTION,OrgIP,Service,IP,MASK
John,2.2.2.2,Green,10.10.3.96,255.255.255.255
Mark,3.3.3.3,Yellow,10.10.3.95,255.255.255.255
Rob,1.1.1.1,Purple,10.10.3.94,255.255.255.255
1
kos

Si vous avez installé sqlite3 et python sur votre système, vous pouvez utiliser ceci http://www.sqlet.com/ .

La commande SQL correspondante serait:

./sqlet.py -d',' -A file1.txt -B file2.txt 'select A3,B2,B3,A1,A2 from A LEFT JOIN B ON A3=B1;' | sqlite3

Cela nécessite que vous supprimiez les en-têtes des deux fichiers. Le script sqlet doit être extrait dans le même dossier où se trouvent ces deux fichiers ou modifié en conséquence.

Je l'ai essayé sur votre extrait d'exemple. Ça marche:

bruni@bruni-Inspiron-5547:~/Downloads$ cat file1.txt
10.10.3.94,255.255.255.255,Rob
10.10.3.95,255.255.255.255,Mark
10.10.3.96,255.255.255.255,John
bruni@bruni-Inspiron-5547:~/Downloads$ cat file2.txt
Rob,1.1.1.1,Purple
John,2.2.2.2,Green
Mark,3.3.3.3,Yellow
bruni@bruni-Inspiron-5547:~/Downloads$ ./sqlet.py -d',' -A file1.txt -B file2.txt 'select A3,B2,B3,A1,A2 from A LEFT JOIN B ON A3=B1;' | sqlite3
Rob,1.1.1.1,Purple,10.10.3.94,255.255.255.255
Mark,3.3.3.3,Yellow,10.10.3.95,255.255.255.255
John,2.2.2.2,Green,10.10.3.96,255.255.255.255
2
Bruni

Si les fichiers ne sont pas trop volumineux, qu'en est-il de awk?

$ awk -F, 'NR==FNR {a[$1]=$2 FS $3 FS $4; next} $3 in a {OFS=","; print $3,a[$3],$2}' services objects
DESCRIPTION,OrgIP,Service,IP,MASK
Rob,1.1.1.1,Purple,10.10.3.94,255.255.255.255
Mark,3.3.3.3,Yellow,10.10.3.95,255.255.255.255
John,2.2.2.2,Green,10.10.3.96,255.255.255.255
1
steeldriver

Tu peux faire:

join -t, -a2 -11 -23 <(head -1 f1.txt; tail -n +2 f1.txt | \
         sort -t, -k1,1) <(head -1 f2.txt; tail -n +2 f2.txt | sort -t, -k3,3)
  • <() est la syntaxe de substitution de processus, bash le remplacera par un descripteur de fichier contenant le résultat de la commande.

  • head -1 f1.txt; tail -n +2 f2.txt | sort -t, -k1,1 sera sort le premier fichier sur le premier champ de la deuxième ligne au repos et la première ligne est ajoutée en haut afin que nous puissions l'utiliser avec join. Il en va de même pour f2.txt avec le champ à sort selon trois.

  • join va juste rejoindre le premier champ de f1.txt et le troisième de f2.txt comme champs communs.

Exemple:

$ cat f1.txt 
DESCRIPTION,OrgIP,Service
Rob,1.1.1.1,Purple
John,2.2.2.2,Green
Mark,3.3.3.3,Yellow

$ cat f2.txt 
IP,MASK,DESCRIPTION
10.10.3.94,255.255.255.255,Rob
10.10.3.95,255.255.255.255,Mark
10.10.3.96,255.255.255.255,John

$ join -t, -11 -23 <(head -1 f1.txt; tail -n +2 f1.txt | sort -t, -k1,1) <(head -1 f2.txt; tail -n +2 f2.txt | sort -t, -k3,3)
DESCRIPTION,OrgIP,Service,IP,MASK
John,2.2.2.2,Green,10.10.3.96,255.255.255.255
Mark,3.3.3.3,Yellow,10.10.3.95,255.255.255.255
Rob,1.1.1.1,Purple,10.10.3.94,255.255.255.255
1
heemayl