Partage
  • Partager sur Facebook
  • Partager sur Twitter

Script shell

Je ne comprend pas comment recuperer les infos

Sujet résolu
21 juin 2019 à 12:36:07

Bonjour à tous,

Voila ma demande, je récupère deux fichiers montés de la même façon, je voudrais récupérer uniquement les données présentes dans le premier qui sont dans le deuxième.

exemple

fichier1.csv

a1;j1;

b1;j2;

c1;j3;

d1;j4;

et le fichier2.csv

id1;j7;

id2;j1;

id3;j4;

A savoir que seulement les j* sont identiques, c'est à dire que les a1, b2, ... et id1, id2 ... ne le sont pas donc je voudrais uniquement les récupérer par rapport aux j*.

Je souhaiterais avoir comme résultat dans un fichier ResultatExistant.csv :

a1;j1;id2;

d1;j4;id3;

Voila merci de votre aide et du temps que vous prendrez à me répondre.

Alors j'ai pu trouver une partie de la commande shell

awk -F\; '{if(NR==FNR){t[$2]=""}else{if(FNR==1||($2 in t))print}}' fichier1.csv fichier2.csv > ResultatExistant.csv

Par contre elle ne m'affiche que :

id2;j1;

id3;j4;

Ce qui est déjà un début, il ne me manque plus que la partie du premier fichier à afficher devant.

-
Edité par BabaMinaTor 21 juin 2019 à 15:33:44

  • Partager sur Facebook
  • Partager sur Twitter
27 juin 2019 à 15:24:27

Si j'étais toi je le ferais plutôt dans un vrai langage de programmation genre Python, ou même du C d'ailleurs.

Il doit sûrement y avoir un one-liner qui te fait ça mais à mon avis tu t'embêteras moins en te codant un programme qui te fait ça.

  • Partager sur Facebook
  • Partager sur Twitter
8 juillet 2019 à 11:05:28

Bonjour,

Je répond sûrement trop tard, et j'espère que tu as trouvé la solution depuis.

potterman28wxcv a écrit:

Si j'étais toi je le ferais plutôt dans un vrai langage de programmation genre Python, ou même du C d'ailleurs.

Il doit sûrement y avoir un one-liner qui te fait ça mais à mon avis tu t'embêteras moins en te codant un programme qui te fait ça.


Awk est un "vrai" langage de programmation. Et ce genre d'application est totalement celle pour laquelle il a été conçu. Il n'y a pas d'obligation d'écrire un programme en awk sur une seule ligne. Dans son cas, il serait d'ailleurs plus simple et plus clair de l'écrire sur plusieurs lignes dans un fichier.

Et puis, je trouve que ça aide personne de dire: "python c'est mieux", surtout quand cette partie du forum est dédiée à d'autres langages que python ou C. Je suis curieux de connaître l'équivalent du programme ci-dessous en Python (sans installer de librairie).

BabaMinaTor a écrit:

awk -F\; '{if(NR==FNR){t[$2]=""}else{if(FNR==1||($2 in t))print}}' fichier1.csv fichier2.csv > ResultatExistant.csv

Par contre elle ne m'affiche que :

id2;j1;

id3;j4;

J'ai du mal à saisir comment elle peut t'afficher ça. Et je ne comprend pas pourquoi dans le else tu pose FNR == 1 comme condition?!

Tu créé un tableau associatif avec uniquement une valeur dans l'index, il suffit de donner le record comme valeur de cette index.

BEGIN { 
    FS = ";" 
}
{
    if ( NR == FNR )
        field2InFile1[$2] = $0        # Associe chaque valeur du 2ème field au record dans un array
    else  if ( $2 in field2InFile1 )
        print field2InFile1[$2] $1  # print le record qui match et le 1er field du record actuel
        # libre à toi de choisir ce que tu veux afficher ici
}

En one-liner, ça donnerait:

awk 'BEGIN { FS = ";" } { if ( NR == FNR ) { field2InFile1[$2] = $0 } else if ( $2 in field2InFile1 ) { print field2InFile1[$2] $1 }}' fichier1.csv fichier2.csv  

Note: attention, s'il y a une virgule dans certains champs, cela risque de faire dysfonctionner le programme. Dans ce cas, il faut préférer FPAT à FS: https://www.gnu.org/software/gawk/manual/gawk.html#Splitting-By-Content.

-
Edité par KoaTao 8 juillet 2019 à 12:52:48

  • Partager sur Facebook
  • Partager sur Twitter
8 juillet 2019 à 18:46:06

KoaTao a écrit:

potterman28wxcv a écrit:

Si j'étais toi je le ferais plutôt dans un vrai langage de programmation genre Python, ou même du C d'ailleurs.

Il doit sûrement y avoir un one-liner qui te fait ça mais à mon avis tu t'embêteras moins en te codant un programme qui te fait ça.


Awk est un "vrai" langage de programmation. Et ce genre d'application est totalement celle pour laquelle il a été conçu. Il n'y a pas d'obligation d'écrire un programme en awk sur une seule ligne. Dans son cas, il serait d'ailleurs plus simple et plus clair de l'écrire sur plusieurs lignes dans un fichier.

Et puis, je trouve que ça aide personne de dire: "python c'est mieux", surtout quand cette partie du forum est dédiée à d'autres langages que python ou C. Je suis curieux de connaître l'équivalent du programme ci-dessous en Python (sans installer de librairie).

Sans installation de librairie, le code Python qui fait ce que demande l'auteur :

import sys

if len(sys.argv) != 3:
  print("Usage: python3 machin.py file1.csv file2.csv")
  sys.exit()

_, filename1, filename2 = sys.argv

f1 = open(filename1, "r")
f2 = open(filename2, "r")

lines1 = f1.readlines()
lines2 = f2.readlines()

for line in lines1:
  line_elts = line.split(";")
  for line2 in lines2:
    line2_elts = line2.split(";")
    if line_elts[1] == line2_elts[1]:
      print("{};{};{};".format(line_elts[0], line_elts[1], line2_elts[0]))

f1.close()
f2.close()

Aucune librairie n'est nécessaire. Après si on veut gérer d'autres séparateurs, on peut s'en tirer avec des expressions régulières mais j'avoue que c'est un peu overkill.

Quand à l'utilité de dire "Python c'est mieux" - ce que je n'ai d'ailleurs pas dit, j'ai juste dit qu'il s'embêtera moins à le faire en Python, mais enfin supposons que j'ai dit ça - je me mets dans le contexte d'un débutant en programmation, ce qui est le cas de la plupart des gens ici. Est-ce qu'on veut qu'ils prennent du temps à maitriser le langage awk, que je vois de moins en moins de personnes utiliser.. Ou est-ce qu'on veut qu'ils apprennent des langages comme le C, le Python, ou même du Java qui ont une communauté beaucoup plus importante d'utilisateurs. Si tu dis à quelqu'un "je maitrise complètement Awk par contre le C et le Python je ne connais pas.." tu vas passer pour quelqu'un qui a appris à programmer sur la machine de son grand père.

Je ne dis pas que awk est vieux, aigris, inutilisable, etc.. Et effectivement la solution que tu donnes est plus élégante que l'équivalent Python, parce que awk est plus spécialisé que Python pour ce genre de problèmes. Et c'est toujours bénéfique d'apprendre du awk, tout comme c'est bénéfique d'apprendre à faire du Bourne shell, des Makefile, savoir bien faire des grep, etc.. ça fait partie des petites choses de la vie de tous les jours d'un développeur qu'il est utile de maitriser.

Mais pour un exemple bête comme celui là, Python fait tout aussi bien l'affaire. En lisant le topic je me suis dit "Il a l'air de galérer là - pourquoi s'embêter à passer par awk quand ça se fait en 5 minutes en Python ?". Par contre, j'avoue que en C ça aurait été un peu plus complexe à écrire, avec une probabilité de se gourer plus importante.

-
Edité par potterman28wxcv 8 juillet 2019 à 18:49:01

  • Partager sur Facebook
  • Partager sur Twitter
9 juillet 2019 à 19:19:20

Bonjour,

potterman28wxcv a écrit:

Mais pour un exemple bête comme celui là, Python fait tout aussi bien l'affaire. En lisant le topic je me suis dit "Il a l'air de galérer là - pourquoi s'embêter à passer par awk quand ça se fait en 5 minutes en Python ?". Par contre, j'avoue que en C ça aurait été un peu plus complexe à écrire, avec une probabilité de se gourer plus importante.

Le script que j'ai proposé prend 5 min pour quelqu'un qui a les bases en awk, il n'y a que l'utilisation des variables built-in NR et FNR qui est spécifique à awk (et le BEGIN). Mais le reste, c'est de l'algo pur et dur. Clairement, le plus long (quelques minutes de réflexion) ici était d'écrire l'algo.

L'OP a galéré parce que justement c'est un débutant en programmation, et non pas un débutant avec awk. Utiliser un autre langage de programmation n'aurait pas résolu son problème vu que c'est un problème d'algo.

Maintenant, ton programme en python soi-disant facile à faire en 5 min fonctionne mais a un très gros défaut. Et ce défaut n'a rien avoir avec python mais encore une fois c'est de l'algo. Tu lis autant de fois le fichier2 qu'il y a de ligne dans le fichier1.

D'ailleurs, en voulant donc améliorer ton programme en python. J'ai remarqué deux choses:

  • L'existence d'un module csv avec python pour parser des fichiers csv: https://docs.python.org/3.7/library/csv.html#module-csv
  • Que le programme que j'ai proposé en avec awk a aussi le défaut prendre en considération qu'une valeur dans le champ 2 des fichiers (les "j[[:digit:]]+") est unique.

Du coup, voici ma solution:

#!/bin/awk -f

BEGIN { 
    FS = ";"        
    OFS = ";"           # Output Field Separator
}
{
    if ( NR == FNR ) {              # NR: nbr total de record parcouru, FNR: nbr total de record parcouru dans le fichier actuellement lu
        fieldsInFile1[$2][FNR] = $1 # Array of Array, fonctionne avec gawk (GNU awk), pas POSIX
    } else if ( $2 in fieldsInFile1 ) {
        for ( j in fieldsInFile1[$2] )
            print $2, $1, fieldsInFile1[$2][j]
    }
}



#!/usr/bin/python

import sys
import csv

_, filename1, filename2 = sys.argv
fieldnames = [ 'column_1', 'column_2']

file1Dict = dict()      # création d'un dictionnaire

with open(filename1, newline='') as csvfile:
    file1Reader = csv.reader(csvfile, delimiter=';')
    for row in file1Reader:
        if row[1] in file1Dict:                 # Si key existe déjà ajoute un item à la liste d'items associés
            file1Dict[row[1]].append(row[0])        
        else:                                   # sinon crée la pair key-item
            file1Dict[row[1]] = [ row[0] ]
            
with open(filename2, newline='') as csvfile:
    file2Reader = csv.reader(csvfile, delimiter=';')
    for row in file2Reader:
        if row[1] in file1Dict:
            for paired_item in file1Dict[row[1]]:
                print("{};{};{}".format(row[1], row[0], paired_item))


HS:

N'ayant pas fait de python depuis quelques années, ça m'a pris plus de 5 minutes pour écrire la version en python, sans compter que c'est fort probablement mal écrit en plus ^^ 

Comme indiqué en commentaire, la version avec awk n'est pas POSIX. L'utilisation de tableau multidimensionnel n'étant pas possible avec une implémentation POSIX d'awk.

J'avais écrit le script en POSIX, mais il était bien plus lent que son équivalent avec gawk (4x environ).

-
Edité par KoaTao 9 juillet 2019 à 19:20:34

  • Partager sur Facebook
  • Partager sur Twitter
10 juillet 2019 à 0:24:00

Oui effectivement on peut passer par un dictionnaire pour avoir une complexité linéaire au lieu d'avoir du N² (avec N la taille d'un fichier).

En fait je ne sais pas du tout ce que c'est que NR et FNR - je suis bien incapable de décrire ce que fait ton programme awk algorithmiquement, et j'étais aussi incapable de décrire ce que faisait son programme awk.

Voyant qu'il allait sûrement recevoir peu de réponse étant donné le côté un peu niche du sujet, c'est aussi pour ça que j'ai voulu le réorienter vers du Python. Mais oui je ne doute pas qu'avec awk c'est tout aussi simple, quand on sait l'utiliser

  • Partager sur Facebook
  • Partager sur Twitter
15 juillet 2019 à 16:01:43

Merci à vous deux pour les réponses c'est vrai que depuis le temps j'avais trouvé la réponse sans fermer la discussion et je m'en excuse.

Mais maintenant, je me mets au python pas trop le choix XD

Mais pour répondre à la question que j'avais posé, j'ai utilisé un awk qui pour moi était plus rapide dans le shell.

Mais la prochaine fois, j'utiliserais peut-être le python.

Alors je regarde dans le fichier1 et le fichier2 les correspondance que je sors dans ResultatExistant.csv

Puis je sors dans les non trouvé dans deux fichiers séparés. (pourquoi séparés car le sujet avait évolué il me fallait les non trouvé de chaque fichier séparés)

awk 'BEGIN{FS=";";OFS=FS;} (FNR==NR){a[$1]=$2;next;} {vu[$1]=1; if (a[$1]!="") print $1,a[$1],$2,"" >"ResultatExistant.csv"; else print $0 >"Non_trouvé_fichier2.csv";} END{for (i in a) if (vu[i]!=1) print i,a[i] >"Non_trouvé_fichier1.csv";}' "fichier1.csv" "fichier2.csv"

Encore merci potterman28wxcv et KoaTao

  • Partager sur Facebook
  • Partager sur Twitter
15 juillet 2019 à 19:22:51

Ah mais si tu as tout à fait le choix, Python n'est pas le seul langage qui existe. Et comme l'a souligné KoaTao, awk est tout aussi bien pour ce genre de tâches là.

Pour un débutant je trouve Python peut être plus facile à prendre en main. Et puis plus utile dans le portfolio.

Mais si on voit que tu sais faire du awk c'est un certain plus aussi.

Bref, fait le langage que t'as envie de faire ! :)

  • Partager sur Facebook
  • Partager sur Twitter