#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) (L6 ?)
CONDITION NOM --> NAME_CONDITION
Du coup, avec un :
sub("_"," ",$k)
el ligne 17 c'est réglé !
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)
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