POUR UNE SUITE : récapitulatif 1 - Dossier d'installation d'un pkg 2 - COMMANDE COMPLETE DE SUPPRESSION [OU DE RECOMPOSITION d'un pkg] [OU DE VERIFICATION par rapport à un pkg] => nécessite de connaître le dossier d'installation (donc le point 1 ci-dessus) 3 - Extraction d'1 fichier ou d'1 dossier depuis un pkg [ou le pkg entier dans un dossier de transit] 4 - commande installer [et softwareupdate] Dossier d'installation d'un pkg $ pl < /Library/Receipts/iStopMotion1.3.pkg/Contents/Info.plist | awk -F\" '/IFPkgReceiptLocation/ { print $2 }' Un autre fichier présent dans le pkg, Info.plist, fournit son lot d'informations dont notamment le dossier à partir duquel ont été désarchivés les documents. pl étant destinée à disparaître à terme, il est plus judicieux et nettement plus économique (en rédaction) d'employer la commande suivante pour arriver au même résultat : $ defaults read /Library/Receipts/iStopMotion1.3.pkg/Contents/Info IFPkgReceiptLocation C'EST TOP GENIAL !!! ATTENTION toutefois, malgré son existence (et aussi GRÂCE à son existence qui permet cet emploi) l'extension .plist NE doit PAS apparaître derrière le nom de fichier Info pour que cela fonctionne (PAR CONTRE, c'est un tout petit moins génial pour consulter un fichier plist dans le dossier courant. Comme defaults ne se base que sur la syntaxe de la source à consulter pour déterminer empiriquement s'il s'agît d'un "domain" ou d'un chemin d'accès, le seul moyen que je vois est d'écrire la commande comme ceci : $ defaults read `pwd`/Info IFPkgFlagDefaultLocation NOTE : dans les anciens formats de pkg, le fichier Info.plist est en fait remplacé par son "ancêtre" NomPackage.info dans le sous-dossier Resources (à l'intérieur du dossier Contents, au même niveau que ses comparses .bom et .pax[.gz]) et adopte une structure plus simple que le format .plist. Juste un fichier ASCII avec un ensemble de ligne au format VARIABLE VALEUR (les noms des variables ne ressemblent pas trop à leurs équivalents dans les fichiers .plist ! ) COMPLEMENT DE NOTE : le pire, ce sont probablement les paquetages (comme par exemple iStopMotion) qui fournissent à la fois un fichier .info et un autre .plist (sûrement en pensant faire au mieux pour l'intérêt de l'utilisateur). Il vaudra mieux alors (sur OS X Panther en particulier) ne se baser que sur le .plist. Pourquoi ? Parce qu'en cas de dossier d'installation effectif différent du dossier prévu par défaut (dans le cas de paquetages "IFPkgFlagRelocatable ") le fichier .plist mentionnera le dossier réel d'installation, information non reprise dans le .info. ENFIN : la tendance dans les paquetages récents "Made in Apple", c'est de n'avoir qu'un fichier Info.plist puis des originaux Archive.bom, Archive.sizes et Archive.pax.gz sous Contents ET AUSSI des NomPaquetage.bom, NomPaquetage.sizes et NomPaquetage.pax.gz, sous Resources, qui pointent symboliquement vers leurs pendants dans le dossier père. ET FAIRE LA COMMANDE COMPLETE DE SUPPRESSION - OU - DE RECOMPOSITION (ou les deux) D'UN PKG => après avoir trouvé le dossier d'installation (la racine) ET déterminé le chemin du bom, faire une boucle (while read) pour lister le bom (sortie formatée pour n'avoir QUE les noms des fichiers, sans autre information et sans les dossiers), concaténer "/" puis la racine à chaque ligne à la "racine" et appliquer un rm dessus ou toute autre commande appropriée (EN ETANT sudo ! ). Exemple d'utilisation de read (builtin de bash) : $ cat /etc/passwd | while read ligne décliner boucles - juste affichage fichiers installés - vérification checksum - recomposition d'un paquetage (format pax.gz ?!) - suppression (option pour déplacement vers la poubelle plutôt que suppression complète) => SOIT en 2 passes : fichiers PUIS dossiers Cette variante, je pensais, offrait l'intérêt de distinguer entre dossier et fichier, ce qui facilitait le choix des commandes à appliquer, rm ou rmdir selon la passe. MAIS comme rm accepte une option -d qui permet d'effacer indifféremment fichiers MAIS AUSSI dossiers, le problème ne se pose plus ! => SOIT en 1 passe en listant à l'envers pour traiter au fur et à mesure les fichiers AVANT les dossiers qui les contiennent (perl -e 'print reverse <>' FICHIER) "des feuilles vers la racine" ==> FINALEMENT, on va peut être pouvoir s'en sortir avec un sort -r Extraction d'1 fichier ou d'1 dossier dans un pkg. ... Bien entendu, ceci ne s'applique qu'aux paquetages complets et non à ceux présents dans /Library/Receipts qui ne sont plus que des coquilles vides (... l'ombre d'eux mêmes). Première chose, le man de la commande pax AINSI que l'auto-documentation (pax -h) OMETTENT TOUTES DEUX l'option -z qui permet de traiter les archives compressées (notamment avec gzip) plutôt que de recourir à la combinaison zcat archive.pax.gz | pax (ou toute autre action que l'action implicite, en l'abscence d'option, "list") La dernière commande se résume alors en pax -z < archive.pax.gz (ou encore pax -zf archive.pax.gz) Utilisation avancée (substitution ET interaction) : $ pax -rzi -s ',^./System/Library/Frameworks,/AUTREDOSSIER,' -f /Volumes/Linux/Packages/FAIT_2/Safari_1.2.pkg/Contents/Archive.pax.gz à chaque fois que le chemin d'accès d'un fichier désarchivé débute (méta-caractère ^) par ./System/Library/Frameworks, le texte précédent est remplacé (option -s) par la chaîne /AUTREDOSSIER Utilisation avancée SUITE (emploi de patterns) : $ pax -zf /Volumes/Linux/Packages/FAIT_2/Safari_1.2.pkg/Contents/Archive.pax.gz './System/Library/Frameworks/*' $ pax -zf /Volumes/Linux/Packages/FAIT_2/Safari_1.2.pkg/Contents/Archive.pax.gz ./System/Library/Frameworks => les deux lignes précédentes ne diffèrent dans leur résultat que par l'absence dans le premier de la référence au dossier ./System/Library/Frameworks NOTE : comme cela est donc visible, l'astérisque est optionnelle si elle suit une chaîne qui fait office de préfixe pour désigner les chemins d'accès. $ pax -zf /Volumes/Linux/Packages/FAIT_2/Safari_1.2.pkg/Contents/Archive.pax.gz ./System/Library/Frameworks '*.png' => quand plusieurs patterns sont utilisés, leurs résultats combinés constituent une union. NOTE : L'ORDRE D'APPARITION des patterns dans les paramètres peut avoir une importance : si le résultat d'un des patterns est complètement INCLUS dans le résultat d'un des patterns qui le précède (plus à gauche dans les paramètres) ALORS on obtient un message d'avertissement quant à l'absence de réponse pour ce pattern du type pax: WARNING! These patterns were not matched: *.h Exemple : $ pax -zf /Volumes/Linux/Packages/FAIT_2/Safari_1.2.pkg/Contents/Archive.pax.gz ./System/Library/Frameworks '*.h' $ pax -zf /Volumes/Linux/Packages/FAIT_2/Safari_1.2.pkg/Contents/Archive.pax.gz '*.h' ./System/Library/Frameworks La première commande génère l'avertissement dans la note ci-dessus car il N'y a PAS de fichier dont le nom se termine par .h AUTRE PART QUE DANS LES lignes déjà trouvées avec le pattern "./System/Library/Frameworks". Pas d'avertissement dans le second cas puisque le second pattern (de gauche à droite) AJOUTE des lignes au résultat obtenu avec le premier pattern ! Prendre le prétexte d'un conférencier allemand qui emprunte mon ordinateur pour ré-installer la localisation allemande de Keynote, un peu trop rapidement jetée pour gagner de la place en croyant que cela ne me servirait jamais. Conclure peut être par la commande installer (Installer en ligne de commande) qui aurait pu être le point de départ (et éventuellement juste l'évocation de softwareupdate) $ sudo installer -pkg /Library/Packages/derniereVersion.pkg -target / Faire une rapide mention des .mpkg pour expliquer que ce sont essentiellement des méta-paquetages qui ne font que de multiples références à des paquetages (.pkg) "traditionnels" dans le but d'en faciliter l'installation en n'ayant qu'une unique procédure à suivre pour installer l'ensemble ================================= pour l'article Terminal : lsbom lsbom avec détails lsbom -f -p MUGsTcf lsbom combiné avec pl Info.plist pour déterminer le dossier d'installation d'un package et en supprimer les fichiers lsbom avec options pour voir détails sur fichiers (pour vérifications : taille, proprio et surtout somme de contrôle) cksum /Applications/iSync.app/Contents/MacOS/iSync lsbom combiné avec grep pour vérifier si un package contient un certain fichier $ pl < /Library/Receipts/iStopMotion1.3.pkg/Contents/Info.plist | awk -F\" '/IFPkgReceiptLocation/ { print $2 }' Préfixer avec / (s'il n'y en a pas - sans problème si doublon alors test inutile... En insérer un systématiquement ! ) lsbom -> ajout préfixe chemin (voir ci-dessus) -> test intégrité fichiers (à la manière de Pacifist) -> NETTOYAGE (suppression) package (sur UN SEUL package à la fois, évite les notions de boucles) -> recherche d'origine (à la manière de --whatprovides dans rpm) => compléter avec commande d'extraction dans .pax correspondant [ -> RECOMPOSITION package (action disponible sur NeXT disparue sur OS X) ] ?? utilité de $ for p in /Library/Receipts/*.pkg; do basename "$p" .pkg ; done => ça donne la liste de pkg installés ! $ for p in /Library/Receipts/*.pkg; do [ -s "$p/Contents/Archive.bom" ] && file "$p/Contents/Archive.bom" || basename "$p" .pkg ; done D'OU, noms des pkg ne respectant pas la "nouvelle" (?) structure (avec emploi de Archive.bom) : $ for p in /Library/Receipts/*.pkg; do [ -s "$p/Contents/Archive.bom" ] || basename "$p" .pkg ; done $ for p in /Library/Receipts/*.pkg; do [ -s "$p/Contents/Archive.bom" ] && file "$p/Contents/Archive.bom" || file "$p/Contents/Resources/`basename $p .pkg`.bom" ; done L'EXPRESSION COMPLETE ! : $ for paquet in /Library/Receipts/*.pkg; do [ -s "$paquet/Contents/Archive.bom" ] && file "$paquet/Contents/Archive.bom" && continue || [ -s "$paquet/Contents/Resources/Archive.bom" ] && file "$paquet/Contents/Resources/Archive.bom" || file "$paquet/Contents/Resources/`basename "$paquet" .pkg`.bom" ; done MAIS CERTAINEMENT MIEUX A TOUT POINT DE VUE AVEC LA FORMULATION (plus court, plus clair, plus facilement exploitable pour des expressions plus complexes) : $ for paquet in /Library/Receipts/*.pkg; do [ -s "$paquet/Contents/Archive.bom" ] && ls "$paquet/Contents/Archive.bom" || ( [ -s "$paquet/Contents/Resources/Archive.bom" ] && ls "$paquet/Contents/Resources/Archive.bom" || ls "$paquet/Contents/Resources/`basename "$paquet" .pkg`.bom" ) ; done En essayant une alternative avec find (find /Library/Receipts/*.pkg -name "*.bom") je me suis rendu compte que, tel quel, ça ne pouvait pas marcher (en fait cela marche "trop bien") puisque on obtient nettement plus de fichier .bom que ce qui est attendu. L'explication tient en ce qu'un fichier .pkg peut parfois contenir DEUX .bom sous la forme PAQUETAGE.pkg/contents/Archive.bom ET PAQUETAGE.pkg/Contents/Resources/PAQUETAGE.bom, ce dernier fichier étant en fait un lien symbolique vers ../Archive.bom ON POURRAIT AFFINER L'EXPRESSION CI-DESSUS EN NE PRENANT QUE LES FICHIER "NORMAUX" : find /Library/Receipts/*.pkg -name "*.bom" -type f MALHEUREUSEMENT, cela n'est toujours pas parfait PARCE QU'il existe des exceptions notables (tous étant des pkg provenant d'Apple) avec l'existence de fichiers PAQUETAGE.pkg/Contents/Resources/PAQUETAGE.bom qui ne sont pas des liens symboliques mais qui se révèlent être de simples coquilles quasiment vide dont le contenu est systématiquement limité à : . 40755 1700/18010 Cela permet de supposer que le fichier PAQUETAGE.pkg/contents/Archive.bom est prioritaire sur PAQUETAGE.pkg/Contents/Resources/PAQUETAGE.bom dans la recherche entreprise par Installer.app (ou installer). Ma boucle for plus haut est donc correcte compte tenu de son ordre de recherche. LA FONCTION "--whatprovides" nuancée en quelque chose qui ressemble plus à un "--locate" : for paquet in /Library/Receipts/*.pkg; do ( [ -s "$paquet/Contents/Archive.bom" ] && lsbom "$paquet/Contents/Archive.bom" || ( [ -s "$paquet/Contents/Resources/Archive.bom" ] && lsbom "$paquet/Contents/Resources/Archive.bom" || lsbom "$paquet/Contents/Resources/`basename "$paquet" .pkg`.bom" ) ) | grep [-i] Verdana > /dev/null && echo $paquet ; done ==> REMARQUE IMPORTANTE : dû il semblerait à un petit souci interne à grep, il est préférable d'employer la redirection vers /dev/null plutôt que l'option -q pour ne pas voir les lignes dans lesquelles la chaîne de caractère recherchée est trouvée au milieu des noms des "bom" (donc des pkg) les contenant. Parler de installer (version CLI) ? => est-ce que l'on peut faire un test d'intégrité avec Elle NE permet PAS de manipules les "coquilles vides" que sont les pkg installés stockés dans /Library/Receipts (nommés justement receipts). ------------ (voir sur Palm pour autres détails) ------------- --- TRAVAIL ---- for paquet in /Library/Receipts/*.pkg; do [ -s "$paquet/Contents/Archive.bom" ] && lsbom "$paquet/Contents/Archive.bom" || ( [ -s "$paquet/Contents/Resources/Archive.bom" ] && lsbom "$paquet/Contents/Resources/Archive.bom" || lsbom "$paquet/Contents/Resources/`basename "$paquet" .pkg`.bom" ) ; done $ defaults read /Library/Receipts/Install\ VideoCapture\ utility.pkg/Contents/Info IFPkgReceiptLocation Applications/Ajouts/Capture MIEUX : $ RACINE=`defaults read /Library/Receipts/Install\ VideoCapture\ utility.pkg/Contents/Info IFPkgReceiptLocation` ; echo $RACINE OU $ RACINE=`defaults read '/Library/Receipts/Install VideoCapture utility.pkg/Contents/Info' IFPkgReceiptLocation` ; echo $RACINE LISTE SEULE des fichiers (sans préfixe du dossier d'installation) : $ PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -f -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" ) LISTE SEULE des fichiers (AVEC préfixe du dossier d'installation) : $ (PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -f -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | while read CHEMIN; do echo "/$RACINE/$CHEMIN"; done ==> NOUVEAU SOUCI : la représentation des resource forks à l'intérieur des .pax.gz qui est au format UFS SOLUTION (modification de l'expression précédente) ET CALCUL CHECKSUM plutôt que simple liste : $ (PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -f -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | sed 's,/._\(.*\),/\1/rsrc,' | while read CHEMIN; do cksum "/$RACINE/$CHEMIN"; done AMELIORATION (petite) consistant à afficher la somme présente dans le bom en début de chaque ligne (juste avant celle calculée à l'instant) : $ (PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -f -p cf "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -f -p cf "$PAQUET/Contents/Resources/Archive.bom" || lsbom -f -p cf "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | sed 's,/._\(.*\),/\1/rsrc,' | while read SOMME CHEMIN; do echo -n "$SOMME =? " ; cksum "/$RACINE/$CHEMIN"; done ==> LÀ ENCORE, les resource forks sont source de problème car leur codage UFS n'est pas identique à leur codage HFS+, ce qui rend caduque la vérification sur la somme de contrôle. En fait il faudrait pouvoir à ce moment convertir le format de la resource fork pour en avoir la version UFS et ainsi faire une comparaison dans des conditions correctes. GENERALISATION (remplacer la valeur de ACTION par la commande à appliquer sur chaque fichier sur le disque mentionné dans le paquetage) : $ ACTION="ls -l" ; (PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -f -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | sed 's,/._\(.*\),/\1/rsrc,' | while read CHEMIN; do $ACTION "/$RACINE/$CHEMIN"; done => ls -ld peut devenir cksum mais aussi [sudo] rm -d[v][f] (il semble que l'option -i de rm ne soit pas utilisable car j'ai comme l'impression qu'elle interfère avec la commande read) NOTE : l'emploi de sort -r n'est indispensable que pour des opérations comme rm pour lesquelles l'ordre de traitement importe. EXPRESSION VUE DE LA SUPPRESSION (nécessite deux aménagements : inclusion des noms des dossiers dans le listing ET inversion de l'ordre des lignes dans le listing) : $ ACTION="echo" ; (PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | sed 's,/._\(.*\),/\1/rsrc,' | sort -r | while read CHEMIN; do $ACTION "/$RACINE/$CHEMIN"; done ==> DERNIER DETAIL A REGLER, POUR LA SUPPRESSION, IL FAUT IGNORER (dans le cas du HFS+) les chemins qui se terminent par /rsrc (peut être faire un ',/rsrc$,d' avec sed pour simplement virer les lignes se finissant par /rsrc OU ALORS PLUS TÔT DANS LE PROCESS EN ViRANT LES ._ ... ALORS POUR TRAITER CE CAS (SAUF pour si les fichiers ont été copiés sur un volume UFS auquel cas on ignore simplement l'instruction sed dans ce qui suit), utiliser l'expression : $ ACTION="echo" ; (PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | sed '/\/._[^/]*$/d' | sort -r | while read CHEMIN; do $ACTION "/$RACINE/$CHEMIN"; done --- EN RESUME (en premier la méthode avec omission des resource forks - emploi pour rm par exemple) : $ PAQUET='/Library/Receipts/Install VideoCapture utility.pkg' ; RACINE=`defaults read "$PAQUET/Contents/Info" IFPkgReceiptLocation` ; echo $RACINE $ ACTION="echo" ; ( [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | sed '/\/._[^/]*$/d;/^.$/d' | sort -r | while read CHEMIN; do $ACTION "/$RACINE/$CHEMIN"; done ET SINON (3 différences notables avec la commande qui précède : le listing du bom privé des dossiers, le traitement des resource forks et le sens du tri (optionnel)) : // on reprend la même première expression pour affecter PAQUET et RACINE puis $ ACTION="ls -l" ; ( [ -s "$PAQUET/Contents/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Archive.bom" || ( [ -s "$PAQUET/Contents/Resources/Archive.bom" ] && lsbom -f -s "$PAQUET/Contents/Resources/Archive.bom" || lsbom -f -s "$PAQUET/Contents/Resources/`basename "$PAQUET" .pkg`.bom" )) | sed 's,/._\(.*\),/\1/rsrc,' | sort | while read CHEMIN; do $ACTION "/$RACINE/$CHEMIN"; done --- Correspondances noms et emplacements archives par rapport aux bom : NOTE : dans le cas d'un pkg dont les bom et pax reprennent le nom, les dénominations de ces derniers doivent être STRICTEMENT identiques au nom du PKG ! (D'ailleurs je ne sais trop sur quoi il se base car changer tous les noms de fichiers de concert ne marche pas mieux.) En fait, on doit pouvoir se baser sur le système de recherche des .bom décrit juste au-dessus pour trouver de la même façon les .pax.gz REMARQUE IMPORTANTE (qui nuance un peu l'hypothèse à la ligne ci-dessus) : il peut arriver que le pax ne soit pas compressé, auquel cas, l'extension .gz est tout simplement omise RadmindTools.pkg Contents/Resources RadmindTools.bom RadmindTools.pax.gz RDClientFor10_2.pkg Contents/Resources RDClientFor10_2.bom RDClientFor10_2.pax.gz RDUpdateFor10_2.pkg Contents/Resources RDUpdateFor10_2.bom RDUpdateFor10_2.pax.gz KeyspanUSAdrvr18.pkg Contents/Resources KeyspanUSAdrvr18.bom KeyspanUSAdrvr18.pax.gz X.pkg 3 Contents Archive.bom Archive.pax.gz CAS PARTICULIER : AsanteFAST_590.pkg AsanteFAST_590.bom DIRECTEMENT DANS LE DOSSIER .pkg ! AsanteFAST_590.pax.gz