1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

egword一括変換の落とし穴:Finderで開くのに自動化だと止まる理由と回避策

Posted at

Gemini_Generated_Image_6x68mz6x68mz6x68_opt.jpg

私の手元には、1995年前後からの膨大な「茶会記」が、当時の定番ワープロソフト「egword」の形式(.egword)で眠っています。30年前から書き残されている記録。かけがえのない「デジタル文化遺産」です。

現代最後の数寄茶人と言われるかたからお預かりした貴重な茶会記なのです

幸い、egwordは一度の販売終了を経て「物書堂」さんの手で復活を遂げました。しかし、500件を超える過去の書類を現代のiPhoneやMac、未来のデバイスで読めるようにするには、汎用的なテキスト形式(.txt)へ変換する高いハードルがありました。

今回は、一括変換を自動化しようとして直面した「macOSの罠」と、それをどう突破したかについてお話しします。

1. 最初の壁:AppleScriptを阻む「サイレント・ブロック」

AppleScriptを書いて「ファイルを開く→テキストとして書き出す→閉じる」操作を自動化すればすぐ終わると考えていました。しかし、いざ実行してみると、不可解な「タイムアウト」が頻発して処理が止まってしまうのです。

原因がわからず試行錯誤を繰り返した結果、真犯人はmacOSのセキュリティ仕様による「見えない警告ダイアログ」 でした。

CleanShot 2026-01-03 at 09.25.24.png

一部の古い書類を開こうとすると、OSが「Webコンテンツ」としての確認を求めてダイアログを出します。これがスクリプト経由だとバックグラウンドに隠れてしまい、ユーザーには見えないところで「返答待ち」の状態になっていたのです。

  • Finderから書類を開くと、警告をパスして開きます
  • terminalから 'open -a' で開いても、警告をパスして開きます
  • スクリプトエディタでASを実行すると、警告がバックグラウンドで開きます(操作できない)
  • スクリプトエディタでASからterminalに 'open -a' コマンドを送ると、警告がバックグラウンドで開きます(操作できない)
  • ショートカットアプリのなかでASを実行すると、警告ダイアログが出て、常に許可すれば警告をパスできるようになります

macOSか、EG Wordのバグだと思うんですけれど…

さらに厄介なことに、この「見えないダイアログ」が残っていると、次以降の正常なファイルも開けなくなるという連鎖的な不具合が発生していました。

回避するには、いちどアプリを終了させなければなりません。

この「さっきは開いたファイルが、いまは開かない」現象にも大変悩まされました

2. 解決策:ショートカットアプリとFinderへの「委ね」

この「OSの壁」を突破するために採用したのが、「ショートカット」アプリをコンテナとして使う方法でした。

CleanShot 2026-01-03 at 14.23.54_opt.jpg

  • 実行環境の変更: スクリプトエディタ単体ではなく、OSから高い権限を与えられているショートカットアプリ内にAppleScriptを組み込むことで、権限の問題をクリアしました。
  • Finder経由のオープン: アプリが直接ファイルを開くのではなく、Finderでファイルを選択し、クイックアクションでショートカットアプリ(のなかでAppleScriptを動かす)形にすることで、セキュリティによるサイレント・ブロックを回避できるようになりました。
  • 警告への自動応答: 「一部の属性を変更しました」といった特定の警告ウィンドウを検知し、自動でEnterキーを送信して処理を続行させるロジックを実装しました。

3. 「止まらない自動化」のための堅牢な設計

大量のファイルを扱うため、途中でエラーが起きても止まらず、かつ進捗がひと目でわかるような工夫を盛り込みました。

異常検知時の自動リセット

正常な処理ができなかった場合、スクリプト内で killall コマンドを実行してアプリを強制終了させます。これにより、バックグラウンドに残った「ゴミ(警告ダイアログ)」を確実に一掃し、次のファイル処理に影響を与えないようにしました。

Finderタグによる状態の可視化

変換の成否をFinderの「タグ(ラベル)」の色で管理するようにしました。

  • : 正常に変換完了。
  • : 既に変換済みのためスキップ。
  • : 警告は出たが、内容は救出できたもの。
  • : ファイル破損など、どうしても開けなかったもの。

進捗管理を、Finderのタグで行うことで、簡単かつわかりやすくなりました。
AppleScriptではなく、Finder→クイックアクション→ショートカット.appのフローにした、いい意味での副作用でした!

4. 現在の進捗とこれからの課題

「ショートカット.app+AppleScript」連携により、ようやく復元作業は軌道に乗りました。現在は2000年頃までの茶会記の復元がおおよそ完了しています。

しかし、1999年以前のさらに古い形式の書類については、現在のegword Universal 2でも開けないものが残っています。これらに対しては、Classic OS(Mac OS 9)環境を構築・自動化スクリプトを書いて救出を試みる、次なる挑戦になりそうです。

デジタルデータを「読める形」で残し続けることは、技術の進化との終わりなき戦いでもあります。もし同じように「昔の書類が開けなくて困っている」という方がいれば、この「自動化の落とし穴と回避策」が少しでも参考になれば幸いです。

村上春樹氏もEG Wordで大量にファイルを持っていそうなので、誰かが変換するんでしょうね🤔

5. 環境

以下のバージョンを利用しています。

  • egword Universal 2 バージョン2.3.1 (539)
  • ショートカット バージョン7.0(4046.0.2.2)
  • スクリプトエディタ 2.11 (233) / AppleScript 2.8
  • macOS Tahoe 26.2
  • MacBook Pro (14-inch, 2024)

6. コード(AppleScript部分)

on run {input, parameters}
	-- ★入力形式の統一
	set fPath to input
	try
		set fPath to POSIX path of (fPath as alias)
	on error
		set fPath to fPath as text
	end try
	
	-- ■ ログ開始
	my writeLog("========================================")
	my writeLog("【開始】処理ファイル: " & fPath)
	
	-- ==================================================
	-- ■ 1. パス計算と「変換済みチェック」
	-- ==================================================
	
	try
		-- (1) ディレクトリパスの計算
		set srcDir to do shell script "dirname " & quoted form of fPath
		
		set oldDelims to AppleScript's text item delimiters
		set AppleScript's text item delimiters to "11_adex_茶会記copy"
		set pathParts to text items of srcDir
		set AppleScript's text item delimiters to "12_text_茶会記"
		set destDir to pathParts as text
		set AppleScript's text item delimiters to oldDelims
		
		my writeLog("  保存先ディレクトリ: " & destDir)
		
		-- (2) 出力ファイル名の予想
		set baseName to do shell script "basename " & quoted form of fPath & " .egword"
		set expectedOutputPath to destDir & "/" & baseName & ".txt"
		
		-- (3) 既にファイルがあるか確認
		set fileExists to false
		try
			do shell script "test -f " & quoted form of expectedOutputPath
			set fileExists to true
		end try
		
		-- ★既に変換済みならスキップ (Yellow Tag)
		if fileExists then
			my writeLog("  [SKIP] 既にファイルが存在するためスキップします")
			my setFileLabel(fPath, 3) -- 黄色
			return input
		end if
		
		-- 保存先フォルダを作成
		do shell script "mkdir -p " & quoted form of destDir
		
	on error e
		my writeLog("  [ERROR] パス計算中にエラー: " & e)
		my setFileLabel(fPath, 2) -- 赤
		return input
	end try
	
	
	-- ==================================================
	-- ■ 2. ファイルを開く -> 変換
	-- ==================================================
	
	my writeLog("  ドキュメントを開きます...")
	set openResult to my openDocument(fPath)
	my writeLog("  開く結果: " & openResult)
	
	-- ★「OK(正常)」または「WARNING_OPENED(警告が出たが開いた)」なら処理続行
	if openResult is "OK" or openResult is "WARNING_OPENED" then
		try
			tell application "egword Universal 2"
				activate
				tell application "System Events"
					tell process "egword Universal 2"
						set frontmost to true
						delay 0.5
						
						-- (A) 書き出しダイアログを開く
						my writeLog("  (UI) 書き出しメニュー実行 (Opt+Cmd+E)")
						keystroke "e" using {command down, option down}
						delay 1.0
						
						-- (B) 設定変更と保存場所の指定
						if exists sheet 1 of window 1 then
							-- 形式を「テキスト」に変更
							try
								click pop up button 1 of sheet 1 of window 1
								delay 0.2
								click menu item "テキスト" of menu 1 of pop up button 1 of sheet 1 of window 1
								delay 0.5
								my writeLog("  (UI) 形式をテキストに変更")
							end try
							
							-- 保存先フォルダへ移動
							my writeLog("  (UI) 保存先へ移動")
							keystroke "g" using {command down, shift down}
							delay 0.8
							
							set the clipboard to destDir
							keystroke "v" using {command down}
							delay 0.5
							key code 36 -- Enter (移動)
							delay 0.5
							
							-- 書き出し実行
							my writeLog("  (UI) 書き出し実行")
							key code 36
							delay 2.0
							
							-- 上書き確認 (Tab -> Space)
							if exists sheet 1 of sheet 1 of window 1 then
								my writeLog("  (UI) 上書き確認 -> 置き換え")
								key code 48
								delay 0.5
								key code 49
								delay 1.0
							end if
						else
							error "書き出しシートが出ませんでした"
						end if
						
						-- 書き出し完了待ち
						delay 0.5
						
						-- (C) 書類を閉じる (⌘W)
						my writeLog("  (UI) 書類を閉じる")
						keystroke "w" using {command down}
						delay 0.8
						
						-- (D) 保存確認 (⌘D)
						if exists sheet 1 of window 1 then
							my writeLog("  (UI) 保存しない (Cmd+D)")
							keystroke "d" using {command down}
						end if
					end tell
				end tell
			end tell
			
			-- ★タグの分岐処理
			if openResult is "WARNING_OPENED" then
				-- 警告ありで変換: オレンジタグ (Label Index 1)
				my setFileLabel(fPath, 1)
				my writeLog("【完了】警告付きで完了 (オレンジ)")
			else
				-- 完全正常: 青タグ (Label Index 4)
				my setFileLabel(fPath, 4)
				my writeLog("【完了】正常終了 (青)")
			end if
			
		on error e
			-- ★失敗: 赤タグ (Label Index 2)
			my writeLog("  [ERROR] 処理中にエラー発生: " & e)
			my setFileLabel(fPath, 2)
			my forceQuitAndReset()
		end try
	else
		-- ★失敗: 赤タグ (Label Index 2)
		my writeLog("  [SKIP] ファイルが開けませんでした: " & openResult)
		my setFileLabel(fPath, 2)
		my forceQuitAndReset()
	end if
	
	return input
end run


-- ==========================================
-- 以下、ハンドラ(関数)
-- ==========================================

-- ★タグ色変更用汎用ハンドラ
on setFileLabel(fPath, labelIdx)
	-- labelIdx: 0=なし, 1=橙, 2=赤, 3=黄, 4=青, 5=紫, 6=緑, 7=灰
	try
		tell application "Finder"
			set theFile to (POSIX file fPath) as alias
			set label index of theFile to labelIdx
		end tell
		
		if labelIdx is 4 then
			my writeLog("  (TAG) 成功タグ(青)を設定しました")
		else if labelIdx is 2 then
			my writeLog("  (TAG) 失敗タグ(赤)を設定しました")
		else if labelIdx is 3 then
			my writeLog("  (TAG) スキップタグ(黄)を設定しました")
		else if labelIdx is 1 then
			my writeLog("  (TAG) 警告タグ(橙)を設定しました")
		end if
	on error e
		my writeLog("  [WARN] タグ変更中にエラー: " & e)
	end try
end setFileLabel


-- ★アプリ終了・再起動用ハンドラ
on forceQuitAndReset()
	my writeLog("  [RESET] アプリの再起動シーケンスを開始")
	
	-- 1. まずは正規の手順で終了 (quit)
	try
		tell application "egword Universal 2" to quit
		my writeLog("  [RESET] quitコマンドを送信しました")
	on error
		-- 既に終了している場合などはここに来るので無視
	end try
	
	delay 2.0
	
	-- 2. 強制終了チェック
	tell application "System Events"
		if (count of (processes whose name is "egword Universal 2")) > 0 then
			my writeLog("  [RESET] quitで終了しきれなかったため、強制終了(kill)を実行します")
			try
				do shell script "killall 'egword Universal 2'"
			end try
		else
			my writeLog("  [RESET] 正常に終了を確認しました")
		end if
	end tell
	
	-- 3. 再起動
	delay 2.0
	try
		my writeLog("  [RESET] アプリを起動します...")
		tell application "egword Universal 2" to activate
		delay 3.0
		my writeLog("  [RESET] 起動待機完了")
	on error e
		my writeLog("  [RESET] 再起動に失敗しました: " & e)
	end try
end forceQuitAndReset

on writeLog(msg)
	set logBaseDir to "/path/.../13_logs/"
	try
		set dateStr to do shell script "date +'%y%m%d'"
		set timeStr to do shell script "date +'%H:%M:%S'"
		set logFileName to dateStr & "_log.txt"
		set logPath to logBaseDir & logFileName
		do shell script "mkdir -p " & quoted form of logBaseDir
		set logContent to "[" & timeStr & "] " & msg
		do shell script "echo " & quoted form of logContent & " >> " & quoted form of logPath
	end try
end writeLog

on openDocument(fPath)
	try
		with timeout of 30 seconds
			tell application "Finder"
				open (POSIX file fPath) as alias using application file (path to application "egword Universal 2")
			end tell
			tell application "egword Universal 2" to activate
			
			-- 監視処理へ
			return my waitForDocumentWindow(5)
		end timeout
	on error e
		return "ERROR: " & e
	end try
end openDocument

-- ★ウィンドウ監視ハンドラ(修正版)
on waitForDocumentWindow(maxSeconds)
	repeat maxSeconds * 2 times
		tell application "System Events"
			tell process "egword Universal 2"
				if (count of windows) > 0 then
					set frontWin to window 1
					set winTitle to name of frontWin
					
					-- ■ 修正点: タイトルで特定して Enter を押す(一番確実)
					if winTitle is "一部の属性を変更しました。" then
						key code 36 -- Enter
						delay 1.0 -- ウィンドウが消えるのを待つ
						return "WARNING_OPENED"
						
					else if exists button "確認" of frontWin then
						-- タイトルが違っても「確認」ボタンがあれば押してみる
						key code 36
						delay 1.0
						return "WARNING_OPENED"
						
					else if exists button "OK" of frontWin then
						return "ERROR_DIALOG"
					else
						-- 通常のウィンドウ
						return "OK"
					end if
				end if
			end tell
		end tell
		delay 0.5
	end repeat
	return "TIMEOUT"
end waitForDocumentWindow
1
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?