Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 06/04/2012, à 19:08

kernelZero

[Résolu] Petit coup de pouce pour script Bash (awk)

Bonjour Messieurs les spécialistes du script Bash (et d'autres aussi !)

J'essaye de faire un traducteur de fichier en Bash.

J'ai un fichier dictionnaire (dictionnaire.txt) qui se presente comme ceci :

NOM , NAME
RETOUR , RETURN
CREE , CREATE
[...]

Et j'ai mon fichier initial (fr.dat) (à traduire) qui se presente comme ceci :

NOM CREE TOTO 
RETOUR
[...]

Et je voudrais qu'il soit traduit (en.dat), par le biais de mon dictionnaire comme ceci :

NAME CREATE TOTO 
RETURN
[...]

Je pense utiliser les commandes : cat, awk, sed et eventuellement quelques boucles for
Pouvez-vous m'aider concernant la logique à suivre ?

J'imagine qu'il ya nettement plus rapide, élégant et efficace que :

sed "s/RETOUR/RETURN/g" fr.dat > en.dat
sed "s/NOM/NAME/g" en.dat > en1.dat
sed "s/CREATE/CREATE/g" en1.dat > en.dat
[...]

Merci d'avance !!

Dernière modification par kernelZero (Le 07/04/2012, à 13:56)


Un petit [Résolu] dans le titre quand c'est le cas !!

Hors ligne

#2 Le 06/04/2012, à 19:43

pingouinux

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Bonjour,
Quelque chose dans ce genre ?

awk 'BEGIN{while(getline<"dictionnaire.txt"){sub(","," ");trad[$1]=$2;}}
{for(k=1;k<=NF;k++) {if(trad[$k]) $k=trad[$k] } print >"en.dat"}' fr.dat

Hors ligne

#3 Le 06/04/2012, à 20:03

kernelZero

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Merci pingouinux !!

ça marche impeccable !! Bon, je vais prendre un bon moment pour le digerer ... mais c'est redoutable d'efficacité !!

Bon, comme je suis chieur jusqu'au bout, j'ai aussi des groupements de 2 mots français qui se combinent en 1 mot anglais, du style (dictionnaire.txt) :

FAIRE , MAKE
NOM , NAME
CREE , CREATE
NON CREE , NONCREATE
RETOUR , RETURN
[...]

et pour pimenter le tout, je peux avoir des abréviations  (au mini les 4 premiers caractères) dans le fichier français :

FAIR NOM CREE TOTO RETO

à traduire en

MAKE NAME CREATE TOTO RETURN

Si tu as une methode, ou mieux la reponse je t'en serais fortement reconnaissant !!

Merci d'avance !!

Dernière modification par kernelZero (Le 07/04/2012, à 14:19)


Un petit [Résolu] dans le titre quand c'est le cas !!

Hors ligne

#4 Le 06/04/2012, à 20:30

pingouinux

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Pour faire le dictionnaire dont les mots contiennent des espaces, pas de problème, car ils sont séparés par une virgule; c'est ensuite que ça se corse : dans le texte fr.dat, on ne peut plus se contenter de prendre les mots un par un. Je proposerais de mettre des _ à la place des espaces dans les mots.

Pour ce qui est des abréviations, je fais le dictionnaire en générant les différentes orthographes du mot, à partir de 4 lettres. Attention aux doublons possibles.

awk '
   BEGIN { OLD_FS=FS; FS=","
     while(getline<"dictionnaire.txt")
     {
        for(k=1;k<=NF;k++) { sub(/^ */,"",$k); sub(/ *$/,"",$k); }
        for(k=4;k<=length($1);k++) { trad[substr($1,1,k)]=$2; }
     }
     FS=OLD_FS
   }
   { for(k=1;k<=NF;k++) { if($k in trad) $k=trad[$k] }
     print >"en.dat"
   }
' fr.dat

Dernière modification par pingouinux (Le 06/04/2012, à 20:57)

Hors ligne

#5 Le 06/04/2012, à 20:46

Postmortem

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Salut,
Ton fichier d'origine, fr.dat, est bien un fichier texte en colonnes ? Ce ne sont pas des phrases ??
Si c'est le cas, tu pourrais le générer avec un séparateur de colonnes différent de l'espace, ce qui simplifierait grandement le traitement des noms contenant une espace pour la traduction.
Quoique je ne doute pas que pingouinux ou un autre membre du forum soit capable de le faire sans modifier le séparateur de colonnes !


Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »

Hors ligne

#6 Le 06/04/2012, à 20:49

kernelZero

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Du coup, tu me propose de modifier le dictionnaire pour les mots contenant des espaces en :

VERIFIER , CHECK
NON_CREE , NONCREATE
NOM , NAME
RETOUR , RETURN
FAIRE , MAKE

avec un fichier fr.dat du style :

FAIRE VERIFIER NON_CREE NOM TOTO RETOUR
[...]

De toute façon, je n'ai pas de underscore dans mes fichier fr.dat et en.dat, je pourrais ensuite les traquer pour les substituer par des espaces.

En tout cas, c'est deja très chouette !!

Merci pingouinux !!

Dernière modification par kernelZero (Le 07/04/2012, à 14:10)


Un petit [Résolu] dans le titre quand c'est le cas !!

Hors ligne

#7 Le 06/04/2012, à 20:57

kernelZero

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Salut Postmortem,

Je ne vois pas bien ce que tu entends par fichier texte à colonnes ?
En fait, il n'y a pas de colonnes mais juste des instructions, de longueurs differentes pour chaque ligne.
Il y a aussi certaines precautions à avoir, notamment observer un espace en debut de certain paragraphe.

Je vais de suite tester la solution de pingouinux ...

Encore "Thanks a lot" de s'interesser à mon probleme !

Dernière modification par kernelZero (Le 07/04/2012, à 14:10)


Un petit [Résolu] dans le titre quand c'est le cas !!

Hors ligne

#8 Le 06/04/2012, à 20:59

pingouinux

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Mon script du #4 devrait faire l'affaire… je croise les doigts.

Hors ligne

#9 Le 06/04/2012, à 21:14

kernelZero

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Bluffant !!

Bon, petit detail, "NOM" est resté "NOM" au lieu de "NAME".
Faut-il compter le nombre de carateres et poser une condition du style :
si moins de 4 caracteres, tu le prends tel-quel ; sinon : tu construis la version abrégée

Par contre j'ai pas compris si tu me conseillais d'insérer des underscore dans mon dictionnaire et/ou dans mon fr.dat ?


Pour finir de t'embeter, j'ai pas tout compris pour les 2 boucles for :

for(k=1;k<=NF;k++) { sub(/^ */,"",$k); sub(/ *$/,"",$k); }
for(k=4;k<=length($1);k++) { trad[substr($1,1,k)]=$2; } 

Merci infiniment !

Dernière modification par kernelZero (Le 06/04/2012, à 21:22)


Un petit [Résolu] dans le titre quand c'est le cas !!

Hors ligne

#10 Le 06/04/2012, à 21:56

pingouinux

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Voilà, c'est corrigé. En fait, je ne traitais que les noms d'au moins 4 caractères.

awk '
   BEGIN { OLD_FS=FS; FS=","  # Le séparateur de champs est la virgule (FS=Field Separator)
     while(getline<"dictionnaire.txt")
     {
     # Boucle sur les mots de la ligne.
     # On retire les espaces de début et de fin de chaque mot, on remplace les espaces dans les mots par _
     # NF=Number of Fields
        for(k=1;k<=NF;k++) { sub(/^ */,"",$k); sub(/ *$/,"",$k); gsub(" ","_",$k); }
     # Le mot entier, de longueur n, est mis dans le dictionnaire
        trad[$1]=$2;
     # S il y a lieu, on y met aussi les débuts du mot (de 4 à n-1 caractères)
        for(k=4;k<length($1);k++) { trad[substr($1,1,k)]=$2; }
     }
     FS=OLD_FS
   }
   { for(k=1;k<=NF;k++) { if($k in trad) $k=trad[$k] }
     print >"en.dat"
   }
' fr.dat

Ajouté : L'ajout des _ est à faire dans fr.dat seulement (je viens de faire la modif)

Dernière modification par pingouinux (Le 06/04/2012, à 22:11)

Hors ligne

#11 Le 06/04/2012, à 22:05

kernelZero

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

D'une, c'est impeccable, tout fonctionne !
De deux, un grand merci pour les explications supplémentaires !

Affaire résolue !

Merci pingouinux !

Dernière modification par kernelZero (Le 07/04/2012, à 13:02)


Un petit [Résolu] dans le titre quand c'est le cas !!

Hors ligne

#12 Le 06/04/2012, à 22:12

pingouinux

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Attention, je viens de corriger #10

Hors ligne

#13 Le 07/04/2012, à 00:51

kernelZero

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Re,
Etant donné que j'avais les combinaisons suivantes (1:1 ; 2:1 ; 2:2) :

FAIRE , MAKE
NON CREE , NONCREATE
CONDITION NOM , NAME CONDITION

ça fonctionnait bien pour (1:1 et 2:1), en indiquant si besoin dans fr.dat les mots "liés" entre-eux par un "_", mais je recuprais un underscore de liaison entre les deux mots anglais pour le cas (2:2) neutral (L6 ?)

CONDITION NOM    -->   NAME_CONDITION

Du coup, avec un :

sub("_"," ",$k)

el ligne 17 c'est réglé ! smile


awk '
    BEGIN { OLD_FS=FS; FS=","             # On change le separateur de champs " " --> "," (L1)
        while(getline<"dictionnaire.txt"){      # (L2)
            for(k=1;k<=NF;k++){            # On retire les espaces de début et de fin de chaque mot, on remplace les espaces dans les mots par _ (L3)
                sub(/^ */,"",$k);                # (L4)
                sub(/ *$/,"",$k);               # (L5)
                gsub(" ","_",$k);              # (L6)
            }
            trad[$1]=$2;                # Le mot entier, de longueur n, est mis dans le dictionnaire
            for(k=4;k<length($1);k++){    # S il y a lieu, on y met aussi les débuts du mot (de 4 à n-1 caractères, n le nbr de caracteres)
                trad[substr($1,1,k)]=$2;    # substr(C,D,F)  renvoie F caractères de C à partir de D
            }
        }
         FS=OLD_FS                        # On (re)change le separateur de champs "," --> " "
    }
    { 
    for(k=1;k<=NF;k++){
        if($k in trad){ $k=trad[$k] ; sub("_"," ",$k) }    #(L17)
    }
    print >"en.dat"
    }
' fr.dat

Reste plus qu'à faire une boucle for sur tous les fichiers du dossier et hop !

@+

Dernière modification par kernelZero (Le 07/04/2012, à 14:15)


Un petit [Résolu] dans le titre quand c'est le cas !!

Hors ligne

#14 Le 07/04/2012, à 06:29

pingouinux

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

Bonjour,
Voici la dernière version, qui me semble tenir la route.

Fichier traduc.sh

#!/bin/bash

fic_in=${1:-/dev/stdin}
fic_out=${2:-/dev/stdout}

awk '
    function ajout(trad,mot_fr,mot_en) {
       if(mot_fr in trad) printf("%s est déjà dans le dictionnaire => %s %s\n",mot_fr,trad[mot_fr],mot_en) >"/dev/stderr";
       else trad[mot_fr]=mot_en;
    }
    BEGIN { OLD_FS=FS; FS=","             # On change le separateur de champs " " --> "," (L1)
        while(getline<"dictionnaire.txt"){      # (L2)
            for(k=1;k<=NF;k++){            # On retire les espaces de début et de fin de chaque mot, on remplace les espaces dans les mots par _ (L3)
                sub(/^ */,"",$k);                # (L4)
                sub(/ *$/,"",$k);               # (L5)
                if(k==1) gsub(" ","_",$k);              # (L6)
            }
            ajout(trad,$1,$2);          # Le mot entier, de longueur n, est mis dans le dictionnaire
            for(k=4;k<length($1);k++){    # S il y a lieu, on y met aussi les débuts du mot (de 4 à n-1 caractères, n le nbr de caracteres)
                ajout(trad,substr($1,1,k),$2);  # substr(C,D,F)  renvoie F caractères de C à partir de D
            }
        }
         FS=OLD_FS                        # On (re)change le separateur de champs "," --> " "
    }
    { 
    for(k=1;k<=NF;k++){
        if($k in trad){ $k=trad[$k] ; }    #(L17)
    }
    print
    }
' <"$fic_in" >"$fic_out"

Appel :

./traduc.sh fr.dat en.dat

Ce qui a changé :

  • La fonction ajout se charge d'ajouter les mots dans le dictionnaire, en vérifiant qu'il n'y a pas de doublons

  • On ne remplace les espaces par _ que dans le 1er mot (français) de chaque ligne du dictionnaire. La manip inverse dans le mot anglais après traduction devient donc inutile

Ce sera je pense plus facile pour adapter à la boucle sur les fichiers. Si tu as besoin d'aide, précise la demande (le dictionnaire est-il toujours le même, noms des fichiers en entrée et en sortie…).

Bonnes vacances

Suggestion pour la boucle (si le dictionnaire ne change pas) :

for i in fr*.dat; do ./traduc.sh "$i" "${i/fr/en}"; done

Dernière modification par pingouinux (Le 07/04/2012, à 10:04)

Hors ligne

#15 Le 07/04/2012, à 09:57

Postmortem

Re : [Résolu] Petit coup de pouce pour script Bash (awk)

kernelZero a écrit :

Salut Postmortem,

Je ne vois pas bien ce que tu entends par fichier texte à colonnes ?
En fait, c'est un script utilisé pour du calcul numérique et il n'y a pas de colonnes mais juste des instructions, de longueurs differentes pour chaque ligne.
Il y a aussi certaines precautions à avoir, notamment observer un espace en debut de certain paragraphe.

Je vais de suite tester la solution de pingouinux ...

Encore "Thanks a lot" de s'interesser à mon probleme !

Ce que je voulais dire, c'est si ton fichier en.dat avait pu être de cette forme :
$GROUP;NAME;CREATE;TOTO
ELEMENT;$L2$;$L3$
NODE;1234;4567
RETURN
[...]

À la place de :
$GROUP NAME CREATE TOTO
ELEMENT $L2$ $L3$
NODE 1234 4567
RETURN
[...]

Ça aurait été plus simple pour ta traduction des mots ayant une espace.
Enfin, avec pingouinux, y'a pas de problème mais que des solutions !

Bon week-end à tous (et bonnes vacances pour certains GRRR) !


Mot' a dit : « Un Hellfest sans Slayer, c'est comme une galette-saucisse sans saucisse ! »

Hors ligne