AppleScript標準ライブラリ解剖 ― /Library/Scripts に眠るApple公式サンプル87本を読み解く
macOSには初期状態で /Library/Scripts に、Appleが書いた実用AppleScriptサンプルが7カテゴリ・87本入っています。ファイル内の著作権表記は2001年から2020年にまたがります。Finder・System Events・Image Events・システム設定を実際にどう叩くかの一次資料として、実機(macOS 26.6.2)の /Library/Scripts を対象に体系的に読み解きました。
構文そのものについてはAppleScript ポケット・リファレンスを、スクリプトエディタの使い方についてはスクリプトエディタと用語説明(辞書)の使い方を参照してください。
| 項目 | 値 |
|---|---|
| カテゴリ数 | 7 |
| スクリプトファイル数 | 87 |
行数(osadecompile 出力の合計) |
約6,700行 |
| Apple著作権表記の年代 | 2001〜2020年 |
大半は .scpt(コンパイル済み)で、GUIから開くとソースが読めません。osadecompile で全ファイルをテキスト化して読みました。7カテゴリはそれぞれ別の技術を実演する、独立した教材として設計されています。
以下のコード引用はすべて実ファイルからの抜粋です。長い箇所は英語コメントや周辺行を省略しています。日本語のコメント(-- に続く日本語)だけは本稿で補ったもので、原文にはありません。
目次
- ColorSync
- Folder Action Scripts
- Folder Actions
- Printing Scripts
- Script Editor Scripts
- UI Element Scripts
- VoiceOver
- 7カテゴリを貫く共通イディオム
- 現行macOSでの賞味期限
- このディレクトリをスキルアップにどう使うか
1. ColorSync
- ファイル数: 8(
.applescript) - 内容: Embed / Extract / Match / Proof / Remove / Rename / Set Info / Show Info
画像のカラープロファイルを埋め込み・抽出・除去・付け替え、あるいは情報表示する8本です。すべて sips(scriptable image processing system)をシェル経由で叩くラッパーで、AppleScript自体は「対象ファイルを受け取って sips に渡す」薄い層に徹しています。
Embed.applescript — パスを quoted form で安全化して sips に渡す
on open draggedItems
set embedProf to choose file with prompt "Choose profile to embed" default location POSIX file "/System/Library/ColorSync/Profiles"
repeat with thisFile in (draggedItems as list)
try
-- use 'sips --embedProfile' to embed the specified profile
-- or use 'sips --embedProfileIfNone' to embed the specified profile only if the image doesn't have an embeded profile
set profPath to quoted form of POSIX path of embedProf
set filePath to quoted form of POSIX path of thisFile
set cmdLine to ("sips --embedProfile " & profPath & " " & filePath) as string
do shell script cmdLine
end try
end repeat
end open
8本とも
on run(単体実行時はchoose fileで選ばせる)とon open(ドロップを受ける)の両方を備え、sipsを呼び、パスはquoted form of POSIX path of ...で安全化する ― この3点が完全に共通しています。一方でエラー処理は素のtryだけで、後述する-128の判定(第8章)を行いません。sipsが失敗しても画面には何も出ないので、この割り切りだけは自作スクリプトに持ち込まないほうが無難です。
2. Folder Action Scripts
- ファイル数: 13(
.scpt) - 内容: Image - Add Icon / Duplicate as JPEG・PNG・TIFF / Flip Horizontal・Vertical / Info to Comment / Rotate Left・Right(9本)、add - new item alert、close - close sub-folders、convert - PostScript to PDF、open - show comments in dialog
フォルダに監視用スクリプトとして実際にアタッチして使う実装本体です。画像系9本と convert - PostScript to PDF の計10本が、resolve_conflicts(重複ファイル名の自動連番)+ process_item(実処理)という同じ骨格を共有しています。違うのは process_item の中身と引数だけで、1本をひな型にコピー&置換で量産されたことが分かります。
Image - Add Icon.scpt 全体 — Folder Actionハンドラと共有ヘルパー2つ
property done_foldername : "Original Images"
property type_list : {"JPEG", "TIFF", "PNGf"}
property extension_list : {"jpg", "jpeg", "tif", "tiff", "png"}
on adding folder items to this_folder after receiving these_items
-- CHECK FOR THE DESTINATION FOLDER WITHIN THE ATTACHED FOLDER
-- IF IT DOESN'T EXIST, THEN CREATE IT
tell application "Finder"
if not (exists folder done_foldername of this_folder) then
make new folder at this_folder with properties {name:done_foldername}
set current view of container window of this_folder to list view
end if
set the target_folder to folder done_foldername of this_folder
end tell
-- PROCESS EACH OF THE ITEMS ADDED TO THE ATTACHED FOLDER
try
repeat with i from 1 to number of items in these_items
set this_item to item i of these_items
set the item_info to the info for this_item
if (alias of the item_info is false and the file type of the item_info is in the type_list) or (the name extension of the item_info is in the extension_list) then
tell application "Finder"
my resolve_conflicts(this_item, target_folder)
set the target_file to (move this_item to the target_folder with replacing) as alias
end tell
process_item(target_file)
end if
end repeat
on error error_message number error_number
if the error_number is not -128 then
tell application "Finder"
activate
display dialog error_message buttons {"Cancel"} default button 1 giving up after 120
end tell
end if
end try
end adding folder items to
on resolve_conflicts(this_item, target_folder)
tell application "Finder"
set the file_name to the name of this_item
if (exists document file file_name of target_folder) then
set file_extension to the name extension of this_item
if the file_extension is "" then
set the trimmed_name to the file_name
else
set the trimmed_name to text 1 thru -((length of file_extension) + 2) of the file_name
end if
set the name_increment to 1
repeat
set the new_name to (the trimmed_name & "." & (name_increment as string) & "." & file_extension) as string
if not (exists document file new_name of the target_folder) then
set the name of document file file_name of the target_folder to the new_name
exit repeat
else
set the name_increment to the name_increment + 1
end if
end repeat
end if
end tell
end resolve_conflicts
on process_item(this_item)
try
set this_item to this_item as string
with timeout of 900 seconds
tell application "Image Events"
launch -- always use with Folder Actions
set this_image to open file this_item
save this_image with icon
close this_image
end tell
end timeout
on error error_message
tell application "Finder"
activate
display dialog error_message buttons {"Cancel"} default button 1 giving up after 120
end tell
end try
end process_item
Folder Actionのハンドラ名は3種類あり、発火タイミングがそのまま名前になっています。13本の内訳は、項目追加時が11本、open - show comments in dialog が「開いた時」、close - close sub-folders が「閉じた時」です(引数名は大半が these_items で、add - new item alert だけ added_items)。
on adding folder items to this_folder after receiving these_items -- 項目追加時
on opening folder this_folder -- フォルダを開いた時
on closing folder window for this_folder -- ウインドウを閉じた時
エラー処理も統一されています。
on error msg number num/if num is not -128(-128はユーザーキャンセル)で握りつぶし、画像処理自体はImage Eventsアプリにwith timeout of 900 secondsを掛けて委譲します。
3. Folder Actions
- ファイル数: 4(
.scpt) - 内容: Attach Script to Folder、Enable Folder Actions、Disable Folder Actions、Remove Folder Actions
カテゴリ2のスクリプトを「フォルダに取り付ける/外す/消す」ための管理ツールです。System Events の folder action オブジェクトモデルをAppleScriptから直接操作する数少ない実例です。
Attach Script to Folder.scpt より抜粋 — フォルダにfolder actionを作り、スクリプトを登録する
property ErrorMsg : " is not a compiled script. (Ignored)."
-- on open DroppedItems の冒頭。取り付け先フォルダを選ばせる
choose folder with prompt ChooseFolderPrompt
set TargetFolder to the result as text
tell application "Finder" to ¬
set FAName to name of alias TargetFolder
tell application "System Events"
if folder action FAName exists then
--Don't make a new one
else
make new folder action ¬
at end of folder actions ¬
with properties {path:TargetFolder}
end if
end tell
repeat with EachItem in DroppedItems
set ItemInfo to info for EachItem
if not folder of ItemInfo then
set FileTypeOfItem to file type of ItemInfo
set FileExtensionOfItem to name extension of ItemInfo
set ItemName to name of ItemInfo
if FileTypeOfItem is "osas" or FileExtensionOfItem is "scpt" then
tell application "System Events"
tell folder action FAName
make new script ¬
at end of scripts ¬
with properties {name:ItemName}
end tell
end tell
else
display dialog ItemName & ErrorMsg with icon caution
end if
end if
end repeat
有効/無効の切り替えはたった1行です —
tell application "System Events" to set folder actions enabled to false。全体トグルとフォルダ単位の付け外しが別レイヤーであると分かります。
4. Printing Scripts
- ファイル数: 6(
.scpt) - 内容: About "Convert" Scripts、About "Print Window" Scripts、Convert To PDF、Convert To PostScript、Print Window、Print Window With Subfolders
「アプリとして保存してDock/デスクトップに置き、ファイルをドラッグして使うdroplet」の教科書的実装です。Finder選択・ドラッグ&ドロップ・ダブルクリックの3経路をすべて1本のスクリプトで受ける構造が共通します。
Convert To PDF.scpt の run ハンドラ — droplet三態分岐(Finder選択中/単体起動/ドラッグ)
on run {}
tell application "Finder" to set FinderSelection to the selection as alias list
set FS to FinderSelection
set SelectionCount to number of FS
if SelectionCount is 0 then
set FS to userPicksFolder() -- 選択なし→ダイアログで選ばせる
else if the SelectionCount is 1 then
set MyPath to path to me
if MyPath is item 1 of FS then
--If I'm a droplet then I was double-clicked
set FS to userPicksFolder()
end if
else
--I'm not a double-clicked droplet
end if
open FS -- on open ハンドラへ合流させる
end run
実際の変換は /System/Library/Printers/Libraries/./convert というApple内蔵ツールをシェル経由で叩いています(このコマンドは現行のmacOSには存在しません。第9章)。
on processFile(thePOSIXFileName)
try
set convertCommand to "/System/Library/Printers/Libraries/./convert "
set newFileName to thePOSIXFileName & ".pdf"
set terminalCommand to convertCommand & "-f " & "\"" & thePOSIXFileName & "\"" & " -o " & "\"" & newFileName & "\"" & " -j \"application/pdf\""
do shell script terminalCommand
end try
end processFile
一方、印刷そのものを行う Print Window With Subfolders.scpt は lpr へのシェルパイプに帰着します。
set theShellScript to ("( echo " & printedPath & " && ls -R \"" & printedPath & "\" ) | lpr " as string)
path to meと選択項目を比較して「自分がダブルクリックされたか」を判定するトリックは、他のdroplet設計にも転用できます。PDF/PostScript変換とプリンタへの実送信とで、シェルへの帰着先(convertコマンド/lpr)が違う点も実装上のポイントです。
5. Script Editor Scripts
- ファイル数: 48(
.scpt) - 内訳: Dialogs / Error Handlers / Conditionals / Tell Blocks / Repeat Routines / Iterate Items / Image Manipulation / Folder Actions Handlers / Action Clauses / String Comparison の10サブフォルダ+トップレベルの
About these scripts....scptとComment Tags.scpt
7カテゴリの中で最も変わっています。コードを書くAppleScript ― スクリプトエディタの「スクリプトメニュー」からワンクリックすると、開いているドキュメントの選択範囲を読み取り、それを定型コードで包んでカーソル位置に挿入する自己反映的なツール群です。
Tell Blocks/Tell "System Events".scpt 全体 — プレースホルダ置換によるテンプレート展開
set CR to ASCII character 13
set NL to ASCII character 10
tell application "Script Editor"
tell front document
set the target_string to "--XXXX"
set the selected_text to contents of selection
if the selected_text is "" then
set the selected_text to the target_string
set the script_text to ""
set the script_text to the script_text & "tell application \"System Events\"" & CR
set the script_text to the script_text & tab & the selected_text & CR
set the script_text to the script_text & "end tell" & CR
set the replacement_string to "-- insert actions here"
else
set the script_text to ""
set the script_text to the script_text & "tell application \"System Events\"" & CR
if last character of selected_text is in {CR, NL} then
set the script_text to the script_text & tab & the selected_text
else
set the script_text to the script_text & tab & the selected_text & CR
end if
set the script_text to the script_text & "end tell" & CR
set the replacement_string to ""
end if
set contents of selection to script_text
try
check syntax
end try
my replace_and_select(target_string, replacement_string)
try
check syntax
end try
end tell
end tell
on replace_and_select(target_string, replacement_string)
tell application "Script Editor"
tell the front document
set this_text to the contents
set this_offset to the offset of the target_string in this_text
if this_offset is not 0 then
set selection to characters this_offset thru (this_offset + (length of the target_string) - 1)
set the contents of the selection to the replacement_string
else
set selection to {}
end if
end tell
end tell
end replace_and_select
「テキストを挿入 → 挿入した文字列内の目印を検索 → その位置を選択状態にしてカーソルを誘導する」という
replace_and_selectヘルパーが、48本中47本(テンプレートを生成しない解説専用のAbout these scripts....scptを除く全部)で再利用されています。ファイルによってtell application "Script Editor"とtell current applicationのどちらを使うか、選択末尾の改行をどう扱うかなど細部に差はありますが、骨格は共通です。スニペット/コード生成ツールを自作する際にそのまま転用できる設計です。
6. UI Element Scripts
- ファイル数: 5(
.applescript、UTF-16) - 内容: Get User Name、Key Down-Up、Probe Menu Bar、Probe Window、Set Output Volume
スクリプティング辞書を持たないアプリでも、アクセシビリティAPI経由でボタンやスライダーを直接叩く「UI要素スクリプティング」の実例です。Probe Menu Bar / Probe Window はFinderを対象に、ウインドウやメニューバーの構成要素を丸ごとダンプする偵察用スクリプトで、対象アプリ名を書き換えれば他のアプリにも転用できます。
Set Output Volume.applescript 全体 — System Settings(System Preferences)のスライダーを操作
tell application "System Settings"
activate
set current pane to pane "com.apple.preference.sound"
end tell
try
tell application "System Events"
tell slider 1 of window "Sound" of process "System Preferences"
if value is 0.5 then
set value to 0.8
else
set value to 0.5
end if
end tell
end tell
on error errMsg
display dialog "Error: " & errMsg
end try
注目すべきは、ペインを開く側は
tell application "System Settings"なのに、UI要素の参照はprocess "System Preferences"のままという食い違いです。macOS 13で「システム環境設定」が「システム設定」に改称された際、前半のアプリ名だけが更新され、後半のプロセス名が取り残された跡だと読めます。macOS 26.6.2 にはSystem Preferences.appが存在せず、System Settings.appの実行ファイル名もSystem Settingsです(本稿では実行はせず、参照先の現存のみ確認しました)。したがって後半は該当プロセスを見つけられずエラーとなり、on errorの「Error: …」ダイアログに落ちます。プロセス名を書き換えれば直るかどうかは、システム設定のUI階層自体が当時とは別物になっているため、別途確かめる必要があります。UI要素スクリプティングがOSの改装で壊れる典型例として読むのが実用的です。
Key Down-Up.applescript より抜粋 — 修飾キーの2つの書き方
key down {shift, option}
keystroke "p"
key up {shift, option}
keystroke return
keystroke "p" using {shift down, option down}
keystroke return
押しっぱなし区間を明示したい場合は
key down/key upを、単発の修飾キー付きキー入力ならkeystroke ... using {...down}が簡潔です。
7. VoiceOver
- ファイル数: 3(
.applescript) - 内容: Time Of Day、Unread Message Count、VoiceOver Screenshot To Mail
スクリーンリーダーVoiceOverとAppleScriptを連携させる例です。3本すべてが同じ2つの前置ハンドラ(isVoiceOverRunning / isVoiceOverRunningWithAppleScript)をそのままコピーして先頭に置いています ― カテゴリ2の resolve_conflicts と同型の「共有ヘルパーの手動複製」パターンです。
Time Of Day.applescript より抜粋
on isVoiceOverRunning()
set isRunning to false
tell application "System Events"
set isRunning to (name of processes) contains "VoiceOver"
end tell
return isRunning
end isVoiceOverRunning
on isVoiceOverRunningWithAppleScript()
if isVoiceOverRunning() then
set isRunningWithAppleScript to true
-- is AppleScript enabled on VoiceOver --
tell application "VoiceOver"
try
set x to bounds of vo cursor
on error
set isRunningWithAppleScript to false
end try
end tell
return isRunningWithAppleScript
end if
return false
end isVoiceOverRunningWithAppleScript
-- 有効なら output で読み上げ、無効なら say コマンドにフォールバック
if isVoiceOverRunningWithAppleScript() then
tell application "VoiceOver"
output currentTime
end tell
else
say currentTime
delay 2
end if
VoiceOver Screenshot To Mailはvo cursorのgrab screenshotでVoiceOverが今フォーカスしている要素だけを撮影し、Mail.appの新規メッセージに添付します ― アクセシビリティ由来の座標情報をアプリ間で受け渡す珍しい実例です。
8. 7カテゴリを貫く共通イディオム
個別に読むと重複だらけですが、裏を返せば「Appleが繰り返し正解としたパターン」が透けて見えます。
-128で握りつぶす
ユーザーキャンセルのエラー番号 -128 だけを除外して、それ以外だけダイアログ表示します。87本中15本 ― Folder Action Scripts 10本(画像系9本と convert - PostScript to PDF)、Folder Actions の Attach Script to Folder、Script Editor Scripts 4本(Action Clauses/Timeout Clause、Error Handlers/Message if not Cancel、Image Manipulation/Resize・Scale)― で使われています。
try
-- 本処理
on error error_message number error_number
if the error_number is not -128 then
tell application "Finder"
activate
display dialog error_message buttons {"Cancel"} default button 1 giving up after 120
end tell
end if
end try
quoted form + do shell script
POSIXパスに空白や記号があってもシェルインジェクションを起こさないよう、パスを quoted form of (POSIX path of ...) に通してから渡します。使われているのは87本中9本 ― ColorSyncの全8本と convert - PostScript to PDF だけで、徹底されているとは言えません。実際、Printing Scriptsは同じ目的に "\"" & thePOSIXFileName & "\"" という手書きのダブルクォート囲みを使っています。引用符を含むファイル名を渡せば壊れる書き方で、真似すべきはColorSync側です。
set profPath to quoted form of POSIX path of embedProf
set filePath to quoted form of POSIX path of thisFile
set cmdLine to ("sips --embedProfile " & profPath & " " & filePath) as string
do shell script cmdLine
timeoutで重い処理を守る
Image Eventsなど時間のかかるアプリ通信は with timeout of 900 seconds で包み、AppleScript既定の2分(Apple「AppleScript Language Guide」による)というタイムアウトを回避します。使っているのは Folder Action Scripts の9本 ― 画像系8本(Image - Info to Comment を除く)と convert - PostScript to PDF です。
with timeout of 900 seconds
tell application "Image Events"
launch -- always use with Folder Actions
set this_image to open file this_item
save this_image with icon
close this_image
end tell
end timeout
フラグ関数で機能の有無を先に確認する
VoiceOverの2段階チェック(プロセスが動いているか→AppleScript制御に応答するか)は、外部アプリ連携全般に流用できる防御的パターンです。「動いていない前提」を先に潰してから本処理に入ります。
9. 現行macOSでの賞味期限
87本は2001〜2020年に書かれたものなので、依存先のコマンドやアプリがすでに失われているものがあります。実機(macOS 26.6.2)で参照先の現存を確認した結果が次の表です。読む前に把握しておくと、「書いてあるとおりなのに動かない」で悩まずに済みます。
| サンプル | 依存先 | macOS 26.6.2 での状態 |
|---|---|---|
| Printing Scripts / Convert To PDF・Convert To PostScript | /System/Library/Printers/Libraries/convert |
存在しない(ディレクトリは残るがコマンドがない) |
| Folder Action Scripts / convert - PostScript to PDF |
pstopdf コマンド |
存在しない(macOS 14でPostScriptインタプリタごと削除) |
| UI Element Scripts / Set Output Volume | process "System Preferences" |
存在しない(System Settings.app に改称) |
| ColorSync 8本 | sips |
健在(/usr/bin/sips) |
| Printing Scripts / Print Window・Print Window With Subfolders | lpr |
健在(/usr/bin/lpr) |
| Folder Action Scripts / 画像系9本 | Image Events.app | 健在 |
| VoiceOver 3本 | VoiceOver.app・Mail.app | 健在 |
| Script Editor Scripts 48本 | Script Editor.app | 健在 |
壊れている3系統も、読み物としての価値は変わりません。ただし手直しの難易度は一様ではありません。
PostScript関連は根が深く、macOS 13で「プレビュー」からPostScript対応が外れ、macOS 14ではPostScriptインタプリタ本体(PSNormalizer.framework)と pstopdf が削除されました。実機でも同フレームワークは存在せず、代替候補の cupsfilter に PostScript → PDF を指示すると「application/postscriptからapplication/pdfに変換するフィルタはありません。」と拒否されます。macOS標準の手段では代替できないため、動かしたい場合はGhostscript(ps2pdf)など別のツールを導入することになります。
一方 Set Output Volume の側は、対象アプリが入れ替わっただけです。同じカテゴリの Probe Window でシステム設定のUI階層を洗い直せば、書き直しの手がかりは自前で取れます。
なお、macOS 10.14以降は他アプリへApple Eventを送る際に「プライバシーとセキュリティ > オートメーション」での許可が必要です。初回実行時の許可ダイアログを拒否すると、以後はエラー -1743 で失敗し続けます。
10. このディレクトリをスキルアップにどう使うか
-
Script Editor Scripts(第5章)→ Folder Action Scripts(第2章)の順で読む。 前者で
tellブロックやエラー処理の「型」を頭に入れてから、後者でその型が実処理でどう組み合わされるかを見ると理解が早いです。 -
UI Element Scripts・VoiceOver(第6章・第7章)は自分のツールで試す。 スクリプタブルでないアプリを操作する数少ない実例です。
osascriptで1行ずつ動かして挙動を確認するのが近道です。 -
共通イディオムを自分のテンプレートに移植する。
-128握りつぶし・quoted form・timeoutの3点セットは、これから書くどのAppleScriptにもそのまま持ち込めます。 - スクリプトエディタから実際に呼び出してみる。 スクリプトエディタの「スクリプト」メニュー(スクリプトメニューが未表示ならスクリプトエディタの環境設定でメニューバー常時表示をON)からこれらは直接実行できます。ソースを見た後で動きを見ると定着します。
参考
- 全ファイル Copyright © Apple Inc.(2001〜2020年の各種年代表記)
-
osadecompileでデコンパイルしたテキストと、UTF-16原本の.applescriptファイルをもとに、macOS 26.6.2実機の/Library/Scriptsを解析。ファイル数・行数・イディオムの使用本数はいずれも実機での実測値。コード引用は学習目的の抜粋 - Apple「AppleScript Language Guide: Control Statements」—
with timeoutの既定値(2分)の出典 - Howard Oakley「PostScript's sudden death in Sonoma」—
pstopdfとPSNormalizer.frameworkが削除された経緯 - AppleScript ポケット・リファレンス — 構文・データ型・制御構文・Apple Event の内部構造
- スクリプトエディタと用語説明(辞書)の使い方 — スクリプトエディタの操作と辞書の読み方
元記事(Bitz Notebook): AppleScript標準ライブラリ解剖 ― /Library/Scripts に眠るApple公式サンプル87本を読み解く