Modifier un fichier texte avec awk en bash... (Mac)
Résoludubcek Messages postés 18702 Date d'inscription Statut Contributeur Dernière intervention -
Mon fichier listfull.txt (mame -listfull > listfull.txt) en sortie de MAME se présente ainsi (le nombre d'espaces, remplacés ici par des _ pour une lecture plus proche de la réalité, entre les noms et les descriptions varient, mais le premier " est toujours le 19e charactere de la ligne...) :
Name:_____________Description:
005_______________"005"
10yard____________"10-Yard Fight (World, set 1)"
10yard85__________"10-Yard Fight '85 (US, Taito license)"
10yardj___________"10-Yard Fight (Japan)"
Mon but est de transformer donc ces lignes en :
cmd00001="005";
butt00001="005";
cmd00002="10yard";
butt00002="10-Yard Fight (World, set 1)";
cmd00003="10yard85";
butt00003="10-Yard Fight '85 (US, Taito license)";
etc...
De manière à pouvoir les injecter sous forme de variables dans Flash...
Et franchement, j'avoue avoir de sérieuses lacunes, balbutiant en bash et surtout découvrant sed, qui me semble être la bonne voie pour atteindre mon but...
D'avance merci, toutes aides, explications, solutions (voir autre methode*) seront les bienvenues...
* pas de php, uniquement qu'en bash, mon projet étant un frontend local et étant de plus limité également par ce qu'accepte de lancer flash localement via un bouton...)
Sincèrement, MAMu_
- Gawk mac
- Mon adresse mac - Guide
- Nettoyer son mac - Guide
- @ Sur mac - Guide
- Espace insécable word mac - Guide
- Temperature mac - Guide
51 réponses
- 1
- 2
- 3
Pour transformer la sortie de MAME en variables utilisables dans Flash, plusieurs échanges préconisent d'utiliser awk plutôt que sed afin d'extraire et réorganiser listfull.txt de façon automatisée. L'approche retenue propose d'utiliser FIELDWIDTHS dans awk pour fixer les longueurs de champs et générer des lignes cmd00001=...; butt00001=...;, avec un incrément de n et une sortie ligne par ligne. D'autres participants suggèrent aussi de trier les entrées par description puis de réorganiser les champs selon les besoins, et d'échapper les caractères spéciaux (&, <, etc.) pour l'injection dans Flash. Enfin, les tests préliminaires montrent que l'export produit des fichiers variables compatibles avec l'injection dans Flash et que la gestion des caractères spéciaux nécessite un pré-traitement des entrées.
je utiliserais awk plutôt que sed :
$ awk 'BEGIN{FIELDWIDTHS="18 99"} !/^Name/ {gsub(" ", "", $1); printf "cmd%05d=%s;\nbutt%05d=%s;\n", ++n, $1, n, $2}' listfull.txt
cmd00001=005;
butt00001="005";
cmd00002=10yard;
butt00002="10-Yard Fight (World, set 1)";
cmd00003=10yard85;
butt00003="10-Yard Fight '85 (US, Taito license)";
cmd00004=10yardj;
butt00004="10-Yard Fight (Japan)";
$
awk est plus indiqué en effet, ne serait-ce que pour le formatage des nombres et la numérotation. Mais voici tout de même une possibilité de résolution avec sed :
sed -n '/"/{=;p;}' listfull.txt | sed -n '{N;s/\n/ /;p;}' | sed 's/\(^[^ ]*\) \([^ ]*\) *\(.*\)$/cmd\1="\2";\nbutt\1=\3;/'
Ca peut probablement se simplifier...
Une solution en bash :
$ cat plop # Le fichier de départ
005 "005"
10yard "10-Yard Fight (World, set 1)"
10yard85 "10-Yard Fight '85 (US, Taito license)"
10yardj "10-Yard Fight (Japan)"
$ ./foo.sh # L'exécution du script
$ cat bar # Le fichier en sortie
cmd00001="005";
butt00001="005";
cmd00002="10yard";
butt00002="10-Yard Fight (World, set 1)";
cmd00003="10yard85";
butt00003="10-Yard Fight '85 (US, Taito license)";
cmd00004="10yardj";
butt00004="10-Yard Fight (Japan)";
$ cat foo.sh # Le script
#! /bin/bash
exec > bar
i=1
while read nom des
do
n=$(printf "%05d" ${i})
echo -e "cmd${n}=\"${nom}\";\nbutt${n}=${des};"
((i++))
done < plop
$
Alors pourquoi le "presque" parfaite...?
J'ai utilisé la méthode de dubcek, la toute dernière, que j'envoie dans un nouveau fichier texte à l'aide de > newlist.txt :
awk '!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "cmd%05d=%s;\nbutt%05d=%s;\n", ++n, x, n, substr($0, 19)}' listfull.txt > newlist.txt
Le résultat est presque parfait, en effet il manque juste les guillemets pour la premiere variable :
cmd00001=005;
butt00001="005";
Cela devrait être :
cmd00001="005";
butt00001="005";
Mais franchement, vu votre rapidité, un enorme merci à tous sans exceptions, je vais pas chippoter et je vais essayer de rajouter ces guillemets par moi même :)
1000 merci :)
Donc j'ai mon bash principal qui marche nickel jusqu'à l'étape 3, celui-ci fait dans l'ordre :
1- Extraire listfull.txt à partir de MAME.SDL, OK
2- transformer listfull.txt en "variables.txt" que je pourrais charger dans mon interface Flash..., OK, Encore merci à vous...
3- Creer un fichier "test.bash" qui reprend à peu près la 2e étape mais en plus simple... et même en plus simple, je galère... :/
4- Rendre "test.bash" executable avec un chmod +x, OK...
Voilà ce que mon 3 est supposé faire, me mettre dans un fichier fullbash.bash ces lignes :
./config.bash 005
./config.bash 10yard
./config.bash 10yard85
etc...
(le config.bash que j'ai déjà codé marche parfaitement, il est fort simple : il me crée un fichier $1.bash contenant une ligne de commande et le rend executable (ou $1 est donc 005, 10yard,...) la ligne de commande lance MAME avec les options et les variables d'environnement dont MAME a besoin)
J'ai donc essayé de modifier le code du 2 (celui que vous m'avez fourni) en ceci :
awk '!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "./config.bash %s;\n", ++n, x, n, substr($0, 19)}' listfull.txt > test.bash
Et j'obtiens comme résultat (dommage, loupé), ceci :
./config.bash 1;
./config.bash 2;
./config.bash 3;
./config.bash 4;
./config.bash 5;
./config.bash 6;
./config.bash 7;
Bref, c'est pas encore ca...
Mon bash principal et ses 4 étapes actuellement :
# Where is MAME ? (ie /Users/prod/Desktop/MAME/mame)
var_dirMAME="/Users/prod/Desktop/MAME/mame"
#-----------------------------------------------------
# Create listfull.txt from mame SDL
$var_dirMAME -listfull > listfull.txt
# Modify listfull.txt in variables.txt usable with Flash
awk '!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "cmd%05d=\"%s\";\nbutt%05d=%s;\n", ++n, x, n, substr($0, 19)}' listfull.txt > variables.txt
# Modify listfull.txt in test.bash (all games bash creation)
awk '!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "config.bash %s;\n", ++n, x, n, substr($0, 19)}' listfull.txt > test.bash
# test.bash is executable
chmod +x test.bash
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre questionawk plus court :
awk -F '[ ]+"' '!/^Name/ {printf "cmd%05d=\"%s\";\nbutt%05d=\"%s;\n", ++n, $1, n, $2}' listfull.txtouawk -F '[ ]+"' '!/^Name/ {printf "./config.bash %s;\n", $1}' plop
Par contre et là je m'éloigne un peu du sujet... mais à peine... Comme j'essaie de rendre mon Front end compatible PC et Mac, l'executable flash, ou l'app flash, ne changeant pas, je me suis penché sur gawk, l'equivalent de awk sur PC pour essayer d'obtenir les mêmes fichiers à l'identique... :/ et là courage... il me sort que "'" n'est pas accepté, j'ai beau essayer la commande -F pour la source, ca ne veut rien savoir... Bref, je galère (qui a dit encore une fois ^^?)
Donc si qqn est capable de me donner la concordance entre les deux lignes format bash :
awk '!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "cmd%05d=\"%s\";\nbutt%05d=%s;\n", ++n, x, n, substr($0, 19)}' listfull.txt > variables.txt
et
awk '!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "../config.bash %s\n", x}' listfull.txt > CREATEALLBASH.bash
(ou la variante plus courte de dubcek qui me sort egalement des erreurs quand je la passe sous gawk...)
en utilisant gawk (ou awk, pas trop compris la difference, les deux étant fourni dans le bin de gawk PC), franchement ca serait plus que bienvenue ^^
C'est clair que je pourrais fournir les fichiers mac légèrement modifié pour le PC... mais je préférerais qu'un utilisateur lambda, en lançant un batch sur PC, et ce quel que soit la nouvelle version de MAME, puisse obtenir de lui-même les fichiers dont mon Front-End a besoin... :/
Merci d'avance si qqn s'est déjà penché sur le sujet "awk>gawk"... :)
awk -f fichier.txt listfull.txt > variables.txt
cat ou type fichier.txt
!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "cmd%05d=\"%s\";\nbutt%05d=%s;\n", ++n, x, n, substr($0, 19)}
En gros voilà, tout marche bien comme prévu dans le developpement de mon FE, mais malheureusement le listfull.txt que je traite n'est pas assez complet pour recuperer toutes les données et variables dont j'ai besoin selon les choix de l'utilisateur...
Donc je me suis tourné vers le MAME -listXML qui lui me sort un fichier bien plus complet avec toutes les variables dont je pourrais avoir besoin, mais qui est le XML le plus barré que je connaisse ^^...
Je l'ai regardé avec Advanced XML converter pour voir un peu son arborescence... et il est bien barbare...
Ci joint le fichier XML en sortie de MAME... :
http://zemamu.free.fr/listfull.xml.zip
Exactement sur la même construction que la sortie de mon listfull.txt, je voudrais récupérer un fichier qui commencerait par :
MAMEver="0.148u2 (Mar 19 2013)";
et ensuite viendraient mes variables à charger dans flash toujours avec le même système d'incrementation pour chaques entrées... (ici exemple 10yard, la 2e entrée...)
name000002="10Yard";
sourcefile000002="m58.c";
ismechanical000002="yes";
cloneof000002="10yard";
description000002="10-Yard Fight (World, set 1)";
year000002="1983";
manufacturer000002="Irem";
//Les 2 lignes 'ismechanical="yes" et "cloneof" ne sont pas présentes à tous les coups,
//elles ne sont pas là pour 10yard, mais comme il me les faut si elles sont présentes...
type000002="raster";
rotate000002="0";
width000002="256";
height000002="224";
status000002="good";
emulation000002="good";
color000002="good";
sound000002="good";
graphic000002="good";
//Ces valeurs ci dessus sont vers la fin de chaque entrée
Croyez vous que vous pourriez encore une fois me donner ce coup de main...?
Ou au moins m'indiquer la voie pour traiter les 4/5 premieres lignes, et ensuite j'essaierai d'appliquer par moi-même... Mais c'est vraiment des lignes de commandes de barbare awk quand on découvre... ^^
Sincèrement, Aymeric...
type|rotate|width|height|status|emulation|color|sound|graphic
et les afficher sous la forme : <nom><compteur>="<valeur>";
Si la variable n'existe pas, ne pas la lister ou la lister avec <valeur> = null, indifferement... car au final tester si la variable existe ou si elle est egale à NULL dans mon projet reviens au même...
$ cat a0.awk
/<game name=/ {c1=";"; c2=";\n"; ns=sprintf("%05d", ++n)
gsub("=", ns "="); sub(">", ""); print $2 c1
if($3 ~ /sourcefile/)print $3 c1
if($4 ~ /ismechanical|cloneof/)print $4 c1
if($5 ~ /ismechanical|cloneof/)print $5 c1}
/<description>|<year>|<manufacturer>/ {split($0, t, "[<>]")
print t[2] ns "=\"" t[3] "\"" c1}
/<display/ {gsub("=", ns "="); print $3 c2 $4 c1
if($5 ~ /width|height/)print $5 c1
if($6 ~ /width|height/)print $6 c1}
/<driver/ {gsub("=", ns "="); print $2 c2 $3 c2 $4 c2 $5 c2 $6 c1}
$
$ awk -f a0.awk listfull.xml > variables.txt
$
$ head -20 variables.txt
name00001="005";
sourcefile00001="segag80r.c";
description00001="005";
year00001="1981";
manufacturer00001="Sega";
type00001="raster";
rotate00001="270";
width00001="256";
height00001="224";
status00001="imperfect";
emulation00001="good";
color00001="good";
sound00001="imperfect";
graphic00001="good";
name00002="10yard";
sourcefile00002="m58.c";
description00002="10-Yard Fight (World, set 1)";
year00002="1983";
manufacturer00002="Irem";
type00002="raster";
$
BEGIN {FS="[ \t>]+"; v="name=|sourcefile=|ismechanical=|cloneof=|type=|rotate=|width=|height=|status=|emulation=|color=|sound=|graphic=" ; c=";"}
/<game / {ns=sprintf("%05d", ++n)}
/<game |<display |<driver / {for(f=2; f<=NF; f++)if($f ~ v){sub("=", ns "=", $f); print $f c }}
/<description>|<year>|<manufacturer>/ {split($0, t, "[<>]"); print t[2] ns "=\"" t[3] "\"" c}
En fait j'ai du changer et adapter légérement le code (l'ancien, je vais regarder le nouveau) pour que ce soit compatible 'variables' en chargement externe sous Flash... Mais pas de soucis, m'en suit sorti en repassant 2 fois à la moulinette awk et sed (comme quoi j'avance et je commence à comprendre) le fichier de sortie de ton code :)
Avant:
var00001="aaaa";
var00002="bbbb";
var00003="cccc";
...
Maintenant:
var00001=aaaa&var00002=bbbb&var00003=cccc&...
pas besoin de guillemets (alors que dans Flash ils sont obligatoires, en chargement externe je les retrouvais dans mon champ texte, et le ";" plus passage à la ligne n'est pas reconnu en chargement externe, il faut direct un "&" et on passe de suite à la variable suivante... (Ca peut servir pour certains qui voudraient faire ce genre de manip variables declarées en externe > import en Flash)
Enfin, merci en tout cas, 1000 fois :)
Je fait donc une fois ton code awk éfféctué les opérations suivantes sur le fichier variables.txt obtenu :
awk -f ext2.awk variables.txt > variables2.txt
sed -i "s/\"//g" variables2.txt
avec ext2.awk : BEGIN {ORS=""} {print} END {print "\n"}
Je remplace donc tous les retours à la ligne (\n) par du vide...
et sed me supprime donc tous les guillemets... (pour le & j'avais directement tapé dans ton ancien code en remplacant les ";" par "&", nickel...)
J'obtiens donc le bon résultat, mais en plus d'étapes... :)
Mais comme j'aime bien les trucs carrés, j'ai essayé de faire pareil mais uniquement avec ton code, soit en une seule étape...
BEGIN {FS="[ \t>]+"; v="name=|sourcefile=|ismechanical=|cloneof=|type=|rotate=|width=|height=|status=|emulation=|color=|sound=|graphic=" ; c=";"}
/<game / {ns=sprintf("%05d", ++n)}
/<game |<display |<driver / {for(f=2; f<=NF; f++)if($f ~ v){sub("=", ns "=", $f); print $f c }}
/<description>|<year>|<manufacturer>/ {split($0, t, "[<>]"); print t[2] ns "=\"" t[3] "\"" c}
Bon sur la dernière ligne, enlever les guillemets \" à la fin, ok...: "=" t[3] "" c}
ca m'enlève bien les guillemets pour les champs description, year, manufacturer... OK
Pour afficher un & en fin de chaque lignes, je modifie sur la première ligne c=";" par c="&"... ca marche parfaitement...
et voilà...
Il me reste donc à réussir à enlever les guillemets pour tous les autres champs, et à réussir à supprimer les retours à la ligne... et je ne réussis pas... cela doit pourtant bien être faisable...
Ton code avec mes modifs en l'état:
BEGIN {FS="[ \t>]+"; v="name=|sourcefile=|ismechanical=|cloneof=|type=|rotate=|width=|height=|status=|emulation=|color=|sound=|graphic=" ; c="&"} {ORS=""} {print} END {print "\n"}
/<game |<display |<driver / {for(f=2; f<=NF; f++)if($f ~ v){sub("=", ns "=", $f); print $f c }}
/<description>|<year>|<manufacturer>/ {split($0, t, "[<>]"); print t[2] ns "=" t[3] "" c}
Si tu vois comment réussir pour les guillemets restant et les retours lignes avec une commande awk compléte et non pas 2 awk et 1 sed comme je fait... volontiers ;)
Merci d'avance...
et il y a des balise <description> et d'autres qui contiennent des &, ca peut être un problème, non ?
Bon je pense que plusieurs etapes vont donc quand même être indispensables...
Je vais faire plus de test ^^
Merci
BEGIN {FS="[ \t>]+"; v="name=|sourcefile=|ismechanical=|cloneof=|type=|rotate=|width=|height=|status=|emulation=|color=|sound=|graphic=" ; c="&"}
/<game / {ns=sprintf("%05d", ++n)}
/<game |<display |<driver / {for(f=2; f<=NF; f++){if($f ~ v){sub("=", ns "=", $f); gsub("\"", "", $f); printf $f c }}}
/<description>|<year>|<manufacturer>/ {gsub("%", "%%"); gsub("&", "and"); split($0, t, "[<>]"); printf t[2] ns "=" t[3] c}
printf au lieu de print n'ajoute pas de \n
gsub supprime les "
% est traité comme format (par ex :%d) par printf, pas un double : %%
Je n'ai pas encore essayé ton code car le nouvel écueil se situe en amont à mon avis, avant la numérotation...:
Je tire correctement toutes mes variables, et les affichent bien dans mon projet flash qui en analysent même certaines pour afficher des images au lieu de la valeur texte (ex logos manufacturer)...
mais la liste des variables commençant par name, puis description (voir copie suivante après ta moulinette (pas modifié les retours à la lignes, ni les "< > & ' "), je n'obtiens pas les variables dans l'ordre alphabétique de (description), ce qui n'est pas top, mais dans l'ordre alphabétique des shorts names de fichiers (name...)
Donc si je comprends bien... ma nouvelle moulinette que je vais essayer de faire par moi-même grace à toute ton aide et à tes dernieres indications, toujours en multipasse pour l'instant, plus simple pour apprendre... ^^ :
il faudrait que je réussisse "avant numérotation"...
1) à faire une seule ligne pour chaque groupe de variables, chaque nouvelle ligne commencant par name=.... en modifiant ton awk, je doit pouvoir réussir à faire ca..
name=005;sourcefile=....description=....
2) réussir à inverser dans chaque ligne le champ "name=<value>" et le champ"description=<value>...
3) faire un sort sur le fichier obtenu...
4) remettre la numérotation qui s'incremente à chaque nouvelle description= pour toutes les variables possibles de chaque ligne...*
5) remettre en une seule ligne...
Je ne sais pas si réfléchir à haute voix sur le forum est accepté, mais je me suis dit que toute cette reflection : projet, problème, solution pourrait être utile pour d'autres... :)
*peut être ce qui va être le plus difficile pour moi... :)
- 1
- 2
- 3
awk '!/^Name/ {x=substr($0,1 ,18); gsub(" ", "", x); printf "cmd%05d=%s;\nbutt%05d=%s;\n", ++n, x, n, substr($0, 19)}' listfull.txt