Bonjour à tous,
Voici ce que je souhaite faire :
Je dispose de dossiers et sous dossiers contenant des images dont le nom est structuré ainsi :
racine fixe égale à G_
puis code produit en 6 caractères
séparateur fixe _
puis code couleur sur 2 à 4 caractères
séparateur fixe _ZP_
Index d'image valeur 1 à 10
séparateur fixe _
Code interne unique sur 6 caractères
Exemple G_12345_320_ZP_1_565656.jpg
est l'image numéro 1 du produit 12345
En parallèle je dispose d'un énorme listing excel composé en colonne A des codes produits et en colonne B de la taille de ces produit (champ texte)
Je cherche à réaliser un script qui pour chaque image va chercher si elle en trouve la taille dans le listing et si oui écrira dans une donnée EXIF la valeur du champ texte.
Si le code produit n'est pas dans le listing, ne rien écrire.
le script demande donc de la récursivité et l'utilisation de EXIFTOOL (que j'ai téléchargé et installé mais sans savoir faire mieux...)
Avez-vous des idées ?
Merci
Speed Moock,
Au lieu d'empiler plusieurs outils, tu pourrais aussi en choisir un qui fait tout.
Et le langage Swift 3, te permet d'essayer interactivement (comme en Bash, en Perl, en Python ou Ruby) dans un playground créé dans XCode, ou dans un terminal. Ou sur le portaill d'IBM (qui a adopté Swift)
https://swiftlang.ng.bluemix.net/
Un gist (un snippet, un bout de code d'exemple) pour charger une image e tse préparer à lire ses propriétés :
https://gist.github.com/tsuyukimakoto/c7ef8ed096a3e22bda7d
Autre exemple:
http://erkkiahola3.blogspot.de/2015/11/exif-data.html
Par ailleurs, exporte le fichier Excel en csv qui est un simple fichier texte, plus facile à lire dans n'importe quel langage, sur n'importe quel OS.
Swift3 est open source et fonctionne très bien sur Linux aussi.
J-P
Bonjour Jaypee,
Merci pour la proposition mais déjà que je suis laborieux en applescript, je ne suis pas rendu sur d'autres langages...
Je vais déjà essayer en AS, je passerai ceinture noire plus tard !
Pas de problème, Speed Moock. Si un outil est une solution, quel problème résoud-il que les autres outils ne savent pas mieux résoudre ?
Applescript demande une connaissance précise du modèle d'objets de macOS. Très facile à lire mais très difficile à écrire avec des nuances comme file, alias, POSIX file etc. Il n'est pas vraiment fait pour les apprentis programmeurs, et source de frustration. Facile pour des choses faciles. Mais quel autre langage ne l'est pas.
Avec la même énergie, on apprend un langage que beaucoup de gens maitrisent avec bien moins de frustrations durant le voyage.
J-P
C'est assez simple à faire avec Livecode (http://www.livecode.com). La version Community est opensource et gratuite. C'est un successeur d'Hypercard qui en a conservé le langage (et dont l'Applescript s'est aussi grandement inspiré)
Comme le dit Jaypee, ce sera beaucoup plus simple de travailler sur un fichier texte issu d'excel (je conseille plutôt le format texte tabulé que le vrai csv).
Je te joins 2 fichiers ("piles") qui devraient te servir de base. Un pour lister les fichiers d'une arborescence, l'autre pour utiliser Exiftools (il permet de géotagger un fichier)
livecode.zip ( 4.94 Ko )
: 7
.
A mon avis on peut se passer de EXIFtools
Il y a dans macOS un petit trésor caché, la commande sips. Essaie un man sips.
Par exemple: sips -g pixelHeight /chemin/de/mon/image.jpg
Et AppleScript a dans sa liste d'applications dont on peut ouvrir le dictionnaire "Image Events"
Il faut commencer par sélectionner une image dans le Finder puis:
Merci à tous !
Je vais profiter du long WE pour m'y pencher.
A bientôt
L'un des scripts de la documentation de "Imagine Photo" fait le boulot, mais le seul champ susceptible de recevoir le texte libre est la champ "info".
Bonjour à tous
En lisant le post de Pbell je viens de me rendre compte d'un énorme quiproquo
La donnée taille dans mon fichier excel est la valeur en cm du produit photographié dans l'image, pas la taille en pixels de l'image 😜😜😜😜
Quant au listing excel il contient 30000 lignes...
Voilà je pense que cette précision de "taille" réorientera vos réflexions
Merci !!
Bonjour,
Merci pour la précision quant aux tailles en cm (je m'en doutais) et à la taille du fichier Excel.
Il reste à savoir ce que tu vas faire de ces dimensions produits (lecture dans d'autres applications,...) une fois mises "dans l'image" pour savoir quel est la meilleure place pour enregistrer cette donnée.
Il y a en effet beaucoup de champs Exif possibles
Cordialement
Une recommandation de haut niveau.
Ce traitement est un cas d'école pour le "design pattern" appelé "Visiteur" qui sépare deux fonctions:
- Celle qui permet de parcourir complètement la structure à traiter, c'est le parcours récursif de liste ou d'arbre, à chaque point visité, on applique la deuxième fonction à tous les éléments intéressés à cette étape de la visite, puis on continue la visite.
- Celle dite fonction de visite qui fait le traitement désiré.
Dans le cas présent, la visite pourrait privilégier la liste Excel, et ce serait seulement un parcours de liste. Si on tient à récursiver, on applique la fonction de visite à la tête de liste, puis on parcourt le reste de la liste.
Soit on privilégie les images, et le parcours est un parcours d'arbre, la fonction de visite doit faire la recherche dans la liste Excel et l'écriture de l' EXIF. Dans ce cas, il peut être intéressant de trier au préalable cette liste selon la clé de recherche. On peut aussi envisager de charger la liste complètement en mémoire : Si la ligne fait 1Ko, on consommera 100 Mo de mémoire, une paille.
Comme il est plus commun de vouloir appliquer un certain traitement à tous les fichiers à partir d'une racine, cette approche est recommandée car plus générique et ré-utilisable
Pour l'adaptation en AppleScript:
- Les appels récursifs se signalent par le mot-clé "my"
on recFonction(x, y, z)
if test de fin then
finir
else
my recFonction(x', y', z')
end if
end recFonction
- Comment passer la fonction de visite, pour séparer le parcours du traitement?
Mon exemple simple
Bonjour,
Pour répondre à Pbell :
Le nombre d'images sera toujours inférieur au nombre de lignes du listing excel. Il y a donc une inclusion de l'un dans l'autre, et donc il faut parcourir les dossiers images et en chercher la présence dans le listing.
si trouve => écriture de la donnée dans le champ exif "info" (quitte à écraser un contenu déjà existant)
si non trouvé = > on ne fait rien
Pour information la métadonnée est ensuite reprise sur un site internet comme élément descriptif du produit. Il y a certainement plus simple pour afficher une taille produit sur un site mais on ne me laisse pas le choix et on me demande que l'information soit dans l'image en tant que métadonnée.
@Jaype : merci pour iMagine, je découvre au passage une appli sympa !
Merci
Je complète mon exemple avec un parcours d'arbre. Vraiment c'est tout dans la subtilité, et ça demande énormément d'attention.
Bonsoir,
Tu peux aussi mettre ton information dans le champs "commentaires" que l'on vois avec un cmd+i .
Ce champs est facilement accessible en applescript avec un script genre : set comment of "chemin du fichier" to "taille du produit" .
Dans ce cas pas besoin d'exiftool.
Exemple avec un fichier texte:
00001 commentaire taille
00002 le com 2
00003 le com 3
00004 le com 4
00005 le com 5
00006 le com 6
00007 le com 7
00008 le com 8
00009 le com 9
00010 le com 10
00011
et un script style:
set CheminTXT to choose file with prompt "Sélectionnez le fichier texte" --Choix du fichier texte
my inspecter(cheminIMG, CheminTXT)
end tell
on inspecter(un_dossier, CheminTXT)
tell application "Finder"
-- traitement des fichiers image:
set les_fichiers to files of un_dossier
repeat with chaque_fichier in les_fichiers
-- traitement d'un fichier image
set nom to name of chaque_fichier --obtient la nom du fichier image
set code_produit to (items 3 thru 7 of nom) as string -- du troisième caractère au septième
-- traitement du fichier texte
open for access CheminTXT
read CheminTXT
set le_texte to the result -- récupère le fichier dans la variable
close access CheminTXT
set AppleScript's text item delimiters to (ASCII character 13)
set toutes_les_lignes to (every text item of le_texte) as list -- récupère les lignes du fichier
set AppleScript's text item delimiters to ""
repeat with i from 1 to count of toutes_les_lignes
set la_ligne to item i of toutes_les_lignes as string -- chaque ligne
set long to length of la_ligne -- si la ligne fait plus de 6 caractères(5 code produit+1tab)
if long > 6 then
set code_prod_list to items 1 thru 5 of la_ligne as string -- recupère la code produit
if code_produit = code_prod_list then -- compare
set lecom to items 7 thru -1 of la_ligne as string --si code produit = alors récupère la suite de la ligne
set comment of chaque_fichier to lecom -- écrit le commentaire
end if
end if
end repeat
end repeat
-- traitement des dossiers images:
set les_dossiers to folders of un_dossier
repeat with chaque_dossier in les_dossiers
-- traitement du dossier
my inspecter(chaque_dossier, CheminTXT)
end repeat
end tell
end inspecter
tell application "Finder"
display dialog "Ok j'ai terminé"
end tell
@Zeltron54,
Le champ Commentaires ne semble pas interrogeable par la commande sips, ni par la lecture des données EXIF.
Si la description L x H x P doit être exploitable par une application web, elle doit être accessible par une interface standard.
Il y a un module Node.js qui lit les données EXIF
https://github.com/RodrigoEspinosa/exif-cli
En revanche le champ Description est le champ EXIF imageInfo, et extractible par sips -g description photo.jpg
D'ailleurs, sachant cela, sips -s description "texte de la description" /chemin/vers/la/phooto.jpg est une interface d'écriture simplifiée:
un "do shell script" pourrait suffire.
J-P
Merci Zeltron pour cette proposition, mais je l'avais écartée dès le départ car les personnes qui récupèrent mes images sont sur windows et disent ne pas savoir récupérer les commentaires finder.
A+
Bonjour,
Effectivement les seuls champs qui peuvent être utilisé avec des images, tant sur Mac que sur Windows doivent être des champs EXIF ou IPTC.
Les autres champs ne seront pas transférés d'un OS à l'autre (dommage que Microsoft n'ai pas aussi copié cette partie lorsqu'il a crée Windows).
Commençons par le language : nous avons jusqu'à 30 000 lignes dans le fichier Excel et sans doute autant de fichiers image. Donc la meilleure solution semble être un script shell Unix en terme de vitesse.
Cependant, j'ai préféré un script Applescript avec des appels Unix pour 2 raisons:
1) il faut commencer par demander à l'utilisateur le nom du dossier racine et du fichier Excel : c'est facile en AS. mais je ne pense pas que cela soit possible en shell Unix
2) J'ai des lacunes en shell qui ne me permettent pas d'assurer un script qui fonctionne. Mais les autres contributeurs pourront aussi traduire en shell
La demande pose 3 problèmes:
- récursivité dans l'arborescence des dossier et sous dossiers
- recherche rapide, à partir du produit, dans le fichier Excel, pour trouver la colonne B content un texte
- ajout de ce texte dans l'image elle-même (EXIF)
Récursivité:
La solution que j'ai retenue fonctionne, mais sera sans doute longue (peut être trop longue) sur 30000 fichier. Inutile de dire que je n'ai pas tester sur ce nombre, mais sur environ 200 images. C'est le point critique de performance. Je me suis basé sur la fonction "entire content" qui extrait tous les fichiers situés dans une arborescence. Si elle a le mérite d'être simple, ce n'est pas performant et j'ai de grands doutes sur 30 000 fichiers. Il sera sans doute utile de lancer le script plusieurs fois sur des dossiers de niveaux inférieurs pour se limiter à environ 1000 à 2000 images à chaque fois.
Si cela pose un problème, dis le, et je ré-écrirai cette partie en vrai récursivité. Il reste que 30 000 en un seul coup risque d'être très long.
Recherche rapide du produit dans le liste Excel:
Là, je me suis dis "facile !", mais c'est en fait là où j'ai passé le plus de temps. La fonction la plus rapide est une commande shell Unix 'grep'. 'Grep xxx yyy' est capable de trouver la ligne contenant xxx dans le fichier yay... Oui, mais qu'est-ce qu'une ligne ?
En fait, grep considère, comme beaucoup de logiciels, qu'un ligne est un ensemble de caractères se terminant par le caractère spécial linefeed (LF ascii=10). Oui mais... Excel en text tabdelimiter, en cdv, en UTF text,... ne mets pas ces fameux linefeed, mais des Carriage Return (CR ascii=13) ! donc pour grep, tout le fichier n'est qu'une ligne !!!
En analysant les différents format de sortie Excel, la solution est de sauvegarder ton fichier Excel en format "Windows fomatted text": là les fin de lignes sont faites avec le CR et le LF ! Du coup le grep fonctionne parfaitement.
Dans ce format de sortie Excel, la séparation entre les colonne se fait par une tabulation (ascii=9).
Pour les curieux, j'ai ajouté un '^' qui signifie que ce que je cherche doit être en début de ligne (au cas où la colonne B contienne des chiffres comme un code produit !)
Ajout du texte dans l'image:
Pas de doute ici, l'instruction à utiliser est le exiftool. Reste à savoir quel tag doit être utilisé. J'ai choisi 'imageDescription' car c'est une partie de l'EXIF standard et donc cela aura plus de chance d'être lu par de nombreux logiciels.
Voici le script résultant : (testé bien sûr)
Salut
Merci Pbell
Toujours clair et concis merci !!
Je teste mercredi sur un vrai dossier et un vrai listing de plusieurs milliers d'éléments
Merci
Juste une petite remarque à propos d'EXIFtools, il n'est pas indispensable, car l'appli gratuite "iMagine Photo" est 100% orientée Applescipt, offrant le dictionnaire qui va bien.
Ensuite, je poursuis mon idée d'avoir tout le fichier Excel chargé en mémoire, qui est peut-être réaliste, ou pas...
Bonsoir Jaypee,
Je ne connaissais pas l'application iMagine Photo que je viens de télécharger sur ton conseil. Merci
Effectivement, le dictionnaire des commandes Applescript est assez complet quant à la manipulation des images.
Cela en fait un outil très intéressant.
Par contre, en terme de données EXIF seules, le type de données gérées est beaucoup plus limité que Exiftool: le dictionnaire 'Exif type' liste environ 26 types.
Exiftool ne traite que les données exif, ce qui est bien plus limité que iMagine Photo, mais il le fait bien mieux avec des centaines de tag possibles.
Je ne les ai jamais compté, mais si tu fais "exiftool -list -EXIF:All" sur le Terminal tu auras la liste. De plus Exiftool ne gère pas que les données purement Exif, mais aussi d'autres tags: "exiftool -list" te donnera les tag Exif et les autres (environ 39 pages de liste de tags sur mon moniteur 27' !!)
J'ajouterai que Exiftool dispose aussi de fonctions de traitement en masse directement au niveau Unix, comme par exemple traiter tous les fichiers d'un dossier en une seule instruction.
En résumé, pour traiter l'image elle-même (dimensions, échelle, rotation), la modifier (ajouter des formes/shapes,...) iMagine Photo est parfait. Merci encore pour l'information.
Pour traiter uniquement les tag exils ou autres, je préfère rester à exiftool, qui est aussi gratuit.
Cordialement
Merci à tous également... Un simple sujet comme celui-ci peut être l'occasion de recherche, et d'acquisition d'expérience.
Je m'intéresse en ce moment aux les performances et au comportement général d'AppleScript
La gestion de mémoire semble le point faible, dans ce sens que les performances baissent fortement lorsque une mémoire importante est requise. J'inclurai mon programme de test pour simuler une "base de données", un tableau de 20000 produits + dimensions, comme ceux que Speed Moock utilise.
La seule génération des produits (boucle et formatage de texte) ralentit significativement au delà de 8500 à 9000 articles.
Ensuite des recherches aléatoires de produits donne des résultats assez médiocres, de l'ordre de 3 à 4 secondes pour 10000 articles.
Comme je pressens la dégradation non-linéaire des performances (données x 3 n'impliquent pas performances x 3, mais plus) ma conclusion serait que:
- Si la communication inter applis fonctionne bien, il faut privilégier la délégation des fonctions complexes aux applis qui savent bien le faire, comme la recherche du produit dans la base. Ou alors prévoir une petite base de données SQL (SQLite est en standard, utilisé par le système lui-même)
- Si cette communication est pareillement médiocre, je crains qu'AppleScript soit limité par ses performances, il faut s'intéresser à des solutions de programmation plus classiques comme Python, Ruby pour les langages de script, et à Swift puisqu'il est promu par Apple (et par IBM maintenant. Une digression sur IBM: Sur un parc de 35000 Macs et autant de PC Windows,les Macs ont coûté 500$ de moins de support par leur service informatique que les PC par an)
Et pendant que j'écrivais les résultats sont tombés:
- 3000 articles: Recherche < 1 s
- 10000 articles: Recherche autour de 3 ou 4 s
- 20000 articles: Recherche autour de 15 à 20 s
Ma méthode de recherche dans le tableau:
- Dichotomie jusqu'à tomber en dessous d'une limite de 100, où la recherche devient séquentielle
- Pour éviter les re-copies de mémoire, la recherche séquentielle est faite entre deux indices départ et fin, sans changer le grand tableau de départ. Normalement, comme il a été observé plus tôt, AppelScript passe ses arguments systématiquement par adresse, dont passer la liste, ne consiste qu'à passer l'adresse, sans recopie.
Conclusion: Non, avec AppleScript on ne doit pas tout charger en mémoire. Même si en quantité, elle ne manque pas, sa gestion par AppleScript est extrêmement peu performante.
J-P
PS: Le code. Pour voir les messages défiler, cliquer sur le 3e l'icone (i) <flèche retour> <document avec des lignes> pour faire apparaître la console, puis choisir l'onglet "messages" ou "événements"
Bonjour à tous,
J'ai testé sur un dossier contenant 291 images (sans sous dossiers) et un listing d'autant de ligne correspondant pile poile au contenu du dossier.
Résultat : 3 minutes de travail (je suis sur un McBook pro 2.2 Ghz Intel Core i7 et avec 4Go de mémoire DDR3 à 1333 Mhz).... donc oui ça sera trés (trop) long sur mes dossiers de travail.
A+
Je recommande très vivement de créer une petite base de données sqlite3. Même depuis AppleScript, le temps de recherche est virtuellement nul avec une base de 32000 articles !
Etape 1: Générer le csv en Ruby. OK. C'est un one-liner, un peu chevelu... Durée pour 32000 articles: instantané. Dans un terminal taper irb (Interactive RuBy) c'est déjà installé en standard sur tous les Macs.
Ah oui quand même...
Bon je vais me creuser la tête pour adapter ce code à mon besoin...
Merci Jaypee !
Bonsoir,
Comme l'a justement indiqué Jaypee, ce type de demande est intéressant car il force à se poser d'autres questions et donc améliore la compréhension.
Je me suis donc penché sur la question de la vitesse.
Sur le grep, je n'avais aucun doute que cela soit la plus rapide. Je confirme grep est au niveau bas shell donc pas mieux.
Sur l'Exiftool, c'est affectivement assez rapide mais il ne faut pas oublier qu'une écriture de donnée EXIF implique en fait une ré-ecriture complète du fichier image; en effet la structure de données Exif implique l'insertion de la donnée (elle ne peut être ajoutée à la fin) et donc la ré-écriture de l'image.
La vitesse dépend des accès disque : disque dur/mémoire flash, accès serveur/carte disque, type de connexion (SATA, USB,...).
Elle dépend aussi de la taille de l'image traitée : je doute que des images pour un site web fassent 10Mb chacune, mais tout de même, ce sera plus long de ré-écrire 300Ko par image que 30Ko !
Il reste, comme l'a parfaitement décrit Jaypee, la question de l'utilisation de la mémoire par AS.
J'ai commencé par revoir le script pour utiliser la récursivité avec des listes de folder et de fichiers. Beaucoup mieux, mais pas encore assez.
J'ai donc mixé un script de base avec un appel de récursivité, pour réduire l'utilisation de la mémoire au maximum. Elle est maintenant dépendante, essentiellement du nombre niveau de sous dossiers.
Une fois la mise au point faite, et comme il me semblait que la performance était là, j'ai décidé de tester à plus grande échelle.
J'ai crée un fichier Excel avec 30 000 lignes de 2 colonnes:
1) code produit = nombre sur 6 chiffres,
2) texte de dimension = ("Dimension " & code produit & "cm") .. qui me permet de vérifier que le grep à partir du code produit est correct
Comme expliqué précédemment, j'ai enregistré cette liste de 30 000 produits/dimension sur un fichier Window Formatted text.
J'ai ensuite écrit un petit script qui me génère des images Jpg avec un nom du type G_xxxxxx_21_ZP_1_test.jpg.
Le script a fait varier xxxxxx de 120 000 à 131 200, donc 11200 images, que j'ai éclatées en 4 sous dossiers, regroupées dans un dossier parent.
Voilà pour l'environnement de test ! Je pense ainsi me rapprocher du besoin en terme de volume. Non ?
Lors de mes tests, je me suis aussi aperçu que les "log" que je mettais ralentissaient. Surtout, j'ai découvert que lancer le script à partir de l'éditeur est bien plus lent que de le sauver en tant qu'application puis de lancer l'application. Le moniteur d'activité confirme une plus faible utilisation de mémoire. Je pense que c'est en partie du à la notion d'historique de l'éditeur, qui n'existe plus en mode "application".
J'ai fait une première série de tests avec toutes les fonctions (boucle, récursivité, recherche produit, conversion de posix,...), sauf l'écriture Exif elle-même.
Par exemple, un premier test sur 200 images avec le nouveau script tourne en 23 secondes dans l'éditeur et seulement en 11 secondes en mode application !
Pour 1000 images, je passe de 2minutes 49 secondes à 1 minute seulement en mode application.
J'ai enfin supprimé les log directs pour passer à l'écriture d'un fichier log .txt, mais en passant via du shell ce qui ne ralenti pas (ou si peu !).
Comme vous pouvez le voir, j'ai limité les appels au Finder au maximum (le Finder est connu pour sa lenteur).
Une fois tous ces tests positifs, j'ai remis l'écriture des Exif. p
Désormais, pour 200 images, cela passe de 11 secondes à 62 secondes. Et encore, mes images sont petites (40Ko) !!
In finé, après quelques ajustements sur le script, j'ai lancé le script avec les 11200 images réparties en 4 sous dossiers, le fichier produits/dimensions de 30 000 lignes: temps de traitement total 49 minutes, tout compris !!
Je crois que je ne peux faire mieux !
...sauf à passer via un script shell direct.
Voici le script dernière version. Il faut l'enregistrer sous forme d'application et lancer l'application (ne pas lancer via l'éditeur !)
Merci Pbell !
Je teste ceci demain en environnement pro
Sur le nombre de niveaux d'arboresence tu as vu juste
Je peux faciliter le traitement en listant dans un excel le listing de mes images et en restreignant le fichier contenant les tailles à ces seules images
Par contre mes images font 5 Mo...
Bref je reviens vers vous avec des nouvelles sous peu
Speed Moock,
Vu la tournure que prend les choses, je crois que tu as intérêt à faire tout en shell script.
A la base fais juste un script "de visite" pour une image
- parsing du nom => produit
- dims=`sqlite3 db sql` # apostrophe inversées pour exécuter la commande, ajouter un dollar devant $dims pour lire la valeur de dims
- exiftools pour écrire l'info dans l'image
Puis le parcours... il existe en standard avec la commande shell find, exemple:
find . : liste tous les fichiers sous le répertoire courant (= le point)
find racine/ -name "*jpg" -exec mon_script.sh {} \; # Trouver tous les jpeg sous la racine et leur appliquer mon script
Conseil d'ami, AppleScript n'apporte plus rien à ce stade, hormis compliquer la tâche
J-P
Merci Jaypee,
Je suis assez d'accord avec toi. J'avais indiqué au départ que le language qui me semblait le plus approprié était le shell script.
Mais je ne suis que très novice en shell script, c'est pourquoi j'ai "habillé" le grep et le exiftool avec de l'Applescript.
Par exemple, lister les fichiers dont le titre commence par "G_" et se termine par ".jpg" avec récursivité sur les sous dossiers ne me pose pas de problème en shell, pas plus que le grep. Je devrais même pouvoir adapter le grep avec un pipe pour ne prendre que la partie après la tabulation.
Par contre, je ne maîtrise pas le traitement au milieu.
J'ai vu qu'il y a des boucles "for" en shell, mais aussi que certains exemples font des pipes "|" en lieu de place de cette boucle pour appliquer une instruction à chaque fichier.
J'ai plein de questions...et pas de réponses !
Il me semble que le pipe est OK à condition que chaque fichier trouvé avec le find ne nécessite qu'une seule instruction. Est-ce exact ?
si plusieurs instructions, il faut utiliser le "for"...?
Dans ce cas, quelle est la syntaxe pour avoir plusieurs instructions dans la boucle et surtout dans quelle variable est le fichier en cours ? Bref quel est l'équivalent des classique repeat/end repeat ou encore for/next (VBA)
Pour le parsing du nom, je devrais pouvoir trouver tout seul, mais pour ces points, tes commentaires, voire un exemple m'aiderait à mieux comprendre.
Merci d'avance.
@SpeedMock, si tes fichiers font 5Mo, de toute façon tu verra que l'essentiel du temps de traitement est l'insertion de la donnée Exif qui nécessite la recopie de l'image: soit, pour 30 000 images, une recopie de 150Go !! d'où l'intérêt de vérifier ta vitesse d'accès disque
Cordialement
@PBell
Il faut raisonner "Inversion de controle" ou "méthode Hollywood" ou "Ne nous appelez pas, NOUS vous appelerons".
Ce n'est pas au script de trouver où chercher il faut lui apporter comme argument (c-à-d $1, puisque $0 est le nom du script lui-même) donc $1 est un nom de la form G_*.jpg
Et ce script travaille sur ce seul fichier mais fait bien le boulot de A à Z.
- extrait le produit du nom
- recherche les dimensions dans la base SQLite3
- ecrit les exifs
- ecrit une trace à la console pour qu'on puisse suivre la progression du travail.
Pas de boucle for, un seul fichier est traité
Appelons ce script dims2exif.sh qui devra avoir le mode 755 (ou +x) c-à-d éxécutable
Puis de l'extérieur un autre script s'occupe de fournir les noms individuellement:
- Voir juste pour la compréhension la sortie de:
find /racine -name "G_*jpg" # normalement il faudrait ajouter -print, mais c'est implicite.
ça trouve récursivement tous les fihciers jpg sous la racine
Pour ce deuxième script, appelons le dossier2exif.sh et qui prend "/racine" en entrée (donc $1=/racine) et le motif "G_*jpg" comme $2
find $1 -name $2 -exec dims2exif.sh {} \; # <--- les {} représente une ligne de la réponse
Il faut suivre les bonnes pratiques habituelles, afficher l'aide si on envoie la commande sans rien. $# sert à compter les arguments donc si $# < 2 afficher l'aide sinon commencer le traitement.
Il faut valider les entrées $1 existe-t-il ? $1 se termine-t-il avec {jpg|gif|png..}
Idem pour les deux scripts.
On peut commencer simple:
dims2exif.sh ne contient que: ls -l $1 # lister le fichier donné.
Puis on travaille le dossier2exif.sh. On doit voir la sortie du ls -l uniquement pour les fichiers du motif. Quand tout celà est OK, on s'attaque à dims2exif.sh, facile à tester pour un seul fichier.
- Indice pour le parsing du nom -> produit: echo $1 | awk -F _ '{echo $2}' devrait faire le boulot.
C'est juste des directions générales.
J-P
PS: Sur les pipes. Les pipes sont les emblèmes de la programmation du style "fonctionnel". L'idée c'est que la liste devant le pipe, doit être transformée par une fonction pour fournir une liste de même taille. En programmation fonctionnelle, c'est un map: en gros f(liste) = liste des f(x), pour tout x appartenant à la liste. Parfois, une liste donne une seule valeur, genre une liste de nombres done une valeur unique, la somme ou le produit de tous les nombres. C'est ce qu'on appelle un réduce. Map et Reduce sont les deux mamelles de ce style de programamtion.
Pour pouvoir utiliser une liste de noms de fichier il faut souvent faire un: ls -C1: listing sur une colonne, mais un foreach est ensuite indispensable. Je préfère le pattern "Visiteur" de la commande find.
@yponomeute @PBell
Une fois qu'on a le script shell qui fait bien tout avec juste la racine et le motif des images, là on peut utiliser AppleScript pour faire un truc sympa pour la saisie des données.
Si je me rapelle bien, avec XCode + AppleScript on peut faire un vrai dialogue modal macOS, avec toutes les questions en même temps dans un formulaire et les boutons qui vont bien.
J-P
Bonsoir Jaypee,
Merci de ton aide...j'ai encore besoin de ton guidage.
J'ai bien compris l'utilisation d'un script de boucle et d'un autre de sub routine qui traite le fichier un par un dans la boucle.
Cependant, je me suis exercé et j'ai un problème avec la boucle.
Commençons par ce qui fonctionne :
Au début de mon script, je teste pour l'instant que l'utilisateur entre bien 2 paramètres: le dossier et le fichier produit/dimension
@PBell, oublie la boucle, et penche-toi sur la commande find. Ensuite, pareil, oublie les greps et les pipes compliqués... C'est chic, mais si ce script plante, tu ne seras pas là pour le réparer. Autant utiliser la technologie la plus efficace qui existe et faire le moins de code possible.
Un proverbe de programmeur: La ligne de code qui ne plantera jamais, c'est celle qu'on aura évité d'écrire.
Pour la démo, je vais générer une nouvelle base compatible avec les codes produits de 2 à 5 caractères, sans formatage par des zéros.
Speed Moock
Le problème de fond et les discussions engagées sont très intéressantes et j'y regarderais d'un peu plus près quand je serais de retour de déplacement ou je suis actuellement.
Personnellement c'est vrai que je me serais davantage tourné vers un shell script pur voir un code ObjC.
Au pire il est assez simple de faire un interface ASOC (applescript objectiveC) et d'appeler un shell ou tout autre code plus performant que AS.
Dans tous les cas si la réécriture de tous les fichiers est nécessaire , le traitement de centaines de fichiers prendra toujours un certain temps dépendant du support sur lequel les fichiers se trouvent.
Les pistes d'optimisations déjà amorcées sont en tous les cas nécessaires et fort intéressantes.
Dans l'attente de regarder cela de plus près, je répond aux problèmes que tu as rencontré dans l'usage du script Shell.
Bonjour TEddy7545
Le sujet m'échappe complètement et je laisse Jaypee et PBell faire avancer leurs recherches. C'est ce que je trouve magique avec ce forum et l'informatique de passionnés, un sujet a priori simple révèle énormément de subtilités.
Pour ma part je suis sur la version 100% applescript qui tourne lentement mais au moins j'en comprends le moteur.
Les échanges restent toutefois intéressants !
Merci à tous,
Speed Moock,
J'entends ton souhait.
Je vais faire un dernier test de perf en quasiment vrai grandeur, je vais utiliser de faux fichier jpg, juste pour leur nom, et utiliser une vrai image sacrifice dans la quelle je vais réellement réécrire les EXIF data. Ainsi toutes les vraies opérations sont simulées.
Le choix entre tout comprendre et avoir une solution utilisable sera facile à faire. Ce sera du genre plusieurs heures d'attente contre quelques minutes pour traiter un dossier complet.
Cela n'empêche pas que la solution restera lisible et facilement modifiable.
Je me rends compte de "corner cases":
- A quoi sert l'index avant l'Id unique de la fin du nom?
- Que faire si on a plusieurs index pour le même code produit, on utilise les mêmes dimensions pour tous les index ?
J-P
Bonjour Jaypee,
- l'index est un identifiant unique généré par notre outil de shooting afin de garantir l'unicité des images, même si un produit revient en shooting plusieurs semaines après (un produit est défini par le couple code produit & code couleur.)
- il peut y avoir dans le listing excel des doublons de code produit mais ils auront toujours la même taille Exif à rajouter. Du coup le script peut s'arrêter au premier trouvé.
A terme je devrai faire évoluer le script pour écrire plusieurs EXIF renseignées dans les autres colonnes (C, D et E). J'essaierai d'être malin et de bien comprendre le code.
Quoiqu'il en soit merci encore
Bonsoir Teddy7545,
Grâce à tes conseils, je crois que je progresse un peu.
Mon script est presque complet : il prends tous les fichiers des sous dossier, pour chacun, il extrait le code produit, puis recherche, dans la base, ce code et extrait la colonne 2 contenant les dimensions.
J'ai ajouté un test au cas où il ne trouve pas de dimensions (code produit inexistant dans la base): dans ce cas , pas d'exif.
Enfin, dans le cas où des dimensions sont trouvées, je peux faire l'exiftool.
Bonsoir,
Une question sur la commande do shell script :
comment y implanter plusieurs instruction successives similaires ?
exemple :
do shell script "/usr/local/bin/exiftool -Title='" & DimText4 & "' -Overwrite_Original " & quoted form of (UnixPath)
do shell script "/usr/local/bin/exiftool -City='" & DimText5 & "' -Overwrite_Original " & quoted form of (UnixPath)
que je voudrai regrouper sous une unique do shell script
do shell script "/usr/local/bin/exiftool -Title='" & DimText4 & "'; "/usr/local/bin/exiftool -City='" & DimText5 -Overwrite_Original " & quoted form of (UnixPath)
merci
& "' -Overwrite_Original " & quoted form of (UnixPath)
Bonsoir SpeedMoock,
Il faut mettre tous les champs à modifier à la suite avec des espaces entre :
Je suis en train de faire un timing grandeur nature je m'attends à plus d'une heure de traitement pour 32000 images. J'ai fait 32000 copies d'un petit jpeg de 16K avec les noms qui vont bien. il faut compter 1/3 à 1/2 seconde par image, soit entre 10000 et 16000 secondes en tout.
@PBell regarde la commande tr (fais un man tr dans le Terminal)
Bonsoir,
Désolé Speed Moock, j'avais en effet lu un peu trop vite les messages.
Mon message précédant s'adressait effectivement davantage aux questions de PBelle qu'à celles de Speed Moock (même si l'objectif final commun est de pouvoir contribuer à solutionner son problème).
Même si Jaypee a avancé de son côté en proposant de nouvelles évolutions de son script, je répond ici pour les problèmes rencontrés par PBelle dans son dernier script avec la boucle.
@PBell
Attention à ne pas confondre ‘ avec ' ou encore “ avec "
En effet, en shell ces caractères ne se comportent pas de la même façon.
Dans tes exemples, les quotes utilisés ne sont pas toujours les bons...ce qui explique la majorité de tes difficultées
Si tu utilises simplement un texte comprenant un espace, les simples quote suffisent --> ' (mais pas ‘)
Par contre, si tu utilises une variable ($xx) , qui potentiellement contiendra un espace ou des espaces, il faudra utiliser les doubles quotes qui permettent l'expansion de la variable (y compris si elle contient un ou des espaces) dans la chaine de caractère --> (mais pas “).
Si je reprends ton script directement, en y apportant les corrections nécessaires, cela devait fonctionner comme cela :
Une note en forme de conclusion sur les performances pour un traitement réel de 32000 images
7041,09s user 740,67s system 91% cpu 2:21:44,00 total
Ce temps s'obtient en précédent l'appel au script de la commande time
time find pictures -name "*jpg" -exec /users/jaypee/workspaces/Ruby/dims2exif.sh
Comment générer ces images de test. Reprenez la ligne de Ruby avec la commande touch, il faut utiliser un "cp modele.jpg " au lieu de "touch ".
Validation au hasard:
Grand merci à Teddy7545 : mon script fonctionne grâce à tes conseils !
Effectivement, j'avais mélangé les " et les “ que mes pauvres yeux n'ont pas détectés.
Merci aussi à Jaypee, car tes lignes m'ont indirectement aidées.
Effectivement le '^' du grep que j'avais mis sur le premier script a malencontreusement disparu des mes versions suivantes. Le voilà de retour à sa place !
J'aime bien ton idée de ne pas mettre la boucle dans le script mais d'appeler le script autant de fois que nécessaire. Je me demande cependant si, d'un appel à l'autre, le script reste en mémoire pour des raisons de performances...?
Par ailleurs, si j'ai bien compris la fonction 'awk' que tu utilises, elle est mieux que mon 'cut' qui suppose des longueurs fixes (mais cela faisait partie du descriptif de Speed Monck). Du coup, je me dis qu'il serait aussi judicieux de l'utiliser pour le résultat de mon 'grep'.
Mais dans ce cas, comment specifier que le séparateur -F est une tabulation (ascii 9) ...que je ne peux taper ?
Enfin, ai-je bien compris que le $(commande) pouvait être remplacé par `commande`? Les ag me semblent plus facile à lire (et on sait ce que la lisibilité est primordiale).
Merci à tous pour votre aide et ce cours.
Je ne m'étais jamais vraiment mis au shell, à part qq instructions dans le Terminal souvent appelées via AS.
... Je sens que je vais continuer à jouer un peu ;-)
Cordialement
@PBell
Apparemment, awk se comporte aussi naturellement que possible
Propulsé par Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)