1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AppleScript標準ライブラリ解剖 ― /Library/Scripts に眠るApple公式サンプル87本を読み解く

1
Posted at

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カテゴリはそれぞれ別の技術を実演する、独立した教材として設計されています。

以下のコード引用はすべて実ファイルからの抜粋です。長い箇所は英語コメントや周辺行を省略しています。日本語のコメント(-- に続く日本語)だけは本稿で補ったもので、原文にはありません。


目次

  1. ColorSync
  2. Folder Action Scripts
  3. Folder Actions
  4. Printing Scripts
  5. Script Editor Scripts
  6. UI Element Scripts
  7. VoiceOver
  8. 7カテゴリを貫く共通イディオム
  9. 現行macOSでの賞味期限
  10. このディレクトリをスキルアップにどう使うか

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. このディレクトリをスキルアップにどう使うか

  1. Script Editor Scripts(第5章)→ Folder Action Scripts(第2章)の順で読む。 前者で tell ブロックやエラー処理の「型」を頭に入れてから、後者でその型が実処理でどう組み合わされるかを見ると理解が早いです。
  2. UI Element Scripts・VoiceOver(第6章・第7章)は自分のツールで試す。 スクリプタブルでないアプリを操作する数少ない実例です。osascript で1行ずつ動かして挙動を確認するのが近道です。
  3. 共通イディオムを自分のテンプレートに移植する。 -128 握りつぶし・quoted form・timeout の3点セットは、これから書くどのAppleScriptにもそのまま持ち込めます。
  4. スクリプトエディタから実際に呼び出してみる。 スクリプトエディタの「スクリプト」メニュー(スクリプトメニューが未表示ならスクリプトエディタの環境設定でメニューバー常時表示をON)からこれらは直接実行できます。ソースを見た後で動きを見ると定着します。

参考


元記事(Bitz Notebook): AppleScript標準ライブラリ解剖 ― /Library/Scripts に眠るApple公式サンプル87本を読み解く

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?