I. Préambule▲
En se promenant sur la toile, vous verrez beaucoup de questions à ce propos, certains se plaignant même de dysfonctionnements. Autant le dire tout de suite, les actions de dossiers fonctionnent… à condition de connaître leurs règles et surtout leurs limites ! Il est possible d'en contourner certaines, mais pas toutes : autant le savoir avant de se lancer.
Bien qu'il soit aussi possible (et très facile !) d'utiliser Automator pour les actions de dossiers, cet article porte uniquement sur leur programmation en AppleScript.
Tous les exemples de cet article ont été testés sur un iMac27 (i7, Snow Leopard). De nombreux tests (mais pas tous !) ont été faits sur un iMac20 (Core duo, Snow Leopard) sur un Power Mac (G5 bipro, Tiger) et sur un iMac24 (i5, Mountain Lion). Je n'ai constaté aucune différence d'exécution (hormis la vitesse !), ce qui permet de penser que tout ce qui suit fonctionne sur tous les systèmes, au moins depuis Tiger.
Tous les codes sont libres d'utilisation.
Les actions de dossier permettent de déclencher automatiquement des scripts, en général après l'ajout de fichiers dans un dossier, mais aussi liés à d'autres événements sur le dossier.
Par exemple, glisser les fichiers images de formats différents dans un dossier peut les convertir automatiquement en format JPG de taille identique. Ou encore de changer leur nom avec un incrément ou avec la date de leur ajout dans le dossier.
Il est possible de limiter les types, faire des conversions vidéo, texte ou PDF, les envoyer vers un serveur distant, les imprimer… Bref tout ce que vous pouvez imaginer. Il existe des actions toutes faites, vous pouvez en créer avec Automator, mais aussi écrire vos propres scripts. Dans ce dernier cas, cet article est fait pour vous aider.
II. Création des actions de dossiers ▲
II-A. Création et enregistrement ▲
Avec l'AppleScript Editeur (dossier Utilitaires), il faut créer son script en l'entourant du « handler » adéquat qui dépend de l'action.
Exemple de script pour ajouts de fichiers :
on
adding folder items to
Mon_Dossier after
receiving Liste_Fichiers
-- Insérer votre script ci-dessous
-- La variable Liste_Fichiers contient la liste des noms de fichiers ajoutés
-- Mon_Dossier contient le nom du dossier dans lequel les fichiers sont ajoutés.
end
adding folder items to
Enregistrez votre programme sous forme de script (extension « scpt ») dans le chemin : Bibliothèque/Scripts/Folder Action Scripts.
Si la bibliothèque est celle de l'utilisateur, l'action n'est disponible que pour cet utilisateur. Pour la rendre disponible à d'autres utilisateurs de la machine, il faut créer le même chemin à partir de la bibliothèque située à la racine du disque (avec les droits administrateurs).
Attention, ce chemin n'existe pas par défaut. Il faut éventuellement créer les dossiers « Scripts » et « Folder Action Scripts » soi-même. Le dossier bibliothèque n'étant plus visible directement dans Mountain Lion, il faut utiliser le menu du « Finder Aller… » en appuyant sur la touche Alt pour y avoir accès.
Pour faciliter l'installation d'un script, en particulier si vous le distribuez à des utilisateurs débutants, le script ci-dessous enregistre automatiquement votre action à la bonne place en prenant soin de créer les dossiers nécessaires. Il pourrait être complété par la création du lien avec un dossier (voir ajout d'action par un script - chapitre III.B).
-- Installation d'un script pour Action de dossier dans la bibliothèque
-- © PBell 2011 09
set
F_Script to
"Scripts"
set
F_Action to
"Folder Action Scripts"
-- Sélection du script à installer
tell
application "Finder"
set
Mon_Script to
(
choose file with
prompt "Sélectionner le script à installer"
without
invisible)
as
alias
if
name extension of
Mon_Script is
not
"scpt"
then
display dialog "Désolé, le fichier "
&
(
name of
Mon_Script)
&
&
#172
;
" n'est pas un script."
&
return
&
return
&
"L'installation a échoué"
buttons {"Fin"
}
return
end
if
set
Ma_Bibli to
path to
library folder from
user domain as
alias
-- Le dossier "Scripts" dans la Bibliothèque utilisateur existe ? Sinon, on le crée
if
not
(
exists
(
folder F_Script of
Ma_Bibli))
then
make
new folder at Ma_Bibli with
properties {name:F_Script}
end
if
-- Le dossier "Folder Action Scripts" existe ? Sinon, on le crée dans le dossier Scripts
set
Doss_Script to
((
Ma_Bibli as
string)
&
F_Script &
":"
)
as
alias
if
not
(
exists
folder F_Action of
Doss_Script)
then
make
new folder at (
folder F_Script of
Ma_Bibli)
with
properties {name:F_Action}
end
if
delay 1
-- parfois le Finder prend un peu de temps !
-- Copie du script à l'emplacement voulu
try
copy
file Mon_Script to
(
folder F_Action of
Doss_Script)
on
error
display dialog "Erreur lors de la copie du fichier"
buttons {"Abandonner"
}
end
try
end
tell
Les plus perfectionnistes peuvent modifier et enregistrer ce script comme une application et insérer, dans le paquet qui en résulte, le fichier du script à ajouter. Ce faisant, un simple double-clic sur l'icône de l'application installera tout sans rien demander à l'utilisateur !
II-B. Les types d'action de dossier▲
Il y a cinq types d'actions de dossiers :
- « on Opening folder Mon_Dossier » : appelée après ouverture de la fenêtre du dossier ;
- « on Closing forlder window for Mon_Dossier » : lorsque la fenêtre du dossier se ferme ;
- « on moving folder window for Mon_Dossier from Ancienne_Position » : quand la fenêtre est déplacée ou redimensionnée;
- « on removing folder items from Mon_Dossier after losing Liste_Fichiers » : lorsque des fichiers sont supprimés du dossier ;
- « on adding folder items to Mon_Dossier after receiving Liste_Fichiers » : lorsque des fichiers sont ajoutés au dossier.
Mon_Dossier est l'alias du dossier concerné.
Ancienne_Position est de type « bounding rectangle » (les coordonnées du rectangle de la fenêtre).
Liste_Fichiers est de type liste et contient un ou plusieurs fichiers concernés avec leur chemin d'accès (attention, voir chapitre V concernant cette variable).
La dernière action, « adding », est, de loin, la plus souvent utilisée. C'est aussi celle qui peut poser le plus de problèmes. C'est la raison pour laquelle nous allons nous y intéresser en détail.
On constate qu'il n'y a malheureusement pas d'action déclenchée lors de la modification d'un fichier. Il existe d'autres méthodes pour cela, mais pas d'action de dossier !
III. Comment assigner les actions à des dossiers▲
III-A. Assignation manuelle▲
Une fois le script créé et enregistré en bonne place, le plus simple est de sélectionner le dossier dans le Finder, faire un clic droit sur celui-ci, et choisir « Configuration des actions de dossier » (menu ci-dessous à gauche). Sur la liste qui apparaît (ci-dessous à droite), sélectionnez votre action de dossier, puis validez avec le bouton « Joindre ».
La liste des scripts de dossier intègre toutes les actions Automator et AppleScript provenant aussi bien de la bibliothèque utilisateur que de la bibliothèque racine du disque.
Voici l'écran d'assignation tel qu'il devrait être.
Ne pas oublier d'activer les actions de dossier dans la case en haut à gauche de cette fenêtre.
Cette validation n'est nécessaire que la première fois où vous utilisez les actions de dossier.
Il est possible d'assigner plusieurs actions à un même dossier : sélectionnez votre dossier dans la colonne de gauche, puis utilisez les boutons « + » et « - » en bas de la colonne de droite pour ajouter ou supprimer des actions de ce dossier. Compte tenu du mécanisme interne de déclenchement des actions, je déconseille aux débutants l'utilisation de plus d'une action par dossier.
Il est aussi possible d'assigner des actions à des dossiers via des scripts (voir assignation en masse).
Pour les curieux, les liens entre dossiers et actions sont dans le fichier utilisateur/bibliothèque/Preferences/com.apple.FolderActions.plist, éditable avec l'utilitaire Property List Editor fourni avec le kit de développement Xcode d'Apple ou bien un éditeur de texte quelconque en prenant soin de conserver les balises.
Normalement, vous n'avez pas à vous occuper de ce fichier. Cependant, ce fichier peut devenir assez gros et entraîner des lenteurs, car il n'est pas mis à jour automatiquement lors de la suppression d'un dossier.
Pour le maintenir propre, il faut s'astreindre, avant de supprimer un dossier lié à une action, à supprimer ce lien avec les boutons « - » côté script d'abord (colonne de droite), puis l'entrée sur le dossier lui-même (colonne de gauche), après avoir sélectionné les éléments dans leur liste respective (voir fenêtre ci-dessus).
III-B. Assignation en masse et propagation aux sous‑dossiers ▲
L'action de dossier, Adding, n'est déclenchée que si un fichier est ajouté dans ce dossier. Elle n'est pas activée lors de l'ajout d'un fichier dans un sous‑dossier de ce dossier. Il est cependant possible de contourner cette limitation avec des scripts.
Imaginons que vous ayez un dossier parent contenant de nombreux sous-dossiers :
- un script peut ajouter une action de dossier à tous les sous‑dossiers d'un dossier parent sélectionné ;
- une action de dossier sur le dossier parent peut assigner automatiquement un script d'action à tout nouveau sous‑dossier ajouté.
Les scripts ci-dessous correspondent aux deux méthodes, la seconde étant plus souple. Ils sont suffisamment commentés pour que vous puissiez les adapter à vos besoins.
Ce script gère les erreurs en cas d'ajout (si l'action existe déjà pour certains des sous‑dossiers) et en cas de suppression (si l'action n'existe pas dans tous les sous‑dossiers). Il contient les instructions sur les actions (création, lecture et suppression) qui montrent les syntaxes à utiliser dans chaque cas.
-- Ce script : ajoute ou supprime la même action de dossier à tous les sous-dossiers d'un dossier parent
-- © PBell 2010-05 & 2013-01
-- Demande à l'utilisateur s'il veut ajouter ou supprimer les actions des sous-dossiers du dossier parent
set
Reponse to
display dialog "Voulez-vous ajouter ou supprimer des actions de dossier sur tous les sous-dossiers d'un dossier parent ?"
&
return
&
#172
;
&
return
&
"(limité à un seul sous-niveau de dossier)"
buttons {"Annuler"
, "Ajouter"
, "Supprimer"
}
set
Choix to
button returned of
Reponse
-- Récupération du chemin par défaut des Folder Action Scripts
tell
application "System Events"
set
Script_Folder to
(
path to
Folder Action scripts folder from
user domain)
as
string
end
tell
tell
application "Finder"
-- Sélection du dossier parent
set
Dos_Parent to
(
choose folder with
prompt "Sélectionner le dossier parent"
)
-- Récupération des sous-dossiers du dossier parent
set
Sous_Dossiers to
every
folder of
folder Dos_Parent
-- Sélection du script à ajouter ou supprimer (dans le dossier folder action scripts!)
set
Mon_Script to
(
choose file with
prompt &
#172
;
"Sélectionner le script à assigner/supprimer"
default location Script_Folder as
alias)
if
Choix =
"Ajouter"
then
set
Mon_Texte to
"Êtes-vous certain de vouloir assigner le script à tous ces dossiers ?"
else
set
Mon_Texte to
"Êtes-vous certain de vouloir supprimer le script de tous ces dossiers ?"
end
if
display dialog "Le dossier parent '"
&
(
name of
Dos_Parent)
&
"' contient "
&
&
#172
;
(
count
of
Sous_Dossiers)
&
" sous-dossiers."
&
return
&
return
&
Mon_Texte
-- boucle sur chaque sous-dossier du dossier parent
repeat
with
Un_Dossier in
Sous_Dossiers
tell
application "System Events"
if
Choix =
"Ajouter"
then
-- Ajout de l'action avec Try au cas où cette action existe déjà
try
attach action to
Un_Dossier as
alias using (
Mon_Script as
string)
end
try
else
-- Suppression de l'action de dossier : on lit toutes les actions
set
Mes_Actions to
attached scripts (
Un_Dossier as
alias)
-- On boucle juste pour montrer la syntaxe de lecture des actions
repeat
with
Mon_Action in
Mes_Actions
if
name of
Mon_Action =
name of
Mon_Script then
try
remove action from
(
Un_Dossier as
alias)
&
#172
;
using action name (
name of
Mon_Action)
end
try
end
if
end
repeat
-- fin boucle sur les actions
end
if
end
tell
end
repeat
-- fin de boucle sur les sous dossiers
display dialog "Action effectuée sur les sous-dossiers"
buttons {"OK"
} &
#172
;
default button 1
giving up after
5
end
tell
Ce script gère un seul niveau de sous‑dossier. Pour gérer plusieurs niveaux (sous-sous-dossiers…) il doit être adapté et faire appel à la récursivité, ce qui sort du cadre de cet article.
Ce second script est une action de dossier à assigner au dossier parent, avant création de sous-dossiers. Ensuite, dès l'ajout d'un sous‑dossier, ce dernier sera automatiquement associé à une action de dossier prédéfinie (qui doit donc exister en bonne place).
-- Ce script est déclenché par une action de dossier "add item" sur le dossier parent
-- si l'action est un l'ajout d'un sous-dossier,
-- alors ce script assigne une action de dossier prédéfinie à ce nouveau sous-dossier
-- © PBell 2012-05
on
adding folder items to
Mon_Dossier after
receiving Liste_Fichiers
-- Mettre ici le nom du script à ajouter à la création d'un sous-dossier
-- Ce script doit exister dans votre Folder Action Script !!
set
Script_sous_Dossier to
"Mon script de sous dossier.scpt"
-- Récupération du chemin par défaut des Folder Action Scripts dans le domaine utilisateur
tell
application "System Events"
to
set
Dossier_Scripts to
&
#172
;
(
path to
Folder Action scripts folder from
user domain)
as
string
set
Mon_Script to
(
Dossier_Scripts &
Script_sous_Dossier)
-- Vérification que le script prédéfini existe. Sinon, on quitte le script !
tell
application "Finder"
if
not
(
exists
file Mon_Script)
then
return
-- Boucle sur chaque item ajouté = un ou plusieurs sous-dossiers)
repeat
with
Un_Item in
Liste_Fichiers
if
(
kind of
Un_Item)
is
"dossier"
then
-- C'est un dossier, on ajoute l'action de dossier
tell
application "System Events"
to
attach action to
Un_Item as
alias &
#172
;
using (
Mon_Script as
string)
end
if
end
repeat
-- fin de boucle sur les items ajoutés
end
tell
end
adding folder items to
IV. Règles de programmation des actions de dossier▲
Contrairement aux scripts habituels et aux processus Automator, les actions de dossier ne sont pas appelées par l'utilisateur, mais par le système lui-même.
Le mécanisme exact est hors de notre propos, mais ce qu'il faut retenir pour simplifier c'est que l'action de dossier se déroule en arrière-plan, et ce, même si vous voyez certains de ses effets. En conséquence, toute erreur rencontrée lors de l'exécution sera invisible, le script s'arrêtera sans aucun message visible (mis à part dans l'utilitaire Console). Vous ne saurez pas ce qui a eu lieu, ce qui n'a pas eu lieu, ni quand il s'est arrêté ! Il est donc particulièrement important d'écrire des scripts qui gèrent correctement toutes les erreurs possibles, faute de quoi, vous aurez des surprises (mauvaises, bien sûr !).
Les blocs « try/end try » et autres « if » vous aideront selon ce que vous voulez faire. Pour les scripts un peu complexes, je vous conseille fortement de les développer en mode script standard pour tracer toutes les erreurs possibles avec l'Éditeur AppleScript.
Pensez juste à ajouter, au début, une sélection d'un dossier et de fichiers de ce dossier pour simuler l'action de dossier comme indiqué ci-dessous :
-- Simulation de l'action de dossier d'ajout de fichiers sur un script standard
-- Sélection du dossier
set
Mon_Dossier to
((
choose folder with
prompt "Choisissez le dossier test :"
without
invisibles)
&
#172
;
as
alias)
as
string
-- Sélection des fichiers ajoutés
set
Liste_Fichiers to
choose file with
prompt "selectionnez fichiers ajoutés"
default location &
#172
;
((
Mon_Dossier)
as
alias)
with
multiple selections allowed
-- Insérez votre script ici
-- Vous pouvez exécuter ce script dans l'Éditeur AppleScript pour le mettre au point
Une fois le script mis au point, vous supprimez les instructions de sélection du dossier et des fichiers et vous encadrez votre script par les « handler » adéquats comme ci-dessous :
on
adding folder items to
Mon_Dossier after
receiving Liste_Fichiers
-- votre script ici, tel qu'il a été mis au point (sans la sélection du dossier et des fichiers bien sûr !)
end
adding folder items to
Enfin, une petite précision : si votre script doit se déclencher à l'ajout d'un élément dans le dossier, et qu'il prévoit l'ajout d'un fichier de trace ou d'un sous-dossier dans ce même dossier, pensez à traiter la récursivité que cela va engendrer. En effet, votre script, en ajoutant un élément, va lui-même déclencher une action de dossier… donc il va s'appeler !
Votre script doit donc prévoir de ne rien faire lorsqu'un élément qu'il génère est ajouté au dossier. À titre d'exemple, le script ci-dessous prévoit d'ajouter un sous-dossier (s'il n'existe pas déjà) dans lequel sont enregistrés les fichiers dont l'extension n'est pas retenue.
(* Script d'action de dossier :
Lors de l'ajout de fichiers dans le dossier, on vérifie leur type
Si le type est incorrect, le fichier sera copié dans un sous-dossier de rejet
Si le dossier de rejet n'existe pas déjà, on le crée dans le dossier.
*)
on
adding folder items to
Mon_Dossier after
receiving Liste_Fichiers
--Nom du sous dossier qui recevra les fichiers dont l'extension est incorrecte
set
Dos_Rejet to
"Mes_rejets"
-- Liste des extensions de fichier acceptées
set
liste_Ext to
{"jpg"
, "txt"
}
tell
application "Finder"
-- Pour éviter que l'ajout du dossier Dos_Rejet ne crée aussi un événement,
-- On teste ce qui est ajouté et, éventuellement, on ne fait rien !
if
name of
first
item of
Liste_Fichiers is
Dos_Rejet then
return
-- Vérifie si le sous dossier existe déjà, sinon, le crée
if
not
(
exists
folder Dos_Rejet of
Mon_Dossier)
then
make
new folder at Mon_Dossier with
properties {name:Dos_Rejet}
-- Attention, cet ajout génère un event add folder items !! -> d'où le premier test !
end
if
-- On crée une liste de fichiers à rejeter vers le sous-dossier de rejet
set
Liste_Rejet to
{}
repeat
with
Mon_Item in
Liste_Fichiers
if
name extension of
Mon_Item is
not
in
liste_Ext then
set
end
of
Liste_Rejet to
Mon_Item
end
if
end
repeat
-- On transfère les fichiers incorrects dans le dossier de rejet
move
Liste_Rejet to
folder Dos_Rejet of
Mon_Dossier as
alias
display dialog "Opération terminée"
end
tell
end
adding folder items to
V. Fonctionnement spécifique de l'action « on adding folder items » et ses limites▲
L'instruction est : on adding folder items to Mon_Dossier after receiving Liste_Fichiers.
Le script doit se terminer par : end adding folder items to.
V-A. Que contient réellement le paramètre Liste_Fichiers ?▲
Compte tenu des termes utilisés, notamment le mot « after », on peut s'attendre à ce que l'ajout de n fichiers simultanément dans un dossier déclenche le script avec Liste_Fichiers contenant les n fichiers ajoutés, donc après l'ajout complet.
C'est parfois le cas, mais pas toujours ! Tout dépend du temps… mis par le système pour faire cet ajout.
En pratique tout ce passe avec deux processus parallèles, non synchronisés :
- copie ou transfert des fichiers sélectionnés dans le dossier ;
- temporisation interne, puis lancement du script d'action de dossier.
Lorsque le processus 1 est fini avant le 2, Liste_Fichiers recevra TOUS les fichiers ajoutés.
Si le processus 1 n'est que partiellement fini, le processus 2 ne recevra qu'une partie des fichiers ajoutés…
Et les autres ? Eh bien ils feront l'objet d'un nouvel appel au script d'action de dossier ! Plus tard, après… au bout d'un certain temps pour paraphraser Fernand Raynaud !
Exemple avec ajout de quatre fichiers qui vont déclencher trois actions :
Chaque action déclenchée aura des paramètres différents : la première avec le fichier 1, la seconde contiendra les fichiers 2 et 3, car au début de cette action, le fichier 2 est ajouté et le 3 a commencé sa copie. Enfin la dernière action avec seulement le fichier 4.
On s'attendait pourtant à seulement une action avec les quatre fichiers !
Le problème se complique pour trois raisons :
- la vitesse du processus 1 dépend des supports physiques, de la taille des fichiers et du type d'ajout (copie ou transfert) ;
- la vitesse du processus 2 et sa temporisation dépendent de la machine et de son OS. Les variations sont cependant faibles ;
- le processus 2 ne se déclenche pas « after » la copie, mais peu de temps après le début de cette copie (aïe, aïe ! voir paragraphe V.B).
Il est facile de comprendre que la vitesse de l'ajout dépend des caractéristiques des disques/volumes d'origine et de destination (celui du dossier). Il faut se souvenir qu'elle va aussi dépendre de la méthode : copie ou transfert, via le Finder, une application, une commande shell Unix ou une autre machine en réseau.
Rappel :
La copie garde le fichier à la source et l'ajoute à la destination. Elle s'exécute soit avec la commande cp en Unix, soit en glissant les fichiers d'un volume à l'autre sur le Finder, soit en glissant les fichiers entre deux emplacements d'un même volume avec la touche Alt.
Le transfert ne peut se faire qu'entre deux dossiers d'un même volume, soit avec la commande mv en Unix, soit avec un simple glisser sur le Finder. En fait, cette commande ne fait qu'écrire un nouveau chemin pour le fichier, sans réécrire ce dernier.
Lors d'un transfert, il y a de fortes chances que la vitesse soit telle que l'action de dossier reçoive la liste de tous les fichiers car le transfert est très rapide ! Lors d'une copie, la réponse dépendra de la taille des fichiers et des vitesses des matériels.
Pour s'en convaincre, rien de tel qu'un exemple pratique : prenez un dossier D1 avec quatre fichiers : deux petits de 4Ko, deux gros de 2Go chacun au moins (des vidéos par exemple).
Prenez un dossier D2, sur le même volume/disque auquel vous assignez l'action de dossier contenant le script ci-dessous qui se contente d'afficher, pendant une seconde après le lancement de l'action, le nombre de fichiers reçus par l'action et le nom du premier :
on
adding folder items to
Mon_Dossier after
receiving Liste_Fichiers
tell
application "Finder"
display dialog "Nb="
&
(
count
of
Liste_Fichiers)
&
return
&
&
#172
;
(
name of
first
item of
Liste_Fichiers)
giving up after
1
end
tell
end
adding folder items to
Réalisez ensuite la copie (glissez les quatre fichiers de D1 vers D2 tout en maintenant la touche Alt).
La barre habituelle de progression du Finder apparaît indiquant la copie en cours de quatre fichiers. Soudain, avant la fin de la copie, une fenêtre indiquant « Nb=x » et le nom d'un des fichiers. Cette fenêtre disparaît au bout d'une seconde. Puis sans doute quelques secondes plus tard, de nouveau une fenêtre Nb=y, et ainsi de suite… Vous pouvez avoir deux ou trois fois la fenêtre.
Note : Les sceptiques (la confiance n'exclut pas le contrôle…) peuvent remplacer le « Display dialog » par une commande shell « touch » pour écrire dans un fichier de trace qui n'influe pas sur la vitesse du Finder.
Selon la vitesse et l'ordre de sélection de fichiers (petits d'abord, au milieu ou en dernier), vous aurez sans doute deux ou trois appels du script avec un nombre total de Nb égal à 4 (bien sûr, car il y a quatre fichiers en tout et pas un de plus).
Les quatre séquences suivantes sont possibles avec les nombres pour chaque dialogue :1-2-1, 1-1-2, 2-1-1, ou 2-2. Si vous avez 1-1-1-1, votre copie est particulièrement lente !
Supprimez les fichiers du dossier D2 et recommencez avec un transfert (D1 et D2 sur le même volume, glissez simplement les fichiers de D1 à D2). Il est fort probable que vous n'aurez qu'une seule action avec Nb=4 ! Tout simplement parce que dans ce cas, le transfert est plus rapide que le délai de latence de l'action de dossier.
J'ai cherché à tester la limite de la temporisation interne.
Un transfert de 1000 fichiers de 4 Ko chacun ne génère qu'une seule action de dossier avec une liste de 1000 fichiers. Sur une machine rapide (iMac27 quad core i7), la copie de ces fichiers donne le même résultat. Par contre, dès que la liste des fichiers contient un seul fichier de 1 Go au milieu, la copie génèrera au moins deux actions de dossier.
Donc, règle N° 1 : ne jamais supposer que tous les fichiers ajoutés vous arriveront en même temps dans le script.
Lors du test précédent, les lecteurs les plus observateurs auront sans doute remarqué que la fenêtre du script s'affiche AVANT la copie complète des fichiers en question.
Ce n'est pas important pour de nombreux scripts, mais si vous devez modifier les fichiers ajoutés (voire les supprimer s'ils ne correspondent pas à vos critères) et que, en plus les fichiers sont gros (1, 2 ou 10 Go…), alors là, oui, nous avons un problème, car votre script commencera à travailler non pas « after », mais « pendant ».
Il se peut que vous ayez besoin d'être certain que le fichier est bel et bien ajouté dans le dossier. Dans ce cas, il faut que votre script sache détecter quand la copie est effectivement terminée, pour ensuite se dérouler comme vous l'avez prévu. C'est ce que nous allons voir dans le prochain paragraphe.
V-B. Comment forcer le script à bien attendre le « after » de l'instruction ?▲
Sur la toile, vous trouverez cette question récurrente (détection de fin de copie), pas seulement pour des actions de dossier, parfois avec des réponses fausses ou approximatives. J'ai testé un certain nombre de méthodes que je vous liste ci-dessous : celles qui ne fonctionnent pas et celles qui fonctionnent.
Ce n'est certainement pas exhaustif, alors n'hésitez pas à partager vos propres méthodes.
V-B-1. Les méthodes qui ne fonctionnent pas (pour vous éviter des heures perdues)▲
D'après la documentation AppleScript, il devrait y avoir plusieurs possibilités pour déterminer si un fichier est en cours de copie ou pas, s'il existe, s'il est en écriture ou bloqué. Malheureusement beaucoup sont inopérantes, dans le cadre d'une action de dossier :
utiliser la propriété « exist » du fichier : non, car elle est « true » dès que l'entrée est inscrite dans le répertoire… Bien avant la fin de copie ! ;
utiliser la propriété « Busy Status » : non, car, bien que Apple demande aux développeurs de la gérer, le Finder ne la gère pas ! (faites comme je dis, pas comme je fais) ;
utiliser la propriété "File Type of Info for" : curieusement, sa valeur est "brok" (testé sur Tiger et Snow Leopard, Moutain Lion à vérifier) si le fichier est en cours de copie via le Finder. Mais elle est vide lors d'un ajout par une application ou la copie par commande Shell Unix. On ne peut donc s'y fier ;
utiliser la commande « open access with write permission » ne donne pas d'erreur pendant la copie, comme si le fichier n'avait pas de contrainte d'accès. Mais on a parfois une erreur lors de l'écriture d'un bloc. Ouf ! Ça c'est normal, mais on ne veut pas forcément écrire sur le fichier ;
attendre que la fenêtre Finder de progression de copie ne soit plus affichée : imparfait car le script peut être face à plusieurs copies simultanées dans le Finder sans savoir quelle fenêtre de copie doit disparaître.
V-B-2. Les méthodes qui fonctionnent▲
Je n'ai trouvé qu'une méthode sur la toile dont le concept fonctionne vraiment (auteur anonyme, merci à lui). Je l'ai simplement remise en forme ici.
Avant de la trouver, j'en avais créé une qui, j'avoue, n'est pas très élégante, mais j'adore la commande Unix ls ! (elle est souvent beaucoup plus rapide qu'AppleScript et puis cela fait un peu travailler les neurones).
Méthode basée sur la taille du fichier : si la taille du fichier ne change plus, c'est que tout est copié ! C'est la plus simple.
set
Mon_Fichier to
"dossier:fichier"
-- mettre ici le chemin et nom de votre fichier
tell
application "Finder"
try
set
ASize to
-
1
repeat
until
(
size of
Mon_Fichier)
=
ASize
set
ASize to
(
size of
Mon_Fichier)
delay 0
.3
end
repeat
end
try
end
tell
display dialog "Le fichier est totalement copié !"
Méthode basée sur les résultats de la commande shell/Unix ls -l. Cette commande affiche, en début de ligne, les autorisations Unix du fichier :
- avant copie -> erreur car pas de fichier !
- pendant copie Finder -> -rwxr-xr-x@ (@ en position 11 et débute par -rwx)
- pendant copie Unix -> -rwx------
- après copie : -rwx, mais plus de @ ni de suite "------". La valeur exacte dépend des autorisations sur le fichier (user, group…).
- Note : j'ai cherché sur le Net, sans succès, la signification du « @ » à la fin des autorisations.
set
Mon_Fichier to
"dossier:fichier"
-- mettre ici le chemin et nom de votre fichier
set
F_Unix to
quoted form of
POSIX path of
Mon_Fichier
set
LS_Debut to
""
set
PosArobase to
"@"
set
LS_Fin to
"------"
set
T to
"xxxxxxxxxxxxx"
repeat
until
(
LS_Debut =
"-rwx"
)
and
not
(
PosArobase =
"@"
)
and
not
(
LS_Fin =
"------"
)
try
set
T to
do shell script
"ls -l "
&
F_Unix
on
error
set
T to
"xxxxxxxxxxxxx"
end
try
set
LS_Debut to
text 1
thru
4
of
T
set
LS_Fin to
text 5
thru
10
of
T
set
PosArobase to
character 11
of
T
end
repeat
display dialog "Le fichier est totalement copié !"
Pour résumer, si vous manipulez de petits fichiers et que vous changez les noms, pas de souci pour votre action de dossier.
Si vous manipulez de gros fichiers et que vous voulez les traiter une fois entièrement copiés (et seulement à ce moment), vous devez insérer l'une de ces méthodes dans votre script.
La méthode la plus « propre », c'est-à-dire lisible, consiste à faire une boucle sur les fichiers de Liste_Fichiers et, dans cette boucle, d'insérer l'un de ces scripts. Votre script continuera ensuite dans la boucle, seulement lorsque la copie du fichier en question sera terminée.
Ainsi chaque fichier sera traité par le script après sa copie, pas pendant.
On peut aussi n'utiliser seulement qu'une fois l'un de ces scripts, avant la boucle de traitement sur Liste_Fichiers. Il suffit de vérifier si le dernier élément de cette liste (instruction last item of Liste_Fichiers) a terminé sa copie. Ainsi on est certain que tous les fichiers sont copiés avant de démarrer l'action de dossier.
En revanche, on attend effectivement la dernière copie pour débuter le script. Bien qu'il fonctionne, le risque est qu'une future version du Finder fasse du parallélisme (« multithreading ») à l'intérieur d'une seule action de copie. Alors vous ne pourrez plus savoir si le dernier fichier de la liste est bien celui qui sera traité en dernier.
Les versions actuelles du Finder font du parallélisme entre les tâches de copies, mais pas à l'intérieur d'une tâche de copie multiple. Pour combien de temps encore ?
À vous de voir en fonction de ce que fera le reste de votre script ! En tout cas, vous voilà prévenu.
V-C. Exemple pratique sur le dossier de téléchargement▲
Afin de mettre en pratique le problème de synchronisation des tâches, voici ci-dessous un exemple basé sur l'ajout, via votre navigateur favori, d'un fichier dans le dossier téléchargement. Il est à peu près certain dans ce cas que le script d'action de dossier débutera avant que le fichier soit entièrement téléchargé. Il est tout aussi probable que l'action ne comportera qu'un fichier ajouté.
L'action elle-même copiera le fichier téléchargé dans un sous-dossier en fonction du type de fichier. Si ce sous-dossier n'existe pas, l'action le crée (gestion des erreurs) et le script passe outre ce nouvel ajout.
Il y a aussi une astuce afin de contourner un comportement particulier du navigateur Safari. En effet, celui-ci, lors d'un téléchargement, crée parfois un fichier temporaire qu'il va renommer ensuite, une fois le chargement effectué.
Par exemple, il va déclencher l'action de dossier avec l'ajout d'un fichier Fichier.Ext.Download, dont l'extension est « download » puis modifier le nom en Fichier.Ext dont l'extension deviendra « Ext ». Curieusement (et heureusement !), la référence alias du fichier reste la même ce qui permet sa copie.
Cependant, il faut bien tenir compte de cela pour assigner ce fichier au dossier adéquat (celui qui doit contenir vos fichiers avec l'extension « Ext »).
Le petit script ci-dessous contourne le problème en copiant immédiatement le nom en mémoire (avant fin de téléchargement) pour en extraire l'extension qui servira à l'assignation du sous-dossier voulu. Il a été testé avec Safari et plusieurs sites et types de fichiers. Il est suffisamment commenté pour que vous puissiez l'adapter à votre cas particulier.
Bien sûr, ce script est à assigner comme action du dossier téléchargement (celui que vous avez sélectionné dans les préférences de votre navigateur).
-- Action de dossier sur le dossier Téléchargement :
-- Transfère les fichiers vers des sous-dossiers par type
-- une fois leur téléchargement terminé
-- Les sous-dossiers regroupent les fichiers par groupes d'extensions
-- Pour chaque groupe sont définis les types et le nom du sous-dossier
property
FImage : {"JPG"
, "jpg"
, "png"
, "TIFF"
}
property
DImage : "Mes_Images"
property
FTexte : {"txt"
, "doc"
, "docx"
, "odt"
}
property
DTexte : "Mes_Textes"
property
FVideo : {"dv"
, "DV"
, "mov"
, "MOV"
, "VOB"
, "vob"
, "divx"
, "mp4"
, "MP4"
}
property
DVideo : "Mes_Videos"
property
FAudio : {"m4a"
, "M4A"
, "aif"
, "mp3"
, "aiff"
}
property
DAudio : "Mes_Musiques"
-- Pour toutes les autres extensions (impossible de lister tous les types !)
property
DAutres : "Autres formats"
on
adding folder items to
Mon_Dossier after
receiving Liste_Fichiers
tell
application "Finder"
-- Définit une liste de tous les sous-dossiers possibles
set
Dossiers_Ajout to
{DImage, DTexte, DVideo, DAudio, DAutres}
-- Boucle sur chaque fichier ajouté dans le dossier de téléchargement
repeat
with
Mon_Item in
Liste_Fichiers
set
Mon_Ext to
name extension of
Mon_Item
set
Mon_Nom to
name of
Mon_Item
-- Safari télécharge parfois en créant un nom temporaire avec "download"
-- Exemple : photo.jpg.download
-- Cela déclenche l'action de dossier, mais Safari change ensuite le nom du fichier!!
-- Si Safari change avant que l'action de dossier ne démarre, cela crée une erreur
-- On commence donc par prendre le nom sans le ".download" s'il existe
-- On extrait l'extension pour attribution du dossier
if
Mon_Ext is
"download"
then
-- On supprime le download (les neuf derniers caractères)
set
Mon_Temp to
text 1
thru
((
length of
Mon_Nom)
-
9
)
of
Mon_Nom
-- Recherche du premier "." en partant de la fin pour l'extension
set
I to
length of
Mon_Temp
repeat
while
character I of
Mon_Temp is
not
"."
set
I to
I -
1
end
repeat
set
Mon_Ext to
text (
I +
1
)
thru
(
length of
Mon_Temp)
of
Mon_Temp
set
Mon_Nom to
text 1
thru
I of
Mon_Temp
end
if
-- attente de fin de téléchargement (vérification que la taille ne change plus)
try
set
Mon_Fichier to
Mon_Item as
alias
set
OldSize to
-
1
repeat
until
(
size of
Mon_Fichier)
=
OldSize
set
OldSize to
(
size of
Mon_Fichier)
delay 0
.5
end
repeat
end
try
-- On teste si l'ajout n'est pas justement l'un des sous-dossiers
-- Si c'est le cas, la boucle ne fait rien et passe à l'item suivant !
if
Mon_Nom is
not
in
Dossiers_Ajout then
-- Détermination du sous-dossier via l'extension
if
Mon_Ext is
in
FImage then
set
Sous_Dossier to
DImage
else
if
Mon_Ext is
in
FTexte then
set
Sous_Dossier to
DTexte
else
if
Mon_Ext is
in
FVideo then
set
Sous_Dossier to
DVideo
else
if
Mon_Ext is
in
FAudio then
set
Sous_Dossier to
DAudio
else
set
Sous_Dossier to
DAutres
end
if
-- Vérification existence du sous-dossier, création si nécessaire
if
not
(
exists
folder Sous_Dossier of
Mon_Dossier)
then
make
new folder at Mon_Dossier with
properties {name:Sous_Dossier}
-- attention, cet ajout génère encore un event add folder items !! -> d'où le premier test !
end
if
-- Transfert du fichier vers le sous-dossier adéquat
move
Mon_Item to
folder Sous_Dossier of
Mon_Dossier as
alias
end
if
-- si l'item n'est pas un sous-dossier
end
repeat
-- fin de boucle sur chaque item ajouté
end
tell
end
adding folder items to
J'espère que ces explications et les exemples vous seront utiles pour écrire et gérer des actions de dossiers.
Cet outil si puissant que les autres OS n'ont pas…
Bon courage !
Merci à Claude Leloup pour sa patience et ses corrections.
Venez discustez de cette article sur le forum 3 commentaires