Menu

Skip to content
AppleScriptの穴
  • Home
  • Products
  • Books
  • Docs
  • Events
  • Forum
  • About This Blog
  • License
  • 仕事依頼

AppleScriptの穴

Useful & Practical AppleScript archive. Click '★Click Here to Open This Script' Link to download each AppleScript

タグ: 15.0savvy

ASCII ART QR Encoder

Posted on 12月 31, 2025 by Takaaki Naganoya

指定のデータをASCII ARTで表現されたQRコードのテキストに変換するAppleScriptです。もともと、QR Code画像の作成は普通にできているので、あえてASCII ARTで作成する必要はないのですが、思いつきで作って(作らせて)みました。

この手の「作ってみるほどの価値はないが、冗談半分で作ってみる」のに人間のパワーを割くのは得策ではありません。ChatGPTに作らせてみました(2回ほど動かないコードが出てきましたが)。

作ったあとで他の作例がないか確認してみたところ、Terminal上でQRコードを表示するのに利用している例が見当たりました。

このASCII ART QR CodeのダイアログをiPhone内蔵カメラで認識させてみたところ、問題なく認識できました。

AppleScript名:ASCII ART QR Encoderscptd.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/12/31
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript
use framework "Foundation"
use framework "CoreImage"
use framework "AppKit"
use scripting additions

set testString to "http://piyocast.com/as/"
set asciiQR to makeAsciiQRCode(testString)

display dialog asciiQR buttons {"OK"} default button "OK"

— ASCII ART QR Encoder
on makeAsciiQRCode(theText)
  — 1. 文字列 → NSData
  
set textData to current application’s NSString’s stringWithString:theText
  
set dataObj to textData’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
  
— 2. QR生成
  
set qrFilter to current application’s CIFilter’s filterWithName:"CIQRCodeGenerator"
  
qrFilter’s setDefaults()
  
qrFilter’s setValue:dataObj forKey:"inputMessage"
  
qrFilter’s setValue:"M" forKey:"inputCorrectionLevel"
  
  
set ciImage to qrFilter’s outputImage()
  
  
— 3. CIImage → NSBitmapImageRep
  
set bitmapRep to current application’s NSBitmapImageRep’s alloc()’s initWithCIImage:ciImage
  
  
— サイズ取得
  
set pixelsWide to bitmapRep’s pixelsWide()
  
set pixelsHigh to bitmapRep’s pixelsHigh()
  
  
set resultText to ""
  
  
— 4. ピクセル走査
  
repeat with y from (pixelsHigh – 1) to 0 by -1
    set lineText to ""
    
repeat with x from 0 to (pixelsWide – 1)
      set pixelColor to (bitmapRep’s colorAtX:x y:y)
      
set grayColor to (pixelColor’s colorUsingColorSpaceName:(current application’s NSCalibratedWhiteColorSpace))
      
set brightness to grayColor’s whiteComponent()
      
      
if brightness < 0.5 then
        set lineText to lineText & "■"
      else
        set lineText to lineText & " " — 全角スペース
      end if
    end repeat
    
set resultText to resultText & lineText & linefeed
  end repeat
  
  
return resultText
end makeAsciiQRCode

★Click Here to Open This Script 

Posted in Image | Tagged 15.0savvy 26.0savvy | Leave a comment

2025年に書いた価値あるAppleScript

Posted on 12月 29, 2025 by Takaaki Naganoya

2025年に使用していたmacOS:macOS 15+macOS 26

毎年行なっている、Piyomaru Softwareが書いたAppleScriptの1年を振り返る記事の2025年版です。

2008年から10年ほど運営を続けてきた旧「AppleScriptの穴」Blogが2018年の年初にホスティング会社との行き違いでシャットダウンされ、ゼロから再構築したのが現行の「AppleScriptの穴」Blogです。

→ 2018年に書いた価値あるAppleScript
→ 2019年に書いた価値あるAppleScript
→ 2020年に書いた価値あるAppleScript
→ 2021年に書いた価値あるAppleScript
→ 2022年に書いた価値あるAppleScript
→ 2023年に書いた価値あるAppleScript
→ 2024年に書いた価値あるAppleScript

旧「AppleScriptの穴」Blogの内容については、データベースから抜き出したデータをもとに再構成した「Blogアーカイブ本」にまとめています。

AppleScriptの穴Blogアーカイブvol.1
AppleScriptの穴Blogアーカイブvol.2
AppleScriptの穴Blogアーカイブvol.3
AppleScriptの穴Blogアーカイブvol.4
AppleScriptの穴Blogアーカイブvol.5
AppleScriptの穴Blogアーカイブvol.6

本Blogは、もともとは、2000年代初頭に開発していた「人工知能インタフェース Newt On」のソースコード部品バラバラにして掲載し、用いた部品を個別にメンテナンスすることを「隠れた目的」としていました。また、Scripter間のノウハウの共有を推進することも目的としています。

AppleScript以外の一般的なテーマの記事については、こちらにいろいろ投稿しています。

https://note.com/140software/

前述のとおり、2018年1月にいちど本Blogは消えていました。その際に、「本Blogが存在しない場合にはどのような現象が起こるのか」を観察。その結果、AppleScriptについて知識を持たない人たちが好き勝手に「嘘」を流布しはじめる、という現象が観測されました。本Blogはそうした「嘘つき」を封じ込めるためのキーストーンとしての役割を果たしているといえます。

電子書籍の発行状況

本Blogを公開しているだけでは、ホスティング費用やドメイン費用がかかるだけで、何も収益が生まれません。そこで、本Blog+αの情報を整理してまとめた電子書籍を発行しています。本Blog読者のみなさまにおかれては、電子書籍を購入することで本Blog運営を支えていただけますと幸いです。

電子書籍の2025年末における刊行は、現時点で98冊。年間4冊となっています。

AppleScriptセキュリティ実践ガイド
AppleScript+Xcode GUIアプリソース集
AppleScript最新リファレンス v2.8対応 v2.0
スクリプトエディタScripting Book with AppleScript

目下、既刊本の最新環境へのアップデートを実行中です。

2025年に書いたAppleScriptの中で注目すべきもの

Blogよりも電子書籍用(掲載用、作業用)にかなりまとまったScriptを書いていたため、そちらの充実度が光ります。とはいえ、かなり技術的に重要なScriptをBlogにも多数書いています。ChatGPTを用いてScriptの作成を行なってみたところ、技術的な難易度が高い割につまらないとか、書くのが面倒くさいとか、すでに存在するものを作り直したくないといった「人間が書くのは気が乗らない」ものを中心に、Webブラウザとのやりとりだけで書けているので

2025年1月
NSObjectのクラス名を取得 v2.1

NSObjectのクラス名を取得 v2.1

Script Debuggerの開発が終了したことを受けて、Xcode上でAppleScriptObjCによるAppleScript開発環境の構築の可能性や、Xcode Projectの作業性の向上をねらって、とりあえずログに情報を出すためのまともな手段を用意しておこうと考えたものです。

こうした試みの成果により、Xode上でもともとのlogコマンドのように情報の確認が行えるようになってはいるものの、やはりlogコマンドが使えないと取りきれない(特定し切れない)バグなどもあるので、logコマンドの正常化には期待を寄せたいところです。

2025年2月
画像をAppleScriptでアスキーアート化

画像をAppleScriptでアスキーアート化

これは、Script内容自体は割とどうでもいいのですが、ChatGPTで高度なScriptを書かせる実験の一環です。やればできることはわかっていつつも、めんどうくさいので書きたくない種類のScriptをChatGPTに書かせてみました。

かなり使えるという手応えが得られたので、一歩進めてObjective-CのプログラムもChatGPTに書かせ、書籍のオマケ用に利用しています。

2025年3月
NSIndexSetを作成し、各index要素を取り出す

NSIndexSetを作成し、各index要素を取り出す

これも、ChatGPTに書かせたものです。必要に迫られて書く必要があったものに対して、ChatGPTを利用。Cocoa Scriptingのプログラムは、割とAPIの仕様を確認して、それに合わせるだけの内容のものが多いため、「プログラミングというよりも作業」だと感じていました。つまり、Cocoa ScriptingはChatGPTなどのLLMとの相性がいいと言えるかもしれません。

2025年4月
macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ

macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ

自分で書いたScriptという話ではなく、macOSのバグの話です。これをレポートするために、割といろいろ書いて試行錯誤しました。一応、macOS 15.5のRelease版で修正されたしたが、ちゃんと検証してからReleaseしてほしいところです。

Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行

Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行

Xcodeのログに出力するObjective-Cのプログラムを見つけたので、以前に作ったものと組み合わせてかなり便利に使えるようになりました。

2025年5月
macOS 26, Tahoe

macOS 26, Tahoe


Beta登場時に「Finderにempty trashコマンドを実行するとエラーになる」バグが発見されており、Release版で修正されていなかったことに驚かされました。

2025年6月
macOS 26, 15.5でShortcuts.app「AppleScriptを実行」アクションのバグが修正される

macOS 26, 15.5でShortcuts.app「AppleScriptを実行」アクションのバグが修正される

CotEditor用ブロック崩しゲーム

CotEditor用ブロック崩しゲーム

ChatGPTに書かせてみたものですが、CotEditorの最前面の書類ウィンドウの内容をAppleScriptで書き換えてゲームを実現してみました。

2025年7月
ASCII ARTで円を線で描く
ASCII ARTで円を塗る
ASCII ARTで直線を引く v3.1

テキストで作成した仮想画面に対して各種描画を行うものです。ChatGPTに書かせて手動で高速化を行なっています。

2025年8月
開始時刻から終了時刻までhh:mm形式の文字列を15分単位でリスト出力

開始時刻から終了時刻までhh:mm形式の文字列を15分単位でリスト出力

1か月分の(奥様の職場の)勤務パターンの視覚化、勤務形態を自動で処理できないかと考えて試作したものです。

2025年9月
暗黙のuseコマンド

暗黙のuseコマンド



個人的に忙しかったのですが、海外の掲示板で聞かれた内容についてまとめたものです。この記事を書く義理は400%なかったのですが、まとめておきました。掲示板にリンクURL を置いておき、あとは勝手に質問者が翻訳サービスなどを使って翻訳すればよいのでは? と、考えてそのまま放っておいたら、掲示板のアカウントが「日本語でコメントを書いた」などの理由で停止されていました。まったくおかしな理由でアカウント停止にされたので、その掲示板のことは忘れることにしました。なので、その掲示板にはログインできないし、メッセージを受け取ることもできません(何回書いても理解してもらえないようなので困っております。どうして被害者の自分が何回も無駄な説明をしなくてはならないのでしょう)。

macOS 15.xで自作AppleScriptライブラリの一部がScript Menuから使えなくなったので修正

macOS 15.xで自作AppleScriptライブラリの一部がScript Menuから使えなくなったので修正

割と困って、いろいろ書き換えてテストしていたのですが……どうやら、JXAをサポートするためにパラメータ(enum)をas stringでcastしていた部分が、スクリプトメニュー上で動かした場合にエラーになっていました。つまり、「AppleScriptライブラリでJXAをサポートしてはいけない」というのが現状のようです。

JXAはバージョン1.1でアップデートが停止しているため、動いても動かなくても知らないよという存在に見えます。

指定フォルダ内から指定拡張子のファイルを取得し、エイリアスだったらオリジナルのパスをPOSIX pathで取得

指定フォルダ内から指定拡張子のファイルを取得し、エイリアスだったらオリジナルのパスをPOSIX pathで取得

フォルダにScriptを入れて動作を行うプログラムにおいて、Scriptのエイリアスを入れておけば処理できるようにしたかったので書いたものです。

各種GUIアプリ書類のオープン速度を向上するためにUIアニメーションの一時停止を

各種GUIアプリ書類のオープン速度を向上するためにUIアニメーションの一時停止を

これは、久しぶりに大笑いしました。たったこれだけのささいな処理を追加するだけで、書類のオープン/クローズをともなう処理において、1書類あたり0.2秒、400書類で80秒も処理速度が向上。つまり、M1 MacでM4 Macと同等の速度を叩き出すことが可能になるわけで、実に爽快な話です(M4で実行すればM4なりに速くなります)。

スクリプトメニューでAirDropが使えない?

スクリプトメニューでAirDropが使えない?

macOS 15/26上のスクリプトメニューに対してAppleの魔の手が迫ってきました。セキュリティ上の制限がさらに厳しくなり、スクリプトメニュー内でAirDropを呼び出すAppleScriptの実行が妨害されるようになりました。

2025年10月
指定日が所属する週のうち、最終日の日付を求める v2

指定日が所属する週のうち、最終日の日付を求める v2

かなり込み入ったカレンダー計算系Scriptです。ただ、書いた甲斐はあったと思います。
カレンダー計算系のScriptは「Newt On Project」の遺産であり、自然言語とAppleScriptを組み合わせた処理を行ううえで、欠かせないものです。

FaceTimeカメラから取り込み v1

FaceTimeカメラから取り込み v1

Cocoaの機能を利用して、FaceTime Cameraから静止画を取り込むものです。いま、ちょうどXcode上で同様のテストを行なっているのですが、なかなか取り込めません。

タブでインデントしたテキストをOutLineViewで表示できるデータ形式に変換 v4(3階層対応)

タブでインデントしたテキストをOutLineViewで表示できるデータ形式に変換 v4(3階層対応)

NSOutlineViewをアラートダイアログ上に表示する「箱庭ダイアログ」系のScript Librariesを準備していますが、単にOutlineViewを表示できるだけでなく、タブでインデントしたテキストデータの構造を読み取ってOutlineViewを表示できるようにする必要性を感じて作成したものです。これも、めんどくさかったのでChatGPTに書かせて添削を行ないました。

階層を指定したlistからOutLineViewで表示できるデータ形式に変換 v4(3階層対応)

階層を指定したlistからOutLineViewで表示できるデータ形式に変換 v4(3階層対応)

1つのScriptを書けたからといって、運用上すべてのニーズを満たせるわけでもありません。複数のやりかたが存在するので、複数のデータ型でパラメータを受け取る準備はしておく必要があります。タブでインデントしたテキストが適しているパターンもあれば、リストで指定するのが適していることもあります。

macOS 15.7.2 スクリプトメニューから実行できなくなった地図系ライブラリ?

macOS 15.7.2 スクリプトメニューから実行できなくなった地図系ライブラリ?

NSAlertダイアログにさまざまなGUI部品を載せて利用する「箱庭ダイアログ」シリーズにおいて、地図を表示するものは実に使い勝手のよい部品です。ただし、スクリプトメニューから実行したときにMkMapViewを表示するScriptが実行できなくなっていました。とはいうものの、すべてが実行できなくなっているということではなく、一部に表示できるものもあり……まだ問題点の洗い出しが済んでいません。住所ジオコーダーまわりは実行できない環境が多いのですが、より機能が低いライブラリで実行できなかったりと、なかなか理解しにくいものがあります。

2025年11月

YouTubeムービー再生実験 v2c

YouTubeムービー再生実験 v2c

YouTube側の仕様が変わったことで、「display youtube」ライブラリを書き換える必要が出てきました。こうした箱庭UIライブラリを公開している人は(Shaneのライブラリは方向性が違いすぎるし)ほかにいないので、なかなか苦労させられます。幸いにしてedama2さんの助言を得られて最小限の修正(Local Web Serverを使用しない)で済みました。

Keynote書類上の選択中の2つのテキストアイテムで、左を比較元、右を比較先として行単位の差分を比較先に赤くマーク

Keynote書類上の選択中の2つのテキストアイテムで、左を比較元、右を比較先として行単位の差分を比較先に赤くマーク

diffとほぼ同じ動作をGUIアプリ上のオブジェクト内のテキスト同士に対して実行できています。Keynote版とPages版を用意しました。こういう道具の必要性を感じていたので、たいへん便利です。これまで、個人的に「車輪の再発明」をなるべく避けていましたが、自分で書くのでなければ再発明上等。ChatGPTは、他の処理系や言語で書かれた既存の処理をAppleScriptで再現するのが一番得意に感じます。

Posted in news | Tagged 15.0savvy 26.0savvy | Leave a comment

FSFindFolder failed with error=-43

Posted on 11月 29, 2025 by Takaaki Naganoya

# Because this information contains important content, linking to macscripter.net or reproduction is prohibited. This is because the macscripter.net administrator’s policy prohibits links to Japanese articles.

Xcode上で作成したAppleScript App Project(AppleScriptObjC)において、Projectに組み込んだ.scpt/.scptdを呼び出したときに、

のように、「FSFindFolder failed with error=-43」のエラーが出て実行できない問題に直面しました。

同エラーは特定のフォルダが見つからない、というCarbon由来のエラーのようですが、とくに呼び出しているScriptではファイルパスの処理は一切していません(カレンダー計算しているだけです)。

このエラーは電子書籍「AppleScript+Xcode GUIアプリソース集」の執筆時に、

本来はTable Viewに指定年の日本の祝祭日をすべて掲載するつもりだったのですが、その計算Scriptをバンドル内に組み込んで呼び出すとエラーに。

引数を単に返してくるだけの単純なハンドラ(testMeハンドラ)を呼び出す分にはエラーにならず、もちろんXcode Projectに組み込むAppleScriptはスクリプトエディタ/Script Debugger上で実行するとエラーになりません。

同書籍の執筆時には「とても解決するための時間が取れない」と考えて祝祭日の計算ライブラリを外して収録・掲載していました。ちょうど、Xcode 26+macOS 26に合わせた内容のアップデート作業を行なっていたところ、本来の計算機能を組み込んでみたところ、やはり動かないことからAppleにバグ(AppleScriptObjC.framework側?)として報告したものです。

組み込んだScriptを呼び出しても、シンプルなテストハンドラでは実行を妨げられないのですが、複雑な処理を行うと問題になります。

Posted in AppleScript Application on Xcode Bug | Tagged 15.0savvy 26.0savvy | Leave a comment

Keynote書類上の選択中の2つのテキストアイテムで、左を比較元、右を比較先として行単位の差分を比較先に赤くマーク

Posted on 11月 21, 2025 by Takaaki Naganoya

Keynote書類上で2つのテキストアイテムを選択し、左を比較元、右を比較先として行単位で差分を計算し、変更や追加が行われた行を赤く着色するAppleScriptです。

ほとんどの部分をChatGPTに書かせていますが、相当にやりとりしてダメ出ししないと書いてくれません。ほかにも、2D Bin Packing(任意の矩形図形xn個を指定エリアに詰め込む演算)のプログラムなどもAppleScriptで実装し直しておきたいところです。


▲Keynote書類上で2つのテキストアイテムを選択。座標値から左右を判定し、左側を比較元、右側を比較先として処理する


▲変更や新規追加のあった行を赤く着色する

こういうScriptを整備していないと、作業の負荷を下げられなくて大変です。

AppleScript名:Keynote書類上の選択中の2つのテキストアイテムで、左を比較元、右を比較先として行単位の差分を比較先に赤くマーク.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/11/21
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.8"
use framework "Foundation"
use scripting additions

— Keynote 側の処理:選択順で source / target を決め、target 側の差分行を赤くする
tell application "Keynote"
  tell front document
    set selItems to selection
    
if (count of selItems) is not 2 then
      display dialog "2つのテキストボックスを選択してください。(選択順が重要です)" buttons {"OK"} default button 1
      
return
    end if
    
    
set sourceTI to item 1 of selItems — 基準
    
set targetTI to item 2 of selItems — 変更を反映する対象
    
    
if (class of sourceTI) is not text item or (class of targetTI) is not text item then
      display dialog "選択されたオブジェクトがテキストボックスではありません。" buttons {"OK"} default button 1
      
return
    end if
    
    
–X座標値が小さい(左に存在する)ものを比較元とする
    
set {sPosX, sPosY} to position of sourceTI
    
set {tPosX, tPosY} to position of targetTI
    
if tPosX < sPosX then copy {sourceTI, targetTI} to {targetTI, sourceTI}
    
    
set sourceText to (object text of sourceTI) as string
    
set targetText to (object text of targetTI) as string
  end tell
end tell

set sourceLines to paragraphs of sourceText
set targetLines to paragraphs of targetText

— LCS を計算して target 側で「保持すべき行」のインデックスを決める
set pairs to computeLCS(sourceLines, targetLines)
set keepIdx to {}
repeat with p in pairs
  set end of keepIdx to item 2 of p — pair is {i, j} -> keep j (target index)
end repeat

— target の全テキストを一旦黒に戻す
tell application "Keynote"
  tell front document
    tell object text of targetTI
      set its color to {0, 0, 0}
    end tell
  end tell
end tell

— keepIdx に含まれない target の行を赤色にする
repeat with idx from 1 to (count targetLines)
  if keepIdx does not contain idx then
    set thisLine to item idx of targetLines
    
set startPos to offsetOfLine(idx, targetText)
    
set endPos to startPos + (length of thisLine)
    
if endPos ≥ startPos then
      tell application "Keynote"
        tell front document
          tell object text of targetTI
            set color of characters startPos thru endPos to {65535, 0, 0}
          end tell
        end tell
      end tell
    end if
  end if
end repeat

— LCS(最長共通部分列)を AppleScript のリストで実装(行単位)
— 戻り値: {{i1, j1}, {i2, j2}, …} という形式のペアリスト(1-based indices)
on computeLCS(listA, listB)
  set lenA to count listA
  
set lenB to count listB
  
  
— DP テーブル初期化: (lenA+1) 行 × (lenB+1) 列、全て 0
  
set dp to {}
  
repeat with r from 0 to lenA
    set row to {}
    
repeat with c from 0 to lenB
      set end of row to 0
    end repeat
    
set end of dp to row
  end repeat
  
  
— DP 計算(注意:dp の行は 0..lenA を 1..(lenA+1) 順で保持)
  
repeat with i from 1 to lenA
    
    
repeat with j from 1 to lenB
      set row_im1 to item i of dp — dp[i-1]
      
set row_i to item (i + 1) of dp — dp[i]
      
      
if (item i of listA) is equal to (item j of listB) then
        — dp[i][j] = dp[i-1][j-1] + 1
        
set prevVal to item j of row_im1 — dp[i-1][j-1]
        
set newVal to prevVal + 1
        
set item (j + 1) of row_i to newVal — store into dp[i][j]
        
        
— 更新した row_i を dp に戻す
        
set item (i + 1) of dp to row_i
      else
        
        
— dp[i][j] = max( dp[i-1][j], dp[i][j-1] )
        
set valUp to item (j + 1) of row_im1 — dp[i-1][j]
        
set valLeft to item j of row_i — dp[i][j-1]
        
        
if valUp ≥ valLeft then
          set item (j + 1) of row_i to valUp
        else
          set item (j + 1) of row_i to valLeft
        end if
        
set item (i + 1) of dp to row_i
        
      end if
    end repeat
  end repeat
  
  
— LCS を復元(後ろ向きに辿る)
  
set i to lenA
  
set j to lenB
  
set lcsPairs to {}
  
  
repeat while (i > 0 and j > 0)
    if (item i of listA) is equal to (item j of listB) then
      — 一致ペアを先頭に追加({{i, j}} & lcsPairs)
      
set lcsPairs to ({{i, j}} & lcsPairs)
      
set i to i – 1
      
set j to j – 1
    else
      — dp[i-1][j] と dp[i][j-1] を比較して移動方向を決める
      
set valUp to item (j + 1) of item i of dp — dp[i-1][j]
      
set valLeft to item j of item (i + 1) of dp — dp[i][j-1]
      
      
if valUp ≥ valLeft then
        set i to i – 1
      else
        set j to j – 1
      end if
      
    end if
  end repeat
  
  
return lcsPairs
end computeLCS

— 行の開始オフセット(文字単位、1-based)
on offsetOfLine(n, fullText)
  if n ≤ 1 then return 1
  
set paras to paragraphs of fullText
  
set pos to 1
  
repeat with idx from 1 to (n – 1)
    set pos to pos + (length of (item idx of paras)) + 1 — 改行分 +1
  end repeat
  
return pos
end offsetOfLine

★Click Here to Open This Script 

Posted in Object control Text | Tagged 15.0savvy 26.0savvy Keynote | Leave a comment

「AppleScriptセキュリティ実践ガイド」を発売

Posted on 11月 6, 2025 by Takaaki Naganoya

電子書籍の新刊を発売しました。「AppleScriptセキュリティ実践ガイド」です。PDF 562ページ、サンプルAppleScriptのZipアーカイブを添付。

→ 販売ページ

macOS上の各種機能を自由に呼び出せて、GUIベースのアプリの機能や情報を呼び出せる強力なAppleScriptは、ソースコードが読めない実行専用形式で、Scriptやアプレットとして公開可能です。

しかし、「暗号化済み」のように見える「実行専用形式」で保存されていたとしても、実際にはApple Events Codeという命令列の形で格納されており、文字列に関してはターミナル上でhexdumpやstringsコマンドによるダンプ出力が可能です。

さらに、昨今ではApple Events Codeそのものをダンプ(=内部の命令構造を可視化)するツールが登場。防衛用のツールですが、攻撃側もこれを利用できる状態にあります。

本書では、Apple Events Codeのダンプツールによってどのような情報が「抜かれ」、それらが実際にどのように見えるのかを詳細に検証しています。

実行専用のScript/アプレットで書き出した内容をダンプ(Apple Events Codeのダンプ)するapplescript-disassemblerと、その出力内容を読みやすく整形するaevt-decompileを用いて、さらにそれらを統合して使いやすくした統合ダンプツールをAppleScriptで開発。

対象フォルダ中のAppleScript書類を実行専用形式で書き出し、ダンプツールで解析して結果をテキスト書き出し……という作業フローを一括で行います。これを、さまざまな構造の複雑さを備えたテスト用AppleScriptに対して、さまざまなデータ型のデータを記述してどの程度漏洩するのか、しないのかを確認しています。

また、すべてのAppleScript予約語を含むサンプルScriptを作成し、個別にどのようにApple Events Codeにダンプされるかを掲載しています。

その上で、想定されるリスクと防止策を提示し、AppleScriptを安全に活用するための基礎資料としてご活用ください。

目次

1章 「実行専用」は安全なのか?

AppleScriptの概要と機能のひろがり/実行プログラムのひろがり/システム設定による機能制限/ヘックスダンプや文字ダンプで得られる情報/Apple Events関連の参考資料/コラム 本書掲載のAppleScriptリストの構文色分け設定について

2章 AppleScriptアプレットの構造

スクリプトエディタでアプレットを作成/書き出したアプレットの内部構造を追う/クラシックOS時代のアプレットのscptリソースの内容と比較

3章 Apple Eventダンプによる情報抽出

アップルスクリプトの完全なデコンパイルは無理/あくまでApple Events Codeとして解釈して読みやすくする/applescript-disassembler/aevt_decompile/統合分析ツールas_disassem+decompile/strings/hexdump

4章 実行専用スクリプトから漏れる情報の実態

Apple Events Code上で各データ型がどう見えるか?/プログラムの複雑さを変更して結果を調べる/アルファベット+数字/日本語の文字列/絵文字の文字列/真偽値/整数値/実数値/dateオブジェクト/数値のリスト(配列)/文字列のリスト(配列)/レコード/アプリケーションのオブジェクト/Xcodeで開発したAppleScrIptObjCアプリ

5章 情報漏洩をふせぐ技術的手法

安直なダンプで見えない形式でユーザー名やパスワードを記述/キーチェーンに情報を保存して内容を取り出すにしてもアクセス用キーワードは必要/データ暗号化の基礎①シフト暗号/データ暗号化の基礎②暗号表による換字式暗号/データ暗号化の基礎③多表式暗号と鍵のしくみ(ヴィジュネル暗号)/データ暗号化の基礎④「鍵」をさらに安全・自動的に扱う仕組みをもつOpenSSLによる現代暗号/プログラム難読化①データ形式変更/プログラム難読化②変数名絵文字置換/プログラム難読化③構造高度化

6章 安全な配布のための設計指針

アップルスクリプトの配布形態とかけられる労力の違い/配布時に最低限でもコード署名が必要。そのために開発者登録も必須/習熟のために経験しておきたいアプレットのオンライン配布

7章 AppleScriptのセキュリティ再考

最近発生したAppleScriptがらみの諸問題はメモリリーク/ユーザー名やパスワード、APIキーをスクリプト内に平文で記録すると危険/かつては可能だったアプリとAppleScript間の通信傍受もいまや不可能に/ネットワーク経由の操作は、途中で傍受される危険性も/AppleScript実行ツールで、埋め込みテキストのスクリプトを設定ファイルに平文保存?

8章 Apple Events Codeダンプ分析資料

全予約語を含む記述例を実行専用状態でApple Events Codeダンプ

9章 ダンプ結果から見る情報露出傾向と対策

コマンド、記述方法、データ型から読み解く露出傾向/script objectによるダンプ対策が有効/「OSAMiner事件」から再考されたAppleScript環境におけるセキュリティ対策/コラム 本書作成のために作成・使用したAppleScript

Posted in Books news Security | Tagged 15.0savvy 26.0savvy | Leave a comment

日本のMac App StoreにEntanglerがカムバックしていた。が……

Posted on 11月 4, 2025 by Takaaki Naganoya

iOSデバイス(iPhone/iPad)からMac上のAppleScriptを実行するアプリには、

  EventScript(LAN/WLANを介して実行)
  Entangler(iCloudを経由して遠隔実行)

の2つがあるのですが、Entanglerについては日本のMac App Storeからしばらく消えていました。

このため、各種電子書籍でもentanglerの紹介については差し止めていました。海外のMac App Storeに出ているのは確認していたのですが、日本のMac App Storeで入手できないのでは紹介できません。

久しぶりにMac App Storeでいろいろ確認作業を行なっていたところ(AppleScriptでキーワード検索する程度のかんたんな作業)、Entanglerを見つけることができました。

これらのアプリには、

EventScripts:Keynoteのスライドめくりなど、割と応答速度が求められるような操作
Entangler:外出先から部屋の様子を撮影してメッセージで返すなど、リアルタイム性がシビアに求められないが、遠くから実行することに意義のある操作

といった「棲み分け」が発生しており、両方が存在する意義があると思っています。

……と、ここまでなら「心あたたまるニュース」であったのですが、Mac App Store上のリンクURLのリンク先(http://andymolloy.net/Entangler/)が応答不能(No Response)になっており、ちょっと心配です。

https://www.entanglerapp.com も、中華系のサイトが表示されるなど、きな臭い印象です。

ドメイン失効時にドメイン占有業者に乗っ取られたと見るべきでしょうか。ただ、これまで日本のMac App Storeに出ていなかったこのアプリが復帰した経緯がよくわかりません。

今後も、行方を注目しておくべきでしょう。

Posted in news Remote Control | Tagged 15.0savvy | Leave a comment

macOS 15.7.2 スクリプトメニューから実行できなくなった地図系ライブラリ?

Posted on 10月 11, 2025 by Takaaki Naganoya

macOS 15.7.2やmacOS 26.x上のスクリプトメニューで地図表示系のライブラリ呼び出し、とくにchoose location libが動作しません。

てっきり「スクリプトメニューで地図系のAPIへのアクセスが禁止されたのか?」と思っていたのですが、

同じく地図系APIにアクセスしているdisplay locationライブラリはスクリプトメニューから呼び出せています。

不思議。

以下に、AppleScriptライブラリの呼び出しが可能なAppleScript実行環境の一覧を示し、呼び出しの検証が行えているものは「Yes」を、検証して動かないことを確認したものに「No」と記入しています。

Program Name Can Call choose location Libraries?
Script Debugger 8 Yes
Script Editor Yes
Mail.app (rule–> Run AppleScript)
Shortcuts.app No Can not load map image
Shortcuts Events No Can not load map image
Script Menu (macOS 10.14 or later) No Can not execute
Terminal.app (osascript) Yes
Service Station
elgato Stream Deck
BBEdit No Can not execute
Jedit Ω
Adobe Indesign
Adobe Illustrator
Music (Script Menu) No Can not execute
Microsoft Excel
Microsoft Word
Microsoft PowerPoint
Keyboard Maestro
EventScript
FastScript 3 Yes
LaunchPalette
Visual Studio Code extension Yes
Dropzone 4
ScreenFloat
Xojo 2025 No Error
Live Code
Posted in Library Map | Tagged 15.0savvy 26.0savvy | 1 Comment

階層を指定したlistからOutLineViewで表示できるデータ形式に変換 v4(3階層対応)

Posted on 10月 10, 2025 by Takaaki Naganoya

入れ子のlist(2D List)で {{インデント階層+タイトル}…} 指定したデータを、NSOutlineViewで表示できるデータ形式に変換するAppleScriptです。

Keynoteに各スライドのインデント・レベルをAppleScriptに返す機能が存在しないため、各スライドのマスタースライド名から「このスライドマスターは、この階層に使いがち」といったルールをもとに階層関係を推測して処理しています。

主に、Keynote書類の各スライド(ページ)上のタイトルを、そのマスタースライド名をもとにインデントレベルを仮想的に判定し、PDFのTOCを作成する際の「プレビュー」を行うべく、NSOutLineViewで表示するために作成したものです。

これ自体は、2d ListからNSOutLineView表示用のデータを作成するだけのScriptです。

n階層をサポートしています。0がトップ階層。1がタブ1個と等価。2がタブ2個インデントと等価です。


{{0, "表紙"}, {0, "目次"}, {0, "1章"}, {1, "セクション1"}, {2, "項目1"}, {3, "コラム1"}, {2, "項目2"}, {3, "コラム2"}, {1, "セクション2"}, {2, "項目3"}, {0, "2章"}, {1, "セクションA"}, {2, "項目A1"}, {2, "項目A2"}, {1, "セクションB"}, {0, "裏表紙"}}

こんなデータを、

{{|name|:"表紙", isLeaf:true, children:{}}, {|name|:"目次", isLeaf:true, children:{}}, {|name|:"1章", isLeaf:false, children:{{|name|:"セクション1", isLeaf:false, children:{{|name|:"項目1", isLeaf:false, children:{{|name|:"コラム1", isLeaf:true, children:{}}}}, {|name|:"項目2", isLeaf:false, children:{{|name|:"コラム2", isLeaf:true, children:{}}}}}}, {|name|:"セクション2", isLeaf:false, children:{{|name|:"項目3", isLeaf:true, children:{}}}}}}, {|name|:"2章", isLeaf:false, children:{{|name|:"セクションA", isLeaf:false, children:{{|name|:"項目A1", isLeaf:true, children:{}}, {|name|:"項目A2", isLeaf:true, children:{}}}}, {|name|:"セクションB", isLeaf:true, children:{}}}}, {|name|:"裏表紙", isLeaf:true, children:{}}}

のように変換します。

NSOutlineViewをダイアログ表示するAppleScriptライブラリを作りかけて、データ作成部分もライブラリに組み込まないと使い物にならないと気づき、ChatGPTに作らせたものです。

AppleScript名:階層を指定したlistからOutLineViewで表示できるデータ形式に変換 v4(3階層対応).scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/10/10
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.8"
use framework "Foundation"
use scripting additions

— サンプル入力(3階層以上)
set srcList to {{0, "表紙"}, {0, "目次"}, {0, "1章"}, {1, "セクション1"}, {2, "項目1"}, {3, "コラム1"}, {2, "項目2"}, {3, "コラム2"}, {1, "セクション2"}, {2, "項目3"}, {0, "2章"}, {1, "セクションA"}, {2, "項目A1"}, {2, "項目A2"}, {1, "セクションB"}, {0, "裏表紙"}}

set aRec to convlistToStructuredRec(srcList) of me
–> {{|name|:"表紙", isLeaf:true, children:{}}, {|name|:"目次", isLeaf:true, children:{}}, {|name|:"1章", isLeaf:false, children:{{|name|:"セクション1", isLeaf:false, children:{{|name|:"項目1", isLeaf:false, children:{{|name|:"コラム1", isLeaf:true, children:{}}}}, {|name|:"項目2", isLeaf:false, children:{{|name|:"コラム2", isLeaf:true, children:{}}}}}}, {|name|:"セクション2", isLeaf:false, children:{{|name|:"項目3", isLeaf:true, children:{}}}}}}, {|name|:"2章", isLeaf:false, children:{{|name|:"セクションA", isLeaf:false, children:{{|name|:"項目A1", isLeaf:true, children:{}}, {|name|:"項目A2", isLeaf:true, children:{}}}}, {|name|:"セクションB", isLeaf:true, children:{}}}}, {|name|:"裏表紙", isLeaf:true, children:{}}}

on convlistToStructuredRec(allLines)
  set topList to {} — 最終出力
  
set stack to {} — 階層ごとの親ノードを保持
  
  
repeat with aLine in allLines
    copy aLine to {tabCount, lineStr}
    
set lineNS to (current application’s NSString’s stringWithString:(lineStr as text))
    
set contentText to (lineNS’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet())) as text
    
if contentText is "" then
      — 空行スキップ
    else
      — 新しいレコード
      
set newRec to {|name|:contentText, isLeaf:true, children:{}}
      
      
if tabCount = 0 then
        — トップレベル
        
set end of topList to newRec
        
set stack to {newRec}
      else
        — stack を tabCount に合わせて切り詰め
        
if (count of stack) ≥ tabCount then
          set stack to items 1 thru tabCount of stack
        end if
        
        
— 親ノード取得
        
set parentRec to item tabCount of stack
        
        
— 親の children に追加
        
set parentChildren to parentRec’s children
        
set end of parentChildren to newRec
        
set parentRec’s children to parentChildren
        
        
— 親の isLeaf を false に
        
set parentRec’s isLeaf to false
        
        
— stack に追加
        
set end of stack to newRec
      end if
    end if
  end repeat
  
  
return topList
end convlistToStructuredRec

★Click Here to Open This Script 

Posted in list Record | Tagged 15.0savvy 26.0savvy | 1 Comment

タブでインデントしたテキストをOutLineViewで表示できるデータ形式に変換 v4(3階層対応)

Posted on 10月 10, 2025 by Takaaki Naganoya

タブでインデントしたテキストを、NSOutlineViewで表示できるデータ形式に変換するAppleScriptの改良版です。

主に、Keynote書類の各スライド(ページ)上のタイトルを、そのマスタースライド名をもとにインデントレベルを仮想的に判定し、PDFのTOCを作成する際の「プレビュー」を行うべく、NSOutLineViewで表示するために作成したものです。

ChatGPTにオーダーを出して、エラー内容と処理結果をそのまま差し返すことで、数回このフローを繰り返して正しい処理内容を取得できました。

これ自体は、テストデータ(テキスト)からNSOutLineView表示用のデータを作成するだけのScriptです。

Tab n個のn階層をサポートしています。

表紙
目次
1章
	セクション1
		項目1
			コラム1
		項目2
			コラム2
	セクション2
		項目3
2章
	セクションA
		項目A1
		項目A2
	セクションB
裏表紙

こんなデータを、

{{|name|:"表紙", isLeaf:true, children:{}}, {|name|:"目次", isLeaf:true, children:{}}, {|name|:"1章", isLeaf:false, children:{{|name|:"セクション1", isLeaf:false, children:{{|name|:"項目1", isLeaf:false, children:{{|name|:"コラム1", isLeaf:true, children:{}}}}, {|name|:"項目2", isLeaf:false, children:{{|name|:"コラム2", isLeaf:true, children:{}}}}}}, {|name|:"セクション2", isLeaf:false, children:{{|name|:"項目3", isLeaf:true, children:{}}}}}}, {|name|:"2章", isLeaf:false, children:{{|name|:"セクションA", isLeaf:false, children:{{|name|:"項目A1", isLeaf:true, children:{}}, {|name|:"項目A2", isLeaf:true, children:{}}}}, {|name|:"セクションB", isLeaf:true, children:{}}}}, {|name|:"裏表紙", isLeaf:true, children:{}}}

のように変換します。

NSOutlineViewをダイアログ表示するAppleScriptライブラリを作りかけて、データ作成部分もライブラリに組み込まないと使い物にならないと気づき、ChatGPTに作らせたものです。

AppleScript名:タブでインデントしたテキストをOutLineViewで表示できるデータ形式に変換 v4(3階層対応).scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/10/10
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.8"
use framework "Foundation"
use scripting additions

— サンプル入力(3階層以上)
set srcText to "表紙
目次
1章
  セクション1
    項目1
      コラム1
    項目2
      コラム2
  セクション2
    項目3
2章
  セクションA
    項目A1
    項目A2
  セクションB
裏表紙"

set aRec to convStrToStructuredRec(srcText) of me
–> {{|name|:"表紙", isLeaf:true, children:{}}, {|name|:"目次", isLeaf:true, children:{}}, {|name|:"1章", isLeaf:false, children:{{|name|:"セクション1", isLeaf:false, children:{{|name|:"項目1", isLeaf:false, children:{{|name|:"コラム1", isLeaf:true, children:{}}}}, {|name|:"項目2", isLeaf:false, children:{{|name|:"コラム2", isLeaf:true, children:{}}}}}}, {|name|:"セクション2", isLeaf:false, children:{{|name|:"項目3", isLeaf:true, children:{}}}}}}, {|name|:"2章", isLeaf:false, children:{{|name|:"セクションA", isLeaf:false, children:{{|name|:"項目A1", isLeaf:true, children:{}}, {|name|:"項目A2", isLeaf:true, children:{}}}}, {|name|:"セクションB", isLeaf:true, children:{}}}}, {|name|:"裏表紙", isLeaf:true, children:{}}}

on convStrToStructuredRec(srcText)
  set nsText to current application’s NSString’s stringWithString:srcText
  
set allLines to nsText’s componentsSeparatedByCharactersInSet:(current application’s NSCharacterSet’s newlineCharacterSet())
  
  
set topList to {} — 最終出力
  
set stack to {} — 階層ごとの親ノードを保持
  
  
repeat with aLine in allLines
    set lineNS to (current application’s NSString’s stringWithString:(aLine as text))
    
set trimmedStr to (lineNS’s stringByTrimmingCharactersInSet:(current application’s NSCharacterSet’s whitespaceAndNewlineCharacterSet())) as text
    
if trimmedStr is "" then
      — 空行スキップ
    else
      — 行頭タブ数
      
set len to lineNS’s |length|()
      
set tabCount to 0
      
repeat with i from 0 to (len – 1)
        set ch to (lineNS’s characterAtIndex:i)
        
if (ch as integer) = 9 then
          set tabCount to tabCount + 1
        else
          exit repeat
        end if
      end repeat
      
      
— タブ除去文字列
      
if len > tabCount then
        set contentText to (lineNS’s substringFromIndex:tabCount) as text
      else
        set contentText to ""
      end if
      
      
— 新しいレコード
      
set newRec to {|name|:contentText, isLeaf:true, children:{}}
      
      
if tabCount = 0 then
        — トップレベル
        
set end of topList to newRec
        
set stack to {newRec}
      else
        — stack を tabCount に合わせて切り詰め
        
if (count of stack) ≥ tabCount then
          set stack to items 1 thru tabCount of stack
        end if
        
— 親ノード取得
        
set parentRec to item tabCount of stack
        
— 親の children に追加
        
set parentChildren to parentRec’s children
        
set end of parentChildren to newRec
        
set parentRec’s children to parentChildren
        
— 親の isLeaf を false に
        
set parentRec’s isLeaf to false
        
— stack に追加
        
set end of stack to newRec
      end if
    end if
  end repeat
  
  
return topList
end convStrToStructuredRec

★Click Here to Open This Script 

Posted in list Record Text | Tagged 15.0savvy 26.0savvy | Leave a comment

FaceTimeカメラから取り込み v1

Posted on 10月 8, 2025 by Takaaki Naganoya

MacBook Air/Pro/iMac搭載のFaceTime HDカメラから静止画を取り込むAppleScriptです。

QRコードの画像を取り込んで、QRコードを認識させて、URLを含んでいる場合にはWebブラウザでオープンするといったワークフローを作っておこうかと考えて書いてみました。

ただ、まだ調べ始めて日がないため、取り込み中の画像のプレビューは表示されません。FaceTime HDカメラが点灯して2秒ぐらいで何も言わずに画像取り込みされる無愛想なプログラムになっています。

本当はAVCaptureViewを用いてプレビュー表示が行われることがベストです。アラートダイアログ上にプレビューが表示できれば……そのうち、できるようになるのではないかと。

AppleScript名:FaceTimeカメラから取り込み v1.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/10/07
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.8"
use framework "Foundation"
use framework "AVFoundation"
use scripting additions

property captureSession : missing value
property stillOutput : missing value
property saveURL : missing value

on run
  set aPath to POSIX path of (choose file name)
  
my captureStillImage(aPath)
end run

on captureStillImage(aPath)
  set saveURL to current application’s |NSURL|’s fileURLWithPath:aPath
  
  
— セッション作成
  
set captureSession to current application’s AVCaptureSession’s alloc()’s init()
  
captureSession’s setSessionPreset:(current application’s AVCaptureSessionPresetPhoto)
  
  
— カメラデバイスを取得
  
set device to (current application’s AVCaptureDevice’s defaultDeviceWithMediaType:(current application’s AVMediaTypeVideo))
  
if device = missing value then
    display dialog "カメラが見つかりません。" buttons {"OK"} default button 1
    
return
  end if
  
  
— 入力設定
  
set input to (current application’s AVCaptureDeviceInput’s deviceInputWithDevice:device |error|:(missing value))
  
if input = missing value then
    display dialog "カメラ入力を初期化できません。" buttons {"OK"} default button 1
    
return
  end if
  
  
if (captureSession’s canAddInput:input) as boolean then
    captureSession’s addInput:input
  else
    display dialog "入力を追加できません。" buttons {"OK"} default button 1
    
return
  end if
  
  
  
— 出力設定
  
set stillOutput to current application’s AVCapturePhotoOutput’s alloc()’s init()
  
if (captureSession’s canAddOutput:stillOutput) as boolean then
    captureSession’s addOutput:stillOutput
  else
    display dialog "出力を追加できません。" buttons {"OK"} default button 1
    
return
  end if
  
  
  
— セッション開始
  
captureSession’s startRunning()
  
delay 1 — カメラ起動待機
  
  
— 写真設定
  
set photoSet to (current application’s AVCapturePhotoSettings’s photoSettings)
  
  
— 撮影
  (
stillOutput’s capturePhotoWithSettings:photoSet delegate:me)
  
  
delay 2
  
captureSession’s stopRunning()
end captureStillImage

on captureOutput:(output) didFinishProcessingPhoto:(photo) |error|:err
  if err ≠ missing value then
    display dialog "撮影エラー: " & (err’s localizedDescription() as text)
    
return
  end if
  
set imageData to photo’s fileDataRepresentation()
  
imageData’s writeToURL:saveURL atomically:true
  
display notification "画像を保存しました。" with title "カメラキャプチャ"
end captureOutput:didFinishProcessingPhoto:|error|:

★Click Here to Open This Script 

Posted in Image System | Tagged 15.0savvy 26.0savvy | 1 Comment

指定日が所属する週のうち、最終日の日付を求める v2

Posted on 10月 7, 2025 by Takaaki Naganoya

指定日を基準として「今週末」までの日付を求めるAppleScriptです。

日付の範囲指定で「今週」という指定を行った場合、+7日を足して相対的に1週間先の日付を返すという処理方法もありますが、(日本国内の)常識的には週末までの日付を返すことでしょう。

そして、今週末といった場合に、日曜日はじまりのカレンダーであれば土曜日が該当するわけですが、全世界的に考えると「日曜日はじまりではない」カレンダーを採用しているエリア(国)も存在しています。

月曜日はじまりのカレンダーを採用している国には、ヨーロッパ全域(EU諸国?)などがあるようです。

「今週末」というキーワードをmacOSの自然言語処理系の機能を用いて具体的な日付に変換することは可能ですが、OSアップデートのたびにAppleがバグを作りやすい機能なので(こんなところにまでバグを作るAppleのエンジニアが信じられない)、もうちょっと安心して計算できるプログラムがあったほうがいいと考えました。

そこで、すでに作ってあるさまざまな日付計算系のルーチンを組み合わせて計算してみました。

AppleScript名:指定日が所属する週のうち、最終日の日付を求める v2.scpt
— Created 2015-02-02 by Shane Stanley
— Modified 2015-02-02 by Takaaki Naganoya
— Modified 2025-10-01 by Takaaki Naganoya

— v2 最終週で、カレンダーが途中で途切れているケースに対応

use AppleScript
use scripting additions
use framework "Foundation"

set targY to 2025
set targM to 9
set targD to 21

set theLastDay to calcLastDayFromTheTargetDay(targY, targM, targD) of me
–> date "2025年9月27日 土曜日 0:00:00"

–指定日が所属する週のうち、最終日の日付を求める
on calcLastDayFromTheTargetDay(targY, targM, targD)
  –指定日が月内で何週目に該当するかを算出
  
set targWN to getDatesWeekNumberWithinAMonth(targY, targM, targD) of me
  
–> 4
  
  
–指定日の当該週の日付を1D Listで返す
  
set wRes to getWeekDatesWithinAWNinAMonth(targY, targM, targWN) of me
  
–> {21, 22, 23, 24, 25, 26, 27}
  
  
–1D List(当該週の日付)内における指定日の登場インデックスを算出
  
set cRes to offsetOf(wRes, targD) of me
  
  
–1D List(当該週の日付)内の末尾までの日付を返す
  
set restDays to items cRes thru -1 of wRes
  
–> {28, 29, 30, missing value, missing value, missing value, missing value}
  
  
–指定日が所属する週のうち、最終日の日付を求める
  
set revList to reverse of restDays –逆順で末尾から数字が入っている場所をシーケンシャルサーチ
  
repeat with i in revList
    set j to contents of i
    
if j is not equal to missing value then
      exit repeat
    end if
  end repeat
  
  
set targDate to getDateInternational(targY, targM, j, 23, 59, 59, "JST") of me
  
  
return targDate
end calcLastDayFromTheTargetDay

–日曜日はじまりのカレンダーにおいて、指定年月内の指定週目の日付を配列で返す
on getWeekDatesWithinAWNinAMonth(targY, targM, targWN)
  set mRen to getMlenInternational(targY, targM) of me
  
  
set aList to make2DBlankArray(7, 5) of me –1か月分の2次元配列を求める
  
  
set aCount to 1
  
repeat with i from 1 to mRen
    set tmpDO to getDateInternational(targY, targM, i, 23, 59, 59, "JST") of me –当初、00:00:00に指定していたが、週末(土曜日)を指定したときに、範囲が00:00;00〜00:00:00になると具合がよくないので00:00:00〜23:59:59になるようこの時間に変更し
    
set aWD to weekday of tmpDO as number
    
    
set aList to setItemByXY(aWD, aCount, aList, i) of me
    
    
if aWD = 7 then
      set aCount to aCount + 1
    end if
  end repeat
  
  
return item targWN of aList
end getWeekDatesWithinAWNinAMonth

–日曜日はじまりのカレンダーにおいて、指定年月日の日付が月内の何週目にあたるかを算出
on getDatesWeekNumberWithinAMonth(targY, targM, targD)
  set mRen to getMlenInternational(targY, targM) of me
  
  
set aList to make2DBlankArray(7, 5) of me
  
  
set aCount to 1
  
repeat with i from 1 to mRen
    set tmpDO to getDateInternational(targY, targM, i, 23, 59, 59, "JST") of me –当初、00:00:00に指定していたが、週末(土曜日)を指定したときに、範囲が00:00;00〜00:00:00になると具合がよくないので00:00:00〜23:59:59になるようこの時間に変更した
    
set aWD to weekday of tmpDO as number
    
    
set aList to setItemByXY(aWD, aCount, aList, i) of me
    
    
if i = targD then return aCount
    
    
if aWD = 7 then
      set aCount to aCount + 1
    end if
    
  end repeat
  
  
return false
end getDatesWeekNumberWithinAMonth

–現在のカレンダーで指定年月の日数を返す
on getMlenInternational(aYear, aMonth)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar() — do *not* use initWithCalendarIdentifier:
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:1 hour:0 minute:0 |second|:0 nanosecond:0
  
set theResult to theNSCalendar’s rangeOfUnit:(current application’s NSDayCalendarUnit) inUnit:(current application’s NSMonthCalendarUnit) forDate:theDate
  
–>  {location:1, length:31}
  
return |length| of theResult
end getMlenInternational

–Make a GMT Date Object with parameters from a given time zone.
on getDateInternational(aYear, aMonth, aDay, anHour, aMinute, aSecond, timeZoneAbbreviation)
  set theNSCalendar to current application’s NSCalendar’s currentCalendar()
  
theNSCalendar’s setTimeZone:(current application’s NSTimeZone’s timeZoneWithAbbreviation:(timeZoneAbbreviation))
  
set theDate to theNSCalendar’s dateWithEra:1 |year|:aYear |month|:aMonth |day|:aDay hour:anHour minute:aMinute |second|:aSecond nanosecond:0
  
return theDate as date
end getDateInternational

–Listに配列的な添字を使ってアクセスするルーチン群
on xFill(aX, aY, aList, aVal, aRepNum)
  repeat with x from aX to (aX + aRepNum – 1)
    set aList to setItemByXY(x, aY, aList, aVal) of me
  end repeat
  
return aList
end xFill

on yFill(aX, aY, aList, aVal, aRepNum)
  repeat with y from aY to (aY + aRepNum – 1)
    set aList to setItemByXY(aX, y, aList, aVal) of me
  end repeat
  
return aList
end yFill

on getItemByXY(aX, aY, aList, aBlankItem)
  try
    set aContents to contents of (item aX of item aY of aList)
  on error
    set aContents to aBlankItem
  end try
  
return aContents
end getItemByXY

on setItemByXY(aX, aY, aList, aContents)
  try
    set (item aX of item aY of aList) to aContents
  end try
  
return aList
end setItemByXY

–空白の2D Array を出力する
on make2DBlankArray(curLen, curMax)
  set outArray to {}
  
repeat curMax times
    set aList to {}
    
repeat curLen times
      set the end of aList to missing value –オリジナルの""(ヌル文字)からmissing valueに変更
    end repeat
    
set the end of outArray to aList
  end repeat
  
return outArray
end make2DBlankArray

–1D Listのoffsetを求める
on offsetOf(aList as list, aTarg)
  set aArray to current application’s NSArray’s arrayWithArray:aList
  
set aIndex to aArray’s indexOfObjectIdenticalTo:aTarg
  
return (aIndex + 1)
end offsetOf

★Click Here to Open This Script 

Posted in Calendar | Tagged 15.0savvy 26.0savvy | Leave a comment

テキストベースの棒グラフ取得

Posted on 9月 27, 2025 by Takaaki Naganoya

テキストで作成した棒グラフの生成AppleScriptです。

比較的どうでもいいデータの傾向を知りたい場合に棒グラフ表示させて全体の傾向を把握することが、よくあります。そんなときに、文字だけで作成した棒グラフを生成します。何回か書いたことがあるような気もするのですが、簡単なものを作っておきます。

本Scriptで生成するグラフのタイプは2種類。


▲モノクロ表示用の、同じ文字を重ねて棒グラフを表示するタイプ


▲カラー(色分け)表示用の、絵文字を用いて色分けして棒グラフを表示するタイプ

項目名の最大文字数を求めて、他の項目で文字数が足りない箇所には不足分のスペースを足して、グラフの位置揃えを行なっていますが、このあたりタブで処理するべきなのか。

実際には、Pages書類上で選択しておいたオブジェクトから画像(image)のみピックアップして、それらの幅を集計してグラフ表示するような用途に使っています。

AppleScript名:テキストベースの棒グラフ取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/09/27
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use radioLib : script "displayTextView"

set bList to {12, 21, 15, 26}
set labelList to {"Aaaa", "Bbb", "Cc", "D"}

set aStr to retCharBarChart(labelList, bList, "|") of me
set aStr to "Name" & tab & tab & "Data" & tab & tab & "Number" & return & return & aStr
display text view aStr main message "Bar chart Sample 1" sub message "Sub Message" with properties {font name:"Courier", size:32, width:800, height:300, color:{0, 255, 128}}
(*
"Name    Data    Number

Aaaa    |||||||||||| 12
Bbb     ||||||||||||||||||||| 21
Cc     ||||||||||||||| 15
D     |||||||||||||||||||||||||| 26
"
*)

set bStr to retCharBarChart(labelList, bList, false) of me
(*
"Aaaa    
🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 12
Bbb     
🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦🟦 21
Cc     
🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 15
D     
🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 26
"
*)

set bStr to "Name" & tab & tab & "Data" & tab & tab & "Number" & return & return & bStr
display text view bStr main message "Bar chart Sample 2" sub message "Sub Message" with properties {font name:"Courier", size:16, width:800, height:150, color:{0, 255, 128}}

–文字ベースで棒グラフを作成するための文字グラフルーチン
on retCharBarChart(labelList, bList, charFlag)
  –ラベル文字の長さをそろえる(文字種類については不問、単に文字数をそろえるだけ)
  
set tMax to maximumItemLengthFromList(labelList) of me
  
  
set resText to ""
  
–set aChar to "■"
  
set aCharList to {"🟥", "🟦", "🟨", "🟩", "🟧", "🟪", "🟫", "⬛️", "❤️", "💙", "💛", "💚", "🧡", "💜", "🤎", "🩶", "🖤", "🔴", "🔵", "🟡", "🟢", "🟠", "🟣", "🟤", "⚫️"}
  
set aCharLLen to length of aCharList
  
  
set iCounter to 1
  
set aCharPointer to 1
  
  
if charFlag = false then
    –ローテーション方式の色文字グラフ
    
repeat with i in bList
      set j to contents of i
      
set aLabel to (contents of item iCounter of labelList) as string
      
set aLabel to paddingSpacesAfter(aLabel, tMax, " ") of me
      
      
set tmpChar to contents of item aCharPointer of aCharList
      
set resText to (resText & aLabel & tab & tab & makeCharRep(j, tmpChar) of me & " " & j as string) & return
      
      
set iCounter to iCounter + 1
      
set aCharPointer to aCharPointer + 1
      
if aCharPointer > aCharLLen then
        set aCharPointer to 1
      end if
    end repeat
  else
    –指定文字で作成する文字グラフ
    
repeat with i in bList
      set j to contents of i
      
      
set aLabel to (contents of item iCounter of labelList) as string
      
set aLabel to paddingSpacesAfter(aLabel, tMax, " ") of me
      
      
set resText to (resText & aLabel & tab & tab & makeCharRep(j, charFlag) of me & " " & j as string) & return
      
      
set iCounter to iCounter + 1
      
set aCharPointer to aCharPointer + 1
      
if aCharPointer > aCharLLen then
        set aCharPointer to 1
      end if
    end repeat
  end if
  
  
return resText
end retCharBarChart

–指定文字を指定回数連結したテキストを返す
on makeCharRep(aNum, aChar)
  set aText to ""
  
repeat aNum times
    set aText to aText & aChar
  end repeat
  
return aText
end makeCharRep

–指定文字列に、指定の長さまで空白文字列を追加
on paddingSpacesAfter(aDat as string, targLen as integer, padStr as {boolean, string})
  set dLen to length of aDat
  
if dLen > targLen then error "Logical error: Target length is shorter than data length"
  
copy aDat to aStr
  
  
set padStr to makeCharRep(targLen – dLen, padStr) of me
  
return aStr & padStr
end paddingSpacesAfter

–1D Listで最長の文字列の文字数を返す
on maximumItemLengthFromList(nList as list)
  script o
    property nl : nList
    
property itemNo : 1
  end script
  
  
set max to length of (item 1 of o’s nl)
  
  
repeat with i from 2 to (count nList)
    set n to length of (item i of o’s nl)
    
if n > max then
      set max to n
      
set o’s itemNo to i
    end if
  end repeat
  
return length of (contents of (item (o’s itemNo) of nList))
end maximumItemLengthFromList

★Click Here to Open This Script 

Posted in dialog | Tagged 13.0savvy 14.0savvy 15.0savvy 26.0savvy | Leave a comment

MacBook Air/MacBook Proのディスプレイ開閉角度を取得

Posted on 9月 23, 2025 by Takaaki Naganoya

samhenrigold氏によるLid Angle SensorをCocoa Framework化して呼び出して、MacBook Air/MacBook Proのディスプレイ開閉角度を取得するAppleScriptです。画面角度センサーを持たないMac miniなどで実行した場合には値を返さず処理終了します。


▲左:119度 右:90度 センサーが返してくる値と実際の画面角度の値には若干の「差」がある。0度の開始値が若干画面が開いた状態になっている?

本記事に掲載しているAppleScriptのプログラムリストだけでは実行できないため、下記のリンクからFrameworkバイナリ入りのScript Bundleをダウンロードして実行してください。

Apple Silicon Macでは実行確認していますが、Intel Macで動くかどうかは未検証です。一応、githubのsamhenrigoldの説明によると2019年のMacBook Pro以降で導入されたセンサーとのことで、Intel Macでも当該モデルであれば動作することが期待されます。

ただし、M1 MacBook Air/Proで問題が発生することが報告されているようです(自分は実機で確認していません。センサー値の取得だけなら問題が起きないのか、センサー値の取得で問題が起きているのか)。

同プログラムをCocoa Framework化して呼び出しているため(Universal Binary)、Script Debuggerおよび同環境で書き出したEnhanced Appletなどで実行可能です。

→ Download Script Bundle with Framework

→ Download angleSensorKit Xcode Project

AppleScript名:画面開閉角度センサーの値を取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/09/07
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use framework "angleSensorKit" — Created by Sam on 2025-09-06. https://github.com/samhenrigold/LidAngleSensor
use scripting additions

set anASensor to current application’s LidAngleSensor’s alloc()’s init()

set aRes to anASensor’s isAvailable() as boolean
if aRes = false then return —Maybe not MacBook Air/Pro
set sensorVal to anASensor’s lidAngle()

return sensorVal

★Click Here to Open This Script 

Posted in System | Tagged 15.0savvy 26.0savvy | Leave a comment

スクリプトメニューでAirDropが使えない?

Posted on 9月 23, 2025 by Takaaki Naganoya

macOS 15/26にて、AirDropをAppleScriptから利用するAirSharing Libを、スクリプトメニューに入れたAppleScriptから呼び出せないという現象に直面しています。

スクリプトエディタ上で動かすと動作するのですが、スクリプトメニューから動かすとまったく動作しません。一般ユーザー向けのAppleScript実行環境であるスクリプトメニューで動作しないのはかなり問題です。

AirSharing LibはもともとSal Soghoianが書いたプログラムにWiFi制御系のコードを追加して書いたものですが、現状のmacOS 15上で動作する最低限の記述だとこんな感じ(↓)です。

スクリプトメニュー上で動作するために、何かの「おまじない」が必要なのか、そもそもスクリプトメニューの仕様上それが禁止されるようになったのか? なかなか不思議なところです。

動作確認できたAppleScript実行環境:
スクリプトエディタ、Script Debugger、Switch Control、Automator、Claris FileMaker Pro 2025、FastScript 3、Xojo 2025

AppleScript名:AirDropTest.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/09/23
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.8"
use framework "Foundation"
use framework "AppKit"
use scripting additions

property airDropService : missing value

set theFile to POSIX path of (choose file with prompt "AirDropで共有するファイルを選んでください:")

my performSelectorOnMainThread:"shareWithAir:" withObject:(theFile) waitUntilDone:true
–my shareWithAir:theFile–for debug

on shareWithAir:thePOSIXFile
  set theFile to thePOSIXFile as string
  
set fileURL to current application’s NSURL’s fileURLWithPath:(theFile)
  
  
— AirDrop用の共有サービスを取得
  
set airDropService to current application’s NSSharingService’s sharingServiceNamed:(current application’s NSSharingServiceNameSendViaAirDrop)
  
  
if airDropService ≠ missing value then
    airDropService’s performSelectorOnMainThread:"performWithItems:" withObject:{fileURL} waitUntilDone:true
  else
    display dialog "AirDrop共有サービスが利用できません。" buttons {"OK"} default button 1
  end if
end shareWithAir:

★Click Here to Open This Script 

Posted in AirDrop Bug Library | Tagged 15.0savvy 26.0savvy | Leave a comment

各種GUIアプリ書類のオープン速度を向上するためにUIアニメーションの一時停止を

Posted on 9月 22, 2025 by Takaaki Naganoya

AppleScriptから各種GUIアプリを操作すると、各種GUIアプリ上の操作に要する時間がそのまま必要になります。端的なところでいえば、書類のオープン時のアニメーションも毎回行われるわけで、割と無意味なアニメーションに、そこそこの時間が消費されています。

そのUIアニメーションを無効化する処理について調査したところ、割と簡単に情報が見つかりました。

ここに掲載しているサンプルAppleScript(要・Metadata Lib)では、Finder上の最前面のウィンドウで選択中のフォルダ以下にあるPages書類をSpotlightの機能を用いてすべてピックアップし、それらを「UIアニメーションあり」「UIアニメーションなし」の条件でオープン/クローズだけ行うものです。

結論をいうと、M2 MacBook Air上で通常のUIアニメーションつきの処理では68秒かかっていたものが、UIアニメーションを無効化するだけで43秒で処理できました(125個のPages書類で実験)。

これはちょうど、M1 MacからM4 Macに買い替えるぐらいの処理速度の向上が、ただUIアニメーションを無効にするだけで実現できたということを意味します。

1書類あたり0.2秒の処理速度向上が見られたわけですが、これが400個の書類を処理するとなれば80秒も変わってくるわけで、割と洒落にならない速度向上が実現できます。もっと早く調べておけばよかったと思うことしきりです。

AppleScript名:UI アニメーションの無効化による速度向上実験.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/09/22
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions
use mdLib : script "Metadata Lib"

tell application "Finder"
  set aSel to (selection as alias list)
  
if aSel = {} then
    set origPath to choose folder
  else
    set origPath to (first item of aSel)
  end if
end tell

–SpotlightでPages書類を検出
set aResList to perform search in folders {origPath} predicate string "kMDItemContentType == %@ || kMDItemContentType == %@" search arguments {"com.apple.iwork.pages.sffpages", "com.apple.iwork.pages.pages"}
–return aResList

–UI Animationを許可
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
openClosePagesDocs(aResList, true) of me
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat – a1Dat

–UI Animationを禁止
set a2Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
openClosePagesDocs(aResList, false) of me
set b2Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c2Dat to b2Dat – a2Dat

return {c1Dat, c2Dat, length of aResList}

on openClosePagesDocs(aResList, animationF)
  if animationF = true then
    –UI Animationをオンにする
    
try
      do shell script "defaults delete NSGlobalDomain NSAutomaticWindowAnimationsEnabled"
    end try
  else –UI Animationをオフにする
    do shell script "defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool NO"
  end if
  
  
–Pages書類を順次オープンしてタイトル(?)を取得
  
set pagesTitleList to {}
  
repeat with i in aResList
    set aFile to POSIX file i
    
    
tell application "Pages"
      open (aFile as alias)
    end tell
    
    
tell application "Pages"
      close front document saving no
    end tell
    
  end repeat
  
  
if animationF = false then
    –UI Animationをオンにする
    
try
      do shell script "defaults delete NSGlobalDomain NSAutomaticWindowAnimationsEnabled"
    end try
  end if
end openClosePagesDocs

★Click Here to Open This Script 

Posted in shell script Spotlight | Tagged 13.0savvy 14.0savvy 15.0savvy Pages | 1 Comment

指定フォルダ内から指定拡張子のファイルを取得し、エイリアスだったらオリジナルのパスをPOSIX pathで取得

Posted on 9月 20, 2025 by Takaaki Naganoya

指定フォルダから、指定拡張子のファイルを取得し、取得したファイルがエイリアス書類だったらオリジナルのパスを返すAppleScriptです。

AppleScriptを指定スケジュールで実行するAppleScriptを作成しており、スケジュールフォルダ以下に「毎月15日 15時」といった名前のフォルダを作っておき、その中に入れたAppleScriptを指定スケジュールで実行するようにしてありました。

ただ、フォルダ内に入れるのが「AppleScript書類そのもの」だと、運用上いろいろまずい(同じScriptの複製が大量にできてしまうとか)ので、エイリアスを入れても反応するように、本Scriptを書いてみた次第です。

割とちゃんと動いているように見えます。

2025/9/29修正:
処理対象にエイリアス書類「ではなく」本物のファイルが指定されていた(=エイリアス書類ではなかった)場合にエラーになっていました。作成・使用目的は「エイリアス書類の実体の追跡」ではあったものの、実体のファイルに行き当たった場合にエラーになるのは意図しない動作です。

このため、掲載リストを一部修正しました。

AppleScript名:指定フォルダ内から指定拡張子のファイルを取得し、エイリアスだったらオリジナルのパスをPOSIX pathで取得.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/09/19
–  Modified on: 2025/09/29
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.8"
use framework "Foundation"
use scripting additions

set aRoot to choose folder

set scptList to getFilePathListWithResolveAlias(aRoot, "scpt") of me
–> {"/Users/me/Documents/sample2.scpt", "/Users/me/Documents/sample1.scpt"}

on getFilePathListWithResolveAlias(aFol, aExt)
  set aURL to current application’s |NSURL|’s fileURLWithPath:(POSIX path of aFol)
  
# 指定フォルダの直下のファイルを取得
  
set filePaths to current application’s NSFileManager’s defaultManager’s ¬
    contentsOfDirectoryAtURL:aURL ¬
      includingPropertiesForKeys:{current application’s NSURLNameKey} ¬
      
options:(current application’s NSDirectoryEnumerationSkipsHiddenFiles) ¬
      
|error|:(missing value)
  set f0List to filePaths as list
  
if f0List = {} then return {}
  
  
set f2List to {}
  
repeat with i in f0List
    set tmpPath1 to POSIX path of i
    
set tmpPath2 to (my fullpathOfOriginalFileForAlias:tmpPath1)
    
set tmpPath to (current application’s NSString’s stringWithString:(tmpPath2))
    
set the end of f2List to tmpPath as string
  end repeat
  
  
return f2List
end getFilePathListWithResolveAlias

–エイリアスファイルのパスからオリジナルのファイルパス(POSIX Path)を返す
on fullpathOfOriginalFileForAlias:posixPath
  tell current application’s |NSURL|
    set anNSURL to its fileURLWithPath:posixPath
    
set theData to its bookmarkDataWithContentsOfURL:anNSURL |error|:(missing value)
    
set theResult to its resourceValuesForKeys:{current application’s NSURLPathKey} fromBookmarkData:theData
  end tell
  
  
if theResult = missing value then return posixPath
  
  
return (theResult’s objectForKey:(current application’s NSURLPathKey)) as text
end fullpathOfOriginalFileForAlias:

★Click Here to Open This Script 

Posted in File path | Tagged 13.0savvy 14.0savvy 15.0savvy 26.0savvy | Leave a comment

macOS 15.xで自作AppleScriptライブラリの一部がScript Menuから使えなくなったので修正

Posted on 9月 19, 2025 by Takaaki Naganoya

X(旧Twitter)上でPiyomaru SoftwareのAppleScriptライブラリについてフィードバックをいただきました。おおよそ改修が済んでいるため、ドキュメントを書き次第、最新版をBlogからダウンロードできるようにしておきます。

こうしたフィードバックを得るために公開しているので、動作しないとか、予想外の動作を行った場合にはフィードバックしていただきたいところです(すぐに対処できる保証はないのですが)。

確認された現象

Piyomaru Software制作のAppleScriptライブラリの一部で、macOS 15.xのスクリプトメニューに呼び出しScriptを入れた場合に実行できないものがありました。その一方で、スクリプトエディタ/Script Debugger上では実行できます。

・AirSharing Lib
・Choose location Lib
・Display Youtube Lib
・Pickup Color Lib

修正して動作するようになった事例

CheckboxLibについては、SDEF中のdocumentからのムービーおよび画像へのリンクを削除。

Enumをas stringでstringにcast処理していた箇所が、スクリプトメニュー上では動作しなくなっていた(→ stringへのcastをやめることで問題解消)。

choose color Libも同様に、Enumをas stringでstringにcast処理していた場所で、スクリプトメニュー上では動作しなくなっていたので修正。

動作条件そのものが変更になったライブラリ

・Display youtube Lib
macOS 15上ではInfo.plistにNSAppTransportSecurityのエントリを追加してアプレット書き出しが必要です。
ただし、macOS 26上ではスクリプトメニュー上で呼び出しScriptを実行しても、問題なくYouTubeムービーの再生が可能です。

問題を解消できていないライブラリ

目下、スクリプトメニューから呼び出すと動作しないライブラリは以下の通り。

・AirSharing Lib
・Choose location Lib

Posted in Library | Tagged 15.0savvy 26.0savvy | Leave a comment

シンプルな文字置換

Posted on 9月 13, 2025 by Takaaki Naganoya

AppleScriptで文字置換を行う場合には、いくつかの方法があります。

①text item delimitersを利用する

AppleScriptの登場以来、利用されてきた方法です。AppleScriptの処理系では最速といってよい方法ですが、複数の箇所を一気に置換するのでやや過剰なのと処理方法が独特なので敬遠する人もいるようです。

また、処理対象の文字列のサイズが数Mバイト以上になると、処理が終了しなかったりします。巨大な文字列の置換には⑤が推奨されます。CotEditorのようなScriptableなテキストエディタの機能を利用するのもアリでしょう。

AppleScript名:文字置換(最短).scpt
set origText to "abcdefg"
set targStr to "de"
set repStr to "xx"

set a to repChar(origText, targStr, repStr) of me
–> "abcxxfg"

–Written By Philip Aker
–文字置換ルーチン
on repChar(origText as string, targStr as string, repStr as string)
  set {txdl, AppleScript’s text item delimiters} to {AppleScript’s text item delimiters, targStr}
  
set temp to text items of origText
  
set AppleScript’s text item delimiters to repStr
  
set res to temp as text
  
set AppleScript’s text item delimiters to txdl
  
return res
end repChar

★Click Here to Open This Script 

②shellコマンドを利用する

do shell scriptコマンド経由で置換コマンドを利用する方法です。Mac OS X以降後に利用できるようになったものですが、日本語に対して正確に置換できるのか不安が残るので、個人的にはあまり積極的には利用していません。

③OSAXを利用する

macOS 10.15でサードパーティのOSAX(Scripting Additions)が廃止されたため、現在では利用できません。

④AppleScript Librariesを利用する

Shane StanleyのRegexAndStuffLibなどのライブラリを用いて正規表現を用いた文字置換を利用するものです。

⑤Cocoaの機能を利用する

Cocoaの機能を呼び出して文字置換を行うものです。いくつも利用できる機能があります。正規表現を使うものなど、お好きなものを利用してください。

AppleScript名:ASOCで文字置換5.scptd
— Created 2015-06-30 by Takaaki Naganoya
use AppleScript version "2.4"
use scripting additions
use framework "Foundation"

set a to "あいうえお++かきくけこ"
set b to cleanUpText(a, "+", "●")
–> "あいうえお●●かきくけこ"

on cleanUpText(someText, targStr, repStr)
  set theString to current application’s NSString’s stringWithString:someText
  
set targString to current application’s NSString’s stringWithString:targStr
  
set repString to current application’s NSString’s stringWithString:repStr
  
  
set theString to theString’s stringByReplacingOccurrencesOfString:targString withString:repString options:(current application’s NSRegularExpressionSearch) range:{location:0, |length|:length of someText}
  
return theString as text
end cleanUpText

★Click Here to Open This Script 

これらに加えて、もっと簡単な方法がないかと探してみたら、

⑥offset ofで文字列検索して置換を行う

offset ofで置換対象の文字列を検索して置換を行います。オリジナル文字列に対して置換対象の文字列の位置が「冒頭ではない」「末尾ではない」という条件がそろっていて、かつ置換対象が0ないし1回しか対象文字列中に存在しないことが事前に分かりきっている場合にのみ使えます。

set urlData to "https://www.amazon.co.jp/gp/css/summary/print.html/ref=oh_aui_ajax_invoice"

set anOffset to offset of "/gp/" in urlData
set newURL to (text 1 thru (anOffset + (length of "/gp/") – 1) of urlData) & "legacy" & (text (anOffset + (length of "/gp/") – 1) thru -1 of urlData)

–> "https://www.amazon.co.jp/gp/legacy/css/summary/print.html/ref=oh_aui_ajax_invoice"

★Click Here to Open This Script 

このぐらいで使えますが、本当にすべてのパターンを利用するには、場合分けしつつ書く必要があります。シンプルなはずなのに、シンプルになっていないという。

AppleScript名:単純文字置換.scpt
set urlData1 to "https://www.amazon.co.jp/gp/css/summary/print.html/ref=oh_aui_ajax_invoice"
set newURL1 to replaceOne(urlData1, "/gp/", "/gp/legacy/") of me
–> "https://www.amazon.co.jp/gp/legacy/css/summary/print.html/ref=oh_aui_ajax_invoice"

set urlData2 to "https://www.amazon.co.jp/css/summary/print.html/ref=oh_aui_ajax_invoice/gp/"
set newURL2 to replaceOne(urlData2, "/gp/", "/gp/legacy/") of me
–> "https://www.amazon.co.jp/css/summary/print.html/ref=oh_aui_ajax_invoice/gp/legacy/"

set urlData3 to "https://www.amazon.co.jp/css/summary/print.html/ref=oh_aui_ajax_invoice/gp/"
set newURL3 to replaceOne(urlData3, "https://", "http://") of me
–> "http://www.amazon.co.jp/css/summary/print.html/ref=oh_aui_ajax_invoice/gp/"

on replaceOne(origStr as string, targStr as string, repStr as string)
  set anOffset to offset of targStr in origStr
  
if anOffset = 0 then return origStr
  
set {aLen, bLen} to {length of origStr, length of targStr}
  
  
if anOffset + bLen > aLen then
    –置換対象文字が末尾にある場合
    
set newStr1 to (text 1 thru (anOffset – 1) of origStr)
    
set newStr2 to ""
  else if anOffset = 1 then
    –置換対象文字が先頭にある場合
    
set newStr2 to (text (anOffset + ((length of targStr))) thru -1 of origStr)
    
set newStr1 to ""
  else
    –通常パターン
    
set newStr1 to (text 1 thru (anOffset – 1) of origStr)
    
set newStr2 to (text (anOffset + ((length of targStr))) thru -1 of origStr)
  end if
  
return newStr1 & repStr & newStr2
end replaceOne

★Click Here to Open This Script 

Posted in Text | Tagged 13.0savvy 14.0savvy 15.0savvy 26.0savvy | Leave a comment

暗黙のuseコマンド

Posted on 9月 2, 2025 by Takaaki Naganoya

かつてAppleScript関連で大きな議論を呼んだ話題に「暗黙のrunハンドラ」というものがありました。

何も書かないAppleScriptのプログラムに暗黙のrunハンドラが存在している(ように動く)という話です。

現在でも同様に、

「明確に仕様として明記されていないものの、おそらくこういう動作になっているだろう」

と推測して書いているものがあります。それが、暗黙のuseコマンドです。

useコマンドはデフォルトで指定されている

何もuseコマンドを書かなくても、仮にuseコマンドを記述しても、パラメータを指定しない場合にはデフォルト値が

use AppleScript
無指定時には、現在実行中のバージョンのAppleScriptで動作するようになっているようです。

use scripting additions
macOS 10.15以降はサードパーティのScripting Additionのサポートが廃止されたため、Apple純正のStandard Additionsのオン/オフ設定コマンドという位置付けになっているようです。

use framework
この記述では、とくに無指定時のデフォルト値というものはないようです。

ただし、Vanilla ScriptとCocoa Scriptで動作が異なっています。また、Script Object内に記述したAppleScriptでも動作が変わります。

AppleScript名:scripting additions 1.scpt
display dialog "TEST"

★Click Here to Open This Script 

AppleScript名:scripting additions in script object.scpt

testScript’s testMes("TEST")

script testScript
  on testMes(aMes as string)
    display dialog aMes
  end testMes
end script

★Click Here to Open This Script 

AppleScript名:scripting additions 2.scpt
use AppleScript
use framework "Foundation"

–display dialog "TEST"–構文チェック自体できない

★Click Here to Open This Script 

AppleScript名:scripting additions in script object.scpt
use AppleScript
use framework "Foundation"

testScript’s testMes("TEST")

script testScript
  use AppleScript
  
use framework "Foundation"
  
use scripting additions –ないと、構文確認をパスできない
  
property parent : AppleScript –なくても動く。Scriptオブジェクトの階層を増やすと必須になったりする
  
  
on testMes(aMes as string)
    display dialog aMes
  end testMes
  
  
on testMes2(aMes as string)
    using terms from scripting additions —どの用語辞書の予約語かを明示的に指定するよう構文確認時に求められるケースがある
      display dialog aMes
    end using terms from
  end testMes2
end script

★Click Here to Open This Script 

Posted in Scripting Additions | Tagged 13.0savvy 14.0savvy 15.0savvy | 1 Comment

開始時刻から終了時刻までhh:mm形式の文字列を15分単位でリスト出力

Posted on 8月 13, 2025 by Takaaki Naganoya

指定の開始時刻から終了時刻まで、hh:mm形式の文字列を15分間隔でリスト出力するAppleScriptです。

ありもののサブルーチンを組み合わせて即席で作ったCocoa版のルーチンと、ChatGPT(GPT-5)に書かせた非Cocoa版のルーチンの2つがあり(ChatGPTにCocoa使用を指定せず)、速度を比較してみたら……

非Cocoa版のルーチンのほうが6倍ぐらい高速でした。データが小さすぎるとか、処理内容が小さすぎる場合にはCocoaの機能を利用しないほうが高速に処理できる場合があります。

Shane Stanleyの処理時間計測アプリ「ScriptGeek」が、かなりCocoa Scriptingの登場初期から存在しており、不思議に思ってきたものですが……処理内容の規模によってはCocoa Scriptingのほうが遅くなるケースがあるため、実際に書いて動かして計測してみないと、Cocoa Scriptingで高速化が達成できるかわからないところです。

このほか、AppleScriptのランタイム環境が何になるかによって実行速度は変わってきます。Xcode上で開発するCocoa Applicationなども実行特性が違うため、やはり動かして計測してみないと何がベストなのかはわかりません。

ただ、この程度の処理であれば、0.0028秒が0.4秒になったところで、何か実用上の差異が発生するわけでもありません。

追記:
追加で最速パターンを掲載しておきます。汎用性を求めて計算して出力していましたが、ほとんどの用途で毎回同じようなデータを出力することでしょう。なので、固定データを返すだけでいいだろうかと。高速版とくらべても65〜70倍ぐらい高速です(当たり前)。

AppleScript名:15分メッシュの時間文字列を生成.scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/08/13
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set aRes to generateWorkUnitList() of me
–> {"5:15", "5:30", "5:45", "6:00", "6:15", "6:30", "6:45", "7:00", "7:15", "7:30", "7:45", "8:00", "8:15", "8:30", "8:45", "9:00", "9:15", "9:30", "9:45", "10:00", "10:15", "10:30", "10:45", "11:00", "11:15", "11:30", "11:45", "12:00", "12:15", "12:30", "12:45", "13:00", "13:15", "13:30", "13:45", "14:00", "14:15", "14:30", "14:45", "15:00", "15:15", "15:30", "15:45", "16:00", "16:15", "16:30", "16:45", "17:00", "17:15", "17:30", "17:45", "18:00", "18:15", "18:30", "18:45", "19:00", "19:15", "19:30", "19:45", "20:00", "20:15", "20:30", "20:45"}

on generateWorkUnitList()
  set hList to {}
  
  
repeat with h from 5 to 20 by 1
    repeat with m from 0 to 59 by 15
      if {h, m} is not equal to {5, 0} then
        set mStr to numToZeroPaddingStr(m, 2, "0")
        
set the end of hList to (h as string) & ":" & mStr
      end if
    end repeat
  end repeat
  
  
return hList
end generateWorkUnitList

–整数の値に指定桁数ゼロパディングして文字列で返す
on numToZeroPaddingStr(aNum as integer, aDigit as integer, paddingChar as text)
  set aNumForm to current application’s NSNumberFormatter’s alloc()’s init()
  
aNumForm’s setPaddingPosition:(current application’s NSNumberFormatterPadBeforePrefix)
  
aNumForm’s setPaddingCharacter:paddingChar
  
aNumForm’s setMinimumIntegerDigits:aDigit
  
  
set bNum to current application’s NSNumber’s numberWithInt:aNum
  
set aStr to aNumForm’s stringFromNumber:bNum
  
  
return aStr as text
end numToZeroPaddingStr

★Click Here to Open This Script 

AppleScript名:15分メッシュの時間文字列を生成(高速).scpt
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/08/13
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4" — Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

set mList to retWorkingMeshStrList() of me
–> {"5:15", "5:30", "5:45", "6:00", "6:15", "6:30", "6:45", "7:00", "7:15", "7:30", "7:45", "8:00", "8:15", "8:30", "8:45", "9:00", "9:15", "9:30", "9:45", "10:00", "10:15", "10:30", "10:45", "11:00", "11:15", "11:30", "11:45", "12:00", "12:15", "12:30", "12:45", "13:00", "13:15", "13:30", "13:45", "14:00", "14:15", "14:30", "14:45", "15:00", "15:15", "15:30", "15:45", "16:00", "16:15", "16:30", "16:45", "17:00", "17:15", "17:30", "17:45", "18:00", "18:15", "18:30", "18:45", "19:00", "19:15", "19:30", "19:45", "20:00"}

on retWorkingMeshStrList()
  set startSeconds to (5 * 3600) + (15 * 60) — 5:15 の相対秒
  
set endSeconds to (20 * 3600) — 20:00 の相対秒
  
set intervalSeconds to 15 * 60 — 15分間隔
  
  
set timeList to {}
  
  
repeat with t from startSeconds to endSeconds by intervalSeconds
    set h to t div 3600
    
set m to (t mod 3600) div 60
    
— 2桁表示に整形
    
–set hStr to text -2 thru -1 of ("0" & h)
    
set hStr to h as string
    
set mStr to text -2 thru -1 of ("0" & m)
    
set end of timeList to (hStr & ":" & mStr)
  end repeat
  
  
return timeList
end retWorkingMeshStrList

★Click Here to Open This Script 

AppleScript名:15分メッシュの時間文字列を生成(最速).scptd
—
–  Created by: Takaaki Naganoya
–  Created on: 2025/08/10
—
–  Copyright © 2025 Piyomaru Software, All Rights Reserved
—

use AppleScript version "2.4"
use framework "Foundation"
use scripting additions

set hList to generateWorkUnitList() of me

on generateWorkUnitList()
  return {"5:15", "5:30", "5:45", "6:00", "6:15", "6:30", "6:45", "7:00", "7:15", "7:30", "7:45", "8:00", "8:15", "8:30", "8:45", "9:00", "9:15", "9:30", "9:45", "10:00", "10:15", "10:30", "10:45", "11:00", "11:15", "11:30", "11:45", "12:00", "12:15", "12:30", "12:45", "13:00", "13:15", "13:30", "13:45", "14:00", "14:15", "14:30", "14:45", "15:00", "15:15", "15:30", "15:45", "16:00", "16:15", "16:30", "16:45", "17:00", "17:15", "17:30", "17:45", "18:00", "18:15", "18:30", "18:45", "19:00", "19:15", "19:30", "19:45", "20:00"}
end generateWorkUnitList

★Click Here to Open This Script 

Posted in date list Text | Tagged 14.0savvy 15.0savvy 26.0savvy ChatGPT | 1 Comment

Post navigation

  • Older posts

電子書籍(PDF)をオンラインストアで販売中!

Google Search

Popular posts

  • macOS 26, Tahoe
  • Script Debuggerの開発と販売が2025年に終了
  • 【続報】macOS 15.5で特定ファイル名パターンのfileをaliasにcastすると100%クラッシュするバグ
  • NSObjectのクラス名を取得 v2.1
  • 2024年に書いた価値あるAppleScript
  • Xcode上のAppleScriptObjCのプログラムから、Xcodeのログ欄へのメッセージ出力を実行
  • AppleScript Dropletのバグっぽい動作が「復活」(macOS 15.5β)
  • macOS 26, 15.5でShortcuts.app「AppleScriptを実行」アクションのバグが修正される
  • Script Debuggerがフリーダウンロードで提供されることに
  • 指定フォルダ以下の画像のMD5チェックサムを求めて、重複しているものをピックアップ
  • Dock Menu
  • 執筆中:AppleScript最新リファレンスver2.8対応(macOS 15対応アップデート)
  • macOS 15.5beta5(24F74)でaliasのキャスティングバグが修正された???
  • Claris FileMaker Pro 2025(v22)がリリースされた
  • 複数の重複検出ルーチンを順次速度計測
  • Cocoa-AppleScript AppletがRosettaオンで動いた!
  • シンプルな文字置換
  • Numbersで選択範囲のdateの年を+1する
  • Excel 指定範囲のセルの上に画像を配置
  • Applicationのactivateを記録する v2

Tags

10.11savvy (1101) 10.12savvy (1242) 10.13savvy (1391) 10.14savvy (587) 10.15savvy (438) 11.0savvy (283) 12.0savvy (212) 13.0savvy (204) 14.0savvy (159) 15.0savvy (162) 26.0savvy (29) CotEditor (67) Finder (53) Keynote (120) NSAlert (61) NSArray (51) NSBitmapImageRep (20) NSBundle (20) NSButton (34) NSColor (53) NSDictionary (28) NSFileManager (23) NSFont (21) NSImage (41) NSJSONSerialization (21) NSMutableArray (63) NSMutableDictionary (22) NSPredicate (36) NSRunningApplication (56) NSScreen (30) NSScrollView (22) NSString (119) NSURL (98) NSURLRequest (23) NSUTF8StringEncoding (30) NSView (33) NSWorkspace (20) Numbers (76) Pages (56) Safari (44) Script Editor (27) WKUserContentController (21) WKUserScript (20) WKWebView (23) WKWebViewConfiguration (22)

カテゴリー

  • 2D Bin Packing
  • 3D
  • AirDrop
  • AirPlay
  • Animation
  • AppleScript Application on Xcode
  • Beginner
  • Benchmark
  • beta
  • Bluetooth
  • Books
  • boolean
  • bounds
  • Bug
  • Calendar
  • call by reference
  • check sum
  • Clipboard
  • Cocoa-AppleScript Applet
  • Code Sign
  • Color
  • Custom Class
  • date
  • dialog
  • diff
  • drive
  • Droplet
  • exif
  • file
  • File path
  • filter
  • folder
  • Font
  • Font
  • GAME
  • geolocation
  • GUI
  • GUI Scripting
  • Hex
  • History
  • How To
  • iCloud
  • Icon
  • Image
  • Input Method
  • Internet
  • iOS App
  • JavaScript
  • JSON
  • JXA
  • Keychain
  • Keychain
  • Language
  • Library
  • list
  • Locale
  • Localize
  • Machine Learning
  • Map
  • Markdown
  • Menu
  • Metadata
  • MIDI
  • MIME
  • Natural Language Processing
  • Network
  • news
  • Newt On Project
  • Noification
  • Notarization
  • Number
  • Object control
  • OCR
  • OSA
  • parallel processing
  • PDF
  • Peripheral
  • process
  • PRODUCTS
  • QR Code
  • Raw AppleEvent Code
  • Record
  • rectangle
  • recursive call
  • regexp
  • Release
  • Remote Control
  • Require Control-Command-R to run
  • REST API
  • Review
  • RTF
  • Sandbox
  • Screen Saver
  • Script Libraries
  • Scripting Additions
  • sdef
  • search
  • Security
  • selection
  • shell script
  • Shortcuts Workflow
  • Sort
  • Sound
  • Spellchecker
  • Spotlight
  • SVG
  • System
  • Tag
  • Telephony
  • Text
  • Text to Speech
  • timezone
  • Tools
  • Update
  • URL
  • UTI
  • Web Contents Control
  • WiFi
  • XML
  • XML-RPC
  • イベント(Event)
  • 未分類

アーカイブ

  • 2025年12月
  • 2025年11月
  • 2025年10月
  • 2025年9月
  • 2025年8月
  • 2025年7月
  • 2025年6月
  • 2025年5月
  • 2025年4月
  • 2025年3月
  • 2025年2月
  • 2025年1月
  • 2024年12月
  • 2024年11月
  • 2024年10月
  • 2024年9月
  • 2024年8月
  • 2024年7月
  • 2024年6月
  • 2024年5月
  • 2024年4月
  • 2024年3月
  • 2024年2月
  • 2024年1月
  • 2023年12月
  • 2023年11月
  • 2023年10月
  • 2023年9月
  • 2023年8月
  • 2023年7月
  • 2023年6月
  • 2023年5月
  • 2023年4月
  • 2023年3月
  • 2023年2月
  • 2023年1月
  • 2022年12月
  • 2022年11月
  • 2022年10月
  • 2022年9月
  • 2022年8月
  • 2022年7月
  • 2022年6月
  • 2022年5月
  • 2022年4月
  • 2022年3月
  • 2022年2月
  • 2022年1月
  • 2021年12月
  • 2021年11月
  • 2021年10月
  • 2021年9月
  • 2021年8月
  • 2021年7月
  • 2021年6月
  • 2021年5月
  • 2021年4月
  • 2021年3月
  • 2021年2月
  • 2021年1月
  • 2020年12月
  • 2020年11月
  • 2020年10月
  • 2020年9月
  • 2020年8月
  • 2020年7月
  • 2020年6月
  • 2020年5月
  • 2020年4月
  • 2020年3月
  • 2020年2月
  • 2020年1月
  • 2019年12月
  • 2019年11月
  • 2019年10月
  • 2019年9月
  • 2019年8月
  • 2019年7月
  • 2019年6月
  • 2019年5月
  • 2019年4月
  • 2019年3月
  • 2019年2月
  • 2019年1月
  • 2018年12月
  • 2018年11月
  • 2018年10月
  • 2018年9月
  • 2018年8月
  • 2018年7月
  • 2018年6月
  • 2018年5月
  • 2018年4月
  • 2018年3月
  • 2018年2月

https://piyomarusoft.booth.pm/items/301502

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org

Forum Posts

  • 人気のトピック
  • 返信がないトピック

メタ情報

  • ログイン
  • 投稿フィード
  • コメントフィード
  • WordPress.org
Proudly powered by WordPress
Theme: Flint by Star Verte LLC