Archive for the 'Finder' Category

2017/10/26 オープン中のムービーのファイルにラベルを付ける

QuickTime Playerでオープン中のファイルにラベルを付けるAppleScriptです。

QuickTimeムービーを取捨選別しているような場合に、オープン中のムービーだけにラベルをつけることで、作業を効率化しようというものです。

QuickTime Playerだけではなく、他のアプリケーションでも同様の処理は有用であるため、試してみるといいんじゃないでしょうか。

qt1.png
▲QuickTime Playerでオープン中のムービー

qt2.png
▲Script実行前

qt3.png
▲Script実行後

AppleScript名:オープン中のムービーのファイルにラベルを付ける
– Created 2017-10-26 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4925
tell application “QuickTime Player”
  set aDocList to every document
end tell

repeat with i in aDocList
  
  
tell application “QuickTime Player”
    tell i
      set aProp to properties
      
set aFile to (file of aProp) as alias
    end tell
  end tell
  
  
tell application “Finder”
    set label index of aFile to 5
  end tell
  
end repeat

★Click Here to Open This Script 

2017/09/26 バンドルIDで指定したプロセスを強制終了

Bundle IDで指定したプロセスを強制終了するAppleScriptです。

実際に試してみたらshellのkill/killallコマンドなどと異なり、Finderを強制終了したら終了しっぱなしになりました。

FinderをkillしてFinderがわりのアプリケーションを起動しておく用途のために必要な機能です。

AppleScript名:バンドルIDで指定したプロセスを強制終了(NSRunningApplication)
– Created 2017-09-17 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4847

set pRes to forceQuitAProcessByBUndleID(“com.apple.finder”) of me

–指定プロセスの強制終了
on forceQuitAProcessByBUndleID(aBundleID)
  set appArray to current application’s NSRunningApplication’s runningApplicationsWithBundleIdentifier:aBundleID
  
if appArray’s |count|() > 0 then
    set appItem to appArray’s objectAtIndex:0
    
set aRes to (appItem’s terminate()) as boolean
    
return aRes
  else
    return false
  end if
end forceQuitAProcessByBUndleID

★Click Here to Open This Script 

2017/09/21 Finder上で指定文字列でSpotlight検索を行なった結果を表示する

FinderのWindowで指定文字列によるSpotlight検索結果を表示するAppleScriptです。

このScriptによるSpotlight検索結果は、あくまでSpotlight結果表示を行うだけのものです。本当にSpotlightによる検索結果を取得したい場合には、大量にサンプルがあるのでそちらを参考にしてください。

たったの1行ですが、Pure AppleScriptだけでは実現できない機能です。何かの実行結果により作成した文字列でSpotlight検索を行い、結果をFinder上で表示するような用途で使えそうです。

spotlight2.png
▲Finderの検索フィールドに検索文字列を入れたのと同じ状態を作ります

spotlight1.png
▲Finder上で指定文字列によるSpotlight検索結果が表示されます

AppleScript名:Finder上で指定文字列でSpotlight検索を行なった結果を表示する
– Created 2017-09-21by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4835

set aResCode to current application’s NSWorkspace’s sharedWorkspace()’s showSearchResultsForQueryString:“Framework”

★Click Here to Open This Script 

2017/09/15 64ビット化して処理能力が低下したCocoa Finderの代替ツールはSystem Events?

macOS 10.6〜10.7で64ビット化にともないCocoaで書き直されたFinderのパフォーマンス低下が(当時から)指摘されていましたが、日常的な用途ではあまり気にしていませんでした。

Cocoa Finderのはじめのうちは、OS起動直後に表示されるFinderのウィンドウ内のアイコン描画が間に合わないのか仮アイコンで表示され、徐々に正しいアイコンで表示されるような光景もよく目にしていました(さすがに最近はそういう光景は目にしなくなりましたけれども)。

そんな中、macOS 10.13beta+MacBook Air 2011で大量のファイルを処理したときに、Finder経由で処理するとトンでもなくスピードが(SSDなのに)遅いことに気づきました。SSDに合わせて再設計したAPFS+SSDの組み合わせなのに、とてつもなく遅い。速い速いというふれ込みだったのに、Finder経由だと驚くほど遅い。

最初は、AppleScriptとFinderの間のやりとりが遅いものだと思っていましたが、実はFinderの処理そのものが遅いことが判明。数百個のファイルを選択してFinder上で(メニューから「複製」コマンドを実行して)ファイルコピーさせたりすると、めまいがするほど低速です。

そして、Appleの「AppleScript Language Guide」で「list folder」コマンド(廃止予定)を調べていたら、同コマンドの代替としてSystem Events経由でファイル情報にアクセスするやり方が掲載されているのを見つけ、Finder経由のファイル処理がすでに推奨されていない状態なのではないか、と疑いを持つようになりました。

そこで、同じファイル処理を(macOS 10.13beta上で)System EventsとFinderに対して実行してみたところ、4,224ファイル存在しているフォルダから、全ファイル名を取得する処理は、

 System Events:1秒
 Finder:6秒

同フォルダに対して、ファイル名に「99」という文字を含むファイル名だけを抽出させてみた(フィルタ参照)ところ、

 System Events:14秒
 Finder:計測不能(timeout時間を3,600秒に設定して実行してみたものの、Finderがハングアップして処理が数十分返ってこない)

といったところ。速度、信頼性ともにFinderよりもSystem Eventsの方が高いという状況です。処理対象ファイル数が100以下ぐらいであればFinder経由でも気にならないのですが、それを超えると露骨にパフォーマンス低下が顕在化します。

ただし、最近はAppleScriptでもCocoaの機能(NSFileManager)を利用してファイルの処理を行なっていたので、System Events経由のファイル処理も「驚くほど速いわけでもない」という印象。指定フォルダ内のファイルの抽出もSpotlight経由で行なっていたので、それほど不自由は感じていませんでした。

サンプルを掲載する際にも、なるべくSystem EventsかNSFileManagerを経由してファイル処理を行うようにする考えです。

Finderのコントロールは、選択中のフォルダやファイルを取得するとか、処理結果のフォルダやファイルを新規Finderウィンドウで表示させるといった用途に限定することが望ましいのでしょう(あと、指定フォルダ内のファイル一覧をas alias listで取得するのも(フィルタ参照を併用しなければ)高速です)。

2017/09/06 ツイ4のページで新規連載マンガの画像を取得してPDF化(新規連載のPDF化)v3

Safariで表示中のWebマンガサイト「ツイ4」(更新情報をTwitterに投稿)のマンガを全エピソードダウンロードしてPDFにまとめるAppleScriptです。

実行にあたってはShane StanleyのAppleScript Libraries「BridgePlus」のインストールを必要とします(~/ibrary/Script Librariesフォルダに入れるだけ)。

実行開始時にはSafariでツイ4の特定のマンガのページをオープンしている必要があります。

tui4.png

Safariの最前面のウィンドウからURLやTitle、リンクされている画像の詳細情報を取得し、条件チェックなどを行なったのちに詳細なデータの抽出を行います。

次に、PDFの保存先を選択するダイアログを表示。このさい、デフォルトの保存先を「ピクチャ」フォルダ、ファイル名をマンガのタイトルに指定。

ページにリンクされていた画像(ツイではファイル名はシーケンシャル番号)から番号の情報だけを抽出して最大値、最小値を計算。この範囲で画像のダウンロード、PDFへの追記を行います。ただし、実運用してみたところ、Safariからすべての画像を取得できないようで(非同期表示しているようなので)、とりあえず1〜9999までの番号の画像を順次ダウンロードし、画像が存在しなければ処理を終了しています。

画像をダウンロードするたびにPDFに追記していますが、このあたりは途中でエラーが出て停止してもそれまでの処理内容が保存されることを意図してのことです。SSD搭載機では問題のない処理ですが、HDD搭載機では若干遅く感じるかもしれません(もはやHDD搭載機が身の回りにないので不明)。

これまでは、マンガの新規連載がはじまるとcurlコマンドで画像をダウンロードしてPDFに連結する作業を手で行なっていたのですが(誰も頼んでねえよ)、新規連載が増えたので自動化してみました。それでもありあわせの部品を組み合わせただけなので、それほど手間はかかっていません。

本Scriptとは別に更新された差分をPDFに連結するAppleScriptを作って日々実行し、大きな画面でブラウズするのに役立てています。割とこういう、ごくごく私的なScriptで野心的な処理を先行してテストしているものです。

AppleScript名:ツイ4のページで新規連載マンガの画像を取得してPDF化(新規連載のPDF化)v3
– Created 2016-09-05 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
use framework “QuartzCore”
use BridgePlus : script “BridgePlus”
–http://piyocast.com/as/archives/4808

property SMSForder : a reference to current application’s SMSForder
property |NSURL| : a reference to current application’s |NSURL|
property NSURLRequest : a reference to current application’s NSURLRequest
property NSURLConnection : a reference to current application’s NSURLConnection
property NSArray : a reference to current application’s NSArray
property NSFileManager : a reference to current application’s NSFileManager
property NSNumberFormatter : a reference to current application’s NSNumberFormatter
property NSPredicate : a reference to current application’s NSPredicate
property PDFPage : a reference to current application’s PDFPage
property PDFDocument : a reference to current application’s PDFDocument
property NSURLRequestUseProtocolCachePolicy : a reference to current application’s NSURLRequestUseProtocolCachePolicy
property NSNumberFormatterPadBeforePrefix : a reference to current application’s NSNumberFormatterPadBeforePrefix
property NSImage : a reference to current application’s NSImage
property NSSortDescriptor : a reference to current application’s NSSortDescriptor
property NSNumber : a reference to current application’s NSNumber
property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSString : a reference to current application’s NSString

property theTargetSite : “http://sai-zen-sen.jp/”

tell application “Safari”
  if (count every document) = 0 then
    display notification “Safari does not open web page”
    
return
  end if
  
  
set docTitle to (do JavaScript “document.title” in front document) –Title
  
  
tell front document –URL
    set aURL to URL
  end tell
end tell

if aURL does not start with theTargetSite then
  display notification “This site is not the target”
  
return
end if

–Safariの最前面のウィンドウから画像リンクをすべて取得(Height, Width, URL)
set aList to getImageSizeAndURLOfFrontSafariDocument() of me

–取得した画像情報の2D Listをサイズで降順ソート
load framework –Force loading BridgePlus framework
set sortIndexes to {0, 1} –Key Item id: begin from 0
set sortOrders to {false, false}
set sortTypes to {“compare:”, “compare:”}
set resList to (current application’s SMSForder’s subarraysIn:(aList) sortedByIndexes:sortIndexes ascending:sortOrders sortTypes:sortTypes |error|:(missing value))

–画像が取得できなかったら処理終了
if (resList as list) = {} then
  display notification “There is no images on this page”
  
return –No Result
end if

–最大サイズの画像情報を取得する(おそらくマンガ)
set {maxHeight, maxWidth, maxURL} to contents of first item of (resList as list)

set aNSURL to |NSURL|’s URLWithString:maxURL
set aNSURLfilename to (aNSURL’s lastPathComponent())
set aNSURLpure to aNSURL’s URLByDeletingLastPathComponent()
set aNSURLextension to aNSURLfilename’s pathExtension() as string
set aNSURLfilenameLen to (aNSURLfilename’s stringByDeletingPathExtension())’s |length|() as integer –画像ファイル名から拡張子を除去した部分の文字列長

–画像情報リストを画像サイズで抽出
set maxHeightStr to (maxHeight as integer) as string
set maxWidthStr to (maxWidth as integer) as string
set thePred to NSPredicate’s predicateWithFormat:(“(self[0] == “ & maxHeightStr & “) AND (self[1] == “ & maxWidthStr & “)”)
set bArray to (resList’s filteredArrayUsingPredicate:thePred) as list

–URLからファイル名の数値部分のみ抽出
set imageArray to current application’s NSMutableArray’s new()
repeat with i in bArray
  set j to contents of last item of i –(Image URL)
  
set aTmpURL to (|NSURL|’s URLWithString:j)
  
set aTmpfilename to (aTmpURL’s lastPathComponent()) as string
  
set numStr to first item of (my findPattern:(“^\\d{1,” & (aNSURLfilenameLen as string) & “}”) inString:aTmpfilename)
  
set jj2 to (SMSForder’s transformedFrom:numStr ICUTransform:“Fullwidth-Halfwidth” inverse:false) as integer
  (
imageArray’s addObject:jj2)
end repeat

–ファイル名から抽出した数値の最小値と最大値を求める。ただ、実運用したらWeb側から画像をすべて取得されない(非同期読み込みを行なっているらしい)ケースがあったため、ここの値は参考値程度にしか使えなかった
set maxRes to (imageArray’s valueForKeyPath:“@max.self”)’s intValue() –最大値
set minRes to (imageArray’s valueForKeyPath:“@min.self”)’s intValue() –最小値
log {minRes, maxRes}

–PDFのファイル名と場所をユーザーに確認
set pdfFile to (choose file name with prompt “Select PDF Name & Location” default location (path to pictures folder) default name (docTitle & “.pdf”))
set pdfFilePOSIX to POSIX path of pdfFile
set newFilePath to current application’s NSString’s stringWithString:pdfFilePOSIX

–Make Blank PDF
set aPDFdoc to PDFDocument’s alloc()’s init()

–Download each image and append to blank PDF
set insCount to 1 –画像ダウンロード用のページ数(Loop Counter)とPDF連結用のページ番号(insCount)を分離

–repeat with i from minRes as integer to maxRes as integer
repeat with i from 1 to 9999
  –URL部品の連結
  
set aFILENAME to numToZeroPaddingStr(i, aNSURLfilenameLen, “0″) of me
  
set aFULLURL to (aNSURLpure’s absoluteString() as string) & (aFILENAME as string) & “.” & (aNSURLextension as string)
  
set aURL to (|NSURL|’s URLWithString:aFULLURL)
  
  
–URL(画像)をダウンロード
  
set {uRes, headerRes, aData} to checkURLResourceExistence(aURL, 3) of me
  
  
if uRes = true then
    display notification “Episode “ & (i as string) & ” exists…”
    
set bImage to (NSImage’s alloc()’s initWithData:aData)
    (
aPDFdoc’s insertPage:(PDFPage’s alloc()’s initWithImage:bImage) atIndex:(insCount - 1))
    (
aPDFdoc’s writeToFile:newFilePath) –1Page更新するたびにファイル保存
    
set changedF to true –PDFにページが追記されたことを検出
  else
    display notification “No more new episode….”
    
exit repeat
  end if
  
  
set insCount to insCount + 1
end repeat

–FinderコメントにURLを記入
tell application “Finder”
  set comment of (pdfFile as alias) to (aNSURLpure’s absoluteString() as string)
end tell

–生成したPDFをオープン。ビューワー経由ではなくFinder経由でopen命令を送って表示
tell application “Finder”
  open (pdfFile as alias)
end tell
–ここで処理終了

—————

on getImageSizeAndURLOfFrontSafariDocument()
  set aList to {}
  
  
tell application “Safari”
    if its running then
      if (count every document) = 0 then return {}
      
set aRes to (do JavaScript “document.images.length” in front document)
      
      
repeat with i from 0 to (aRes - 1)
        set aHeight to do JavaScript ((“document.images[” & i as string) & “].height”) in front document
        
set aWidth to do JavaScript ((“document.images[” & i as string) & “].width”) in front document
        
set aSRC to do JavaScript ((“document.images[” & i as string) & “].src”) in front document
        
set the end of aList to {aHeight, aWidth, aSRC}
      end repeat
    end if
  end tell
  
  
return aList
end getImageSizeAndURLOfFrontSafariDocument

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list
  
set theResult to {}
  
set theNSString to NSString’s stringWithString:theString
  
  
repeat with i in theFinds
    set theRange to (contents of i)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

–1D List(文字)をsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DList:theList ascOrder:aBool
  set aDdesc to NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DList:ascOrder:

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

– 指定URLにファイル(画像など)が存在するかチェック
–> {存在確認結果(boolean), レスポンスヘッダー(NSDictionary), データ(NSData)}
on checkURLResourceExistence(aURL, timeOutSec as real)
  set aRequest to (NSURLRequest’s requestWithURL:aURL cachePolicy:(NSURLRequestUseProtocolCachePolicy) timeoutInterval:timeOutSec)
  
set aRes to (NSURLConnection’s sendSynchronousRequest:aRequest returningResponse:(reference) |error|:(missing value))
  
set dRes to (first item of (aRes as list))
  
set bRes to (second item of (aRes as list))
  
if bRes is not equal to missing value then
    set hRes to (bRes’s allHeaderFields())
    
set aResCode to (bRes’s statusCode()) as integer
  else
    set hRes to {}
    
set aResCode to 404
  end if
  
return {(aResCode = 200), hRes, dRes}
end checkURLResourceExistence

–指定PDFのページ数をかぞえる(10.9対応。普通にPDFpageから取得)
–返り値:PDFファイルのページ数(整数値)
on pdfPageCount(aFile)
  set aFile to POSIX path of aFile
  
set theURL to |NSURL|’s fileURLWithPath:aFile
  
set aPDFdoc to PDFDocument’s alloc()’s initWithURL:theURL
  
set aRes to aPDFdoc’s pageCount()
  
return aRes as integer
end pdfPageCount

★Click Here to Open This Script 

2017/09/03 ソフトウェア的にアンマウントしたが物理的に接続解除していないDriveの名前を取得

Finder上でいったんマウントしたあと、アンマウントしてもMac本体に物理的に接続したままのドライブ名を取得するAppleScriptです。

  「こんなもの、誰が何のために使うんだろうか?」

と首をひねる内容ですが、自分のところにも以前に1回だけこのような質問が来たことがあります。このScriptのオリジナルはShane Stanleyによるものですが(魔改造してブラッシュアップしていますが)、どういうニーズから作ったのか本人に聞いてみたいものです。

Finder経由でドライブ情報を取得するかぎりではそういう状態にあるドライブの存在を知ることは不可能ですが、diskutil経由で取得できるというのは意外でした。そういう意味では純粋なAppleScriptで書くことも可能ですが、おそらくこの倍ぐらいの長さになるものと思われます。

一応、macOS 10.13 Beta上でテストしてありますが、RAM 4GBのMacBook Airでは何かFinderの動作が不安定でいろいろクラッシュしています。

HFSおよびAPFSのドライブには対応していますが、macOSでマウント可能な他のフォーマットのドライブすべて(HFS、HFS Plus、APFS、WebDAV、UDF、FAT、ExFAT、SMB/CIFS、AFP、NFS、FTP、Xsan、NTFS、CDDAFS、ISO9660)を試せているわけではないので、実用的なレベルに持っていくためにはそのあたりを攻める(実際にテストして拡充させる)必要があることでしょう。

AppleScript名:ソフトウェア的にアンマウントしたが物理的に接続解除していないDriveの名前を取得
– Created 2017-06-20 Shane Stanley
– Modified 2017-06-20 Takaaki Naganoya
use AppleScript version “2.4″ – Yosemite (10.10) or later
use framework “Foundation”
use scripting additions
–http://piyocast.com/as/archives/4802

property NSString : a reference to current application’s NSString
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableSet : a reference to current application’s NSMutableSet
property NSMutableArray : a reference to current application’s NSMutableArray

set dRes to retUnmountedAndUnremovedDriveNames() of me
–> {”Data”}

on retUnmountedAndUnremovedDriveNames()
  set theResult to do shell script “diskutil list -plist”
  
  
– make dictionary from property list
  
set theResult to NSString’s stringWithString:theResult
  
set pListDict to theResult’s propertyList()
  
  
– extract relevant info
  
set disksAndParitions to pListDict’s objectForKey:“AllDisksAndPartitions”
  
  
set partitionsArray to NSMutableArray’s array() – to store values
  
repeat with anEntry in disksAndParitions
    set thePartitions to (anEntry’s objectForKey:“Partitions”)
    
if thePartitions = missing value then – no partitions means a volume
      (partitionsArray’s addObject:anEntry)
    else
      (partitionsArray’s addObjectsFromArray:thePartitions)
    end if
  end repeat
  
  
– filter by Content type
  
set thePred to NSPredicate’s predicateWithFormat:“Content == ’Apple_HFS’ OR Content == ’Apple_APFS’ “
  
set partitionsArray to partitionsArray’s filteredArrayUsingPredicate:thePred
  
  
– get names
  
set dList to (partitionsArray’s valueForKey:“VolumeName”) as list
  
  

  
set edList to retEjectableDriveNames() of me
  
set the end of edList to retBootDriveName() of me
  
set the end of edList to missing value –消し込みのために追加
  
  
set aSet to NSMutableSet’s setWithArray:dList
  
set bSet to NSMutableSet’s setWithArray:edList –Boot drive & local ejectable drives
  
  
aSet’s minusSet:bSet –補集合
  
set dRes to aSet’s allObjects() as list
  
  
return dRes
end retUnmountedAndUnremovedDriveNames

on retEjectableDriveNames()
  tell application “Finder”
    try
      set dList to name of every disk whose ejectable is true
    on error
      set dList to {}
    end try
  end tell
  
return dList
end retEjectableDriveNames

on retBootDriveName()
  tell application “Finder”
    return name of startup disk
  end tell
end retBootDriveName

★Click Here to Open This Script 

2017/09/02 Finder上で選択中のファイルの中に書かれているタイトルに基づいてリネーム

Finder上で選択中のテキストファイルの中に書かれているタイトルを正規表現による検索で検出して、ファイル名に指定するAppleScriptです。

技術的にはぜんぜん見るべき点がありませんが、そこそこ役立つ見本です(ありものをつなぎ合わせた書き捨てレベル)。

「小説を読もう」サイトからKnight’s and Magicのテキストを(テキストエンコーディングにUTF-8を指定して)ダウンロードしてきたら、

text_download.png

ファイル名が、

filename_before.png

のようになっていたので、これを、

filename_after.png

のようにリネームしたいと考え、各テキストファイル中に書かれているタイトル部分を、

file_contents.png

正規表現で検索して指定してみました。ほとんどテキストの1行目にタイトルが書かれているので、1行目の内容をファイル名にしてもよかったのですが、

knight_magic4.png

このように(↑)たまにイレギュラーなファイルが存在しているので、正規表現でサーチしています。

実際にテストしてみたところ、全角文字の「#」と半角文字の「#」が登場していたので、全角文字で検索して存在しないようであれば半角文字で再検索するようになっています。

数百個ぐらいのファイルの処理だと、このようにFinderからselectionを拾ってきても十分なところですが、数千個以上になると別の方法を考えるべきでしょう。

AppleScript名:Finder上で選択中のファイルの中に書かれているタイトルに基づいてリネーム
– Created 2017-09-01 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4797

property NSRegularExpressionDotMatchesLineSeparators : a reference to current application’s NSRegularExpressionDotMatchesLineSeparators
property NSRegularExpressionAnchorsMatchLines : a reference to current application’s NSRegularExpressionAnchorsMatchLines
property NSRegularExpression : a reference to current application’s NSRegularExpression
property NSString : a reference to current application’s NSString

tell application “Finder”
  set aSel to selection as alias list
  
if aSel = {} or aSel = “” then return
end tell

repeat with i in aSel
  set aStr to (read i as «class utf8»)
  
  
–全角のタイトルマークでタイトルを検索
  
set aList to retHeaders(aStr, “#”) of me
  
if aList is not equal to {} then
    set newName to (contents of first item of aList)
    
tell application “Finder”
      set name of i to newName & “.txt”
    end tell
  else
    –半角のタイトルマークでタイトルを検索
    
set bList to retHeaders(aStr, “#”) of me
    
if bList is not equal to {} then
      set newName to (contents of first item of bList)
      
tell application “Finder”
        set name of i to newName & “.txt”
      end tell
    else
      log {i as string}
    end if
  end if
end repeat

on retHeaders(aCon, aHeaderMark)
  set tList to {}
  
set regStr to “^” & aHeaderMark & “{1,6}[^” & aHeaderMark & “]*?$”
  
  
set headerList to my findPattern:regStr inString:aCon
  
repeat with i in headerList
    set j to contents of i
    
set regStr2 to “^” & aHeaderMark & “{1,6}[^” & aHeaderMark & “]*?”
    
set headerLevel to length of first item of (my findPattern:regStr2 inString:j)
    
set the end of tList to (text (headerLevel + 1) thru -1 in j)
  end repeat
  
  
return tList
end retHeaders

on findPattern:thePattern inString:theString
  set theOptions to ((NSRegularExpressionDotMatchesLineSeparators) as integer) + ((NSRegularExpressionAnchorsMatchLines) as integer)
  
set theRegEx to NSRegularExpression’s regularExpressionWithPattern:thePattern options:theOptions |error|:(missing value)
  
set theFinds to theRegEx’s matchesInString:theString options:0 range:{location:0, |length|:length of theString}
  
set theFinds to theFinds as list – so we can loop through
  
set theResult to {} – we will add to this
  
set theNSString to NSString’s stringWithString:theString
  
repeat with i from 1 to count of items of theFinds
    set theRange to (item i of theFinds)’s range()
    
set end of theResult to (theNSString’s substringWithRange:theRange) as string
  end repeat
  
return theResult
end findPattern:inString:

★Click Here to Open This Script 

2017/08/28 指定フォルダ内の指定文字列を名称に含むファイル、フォルダを抽出する v2

指定フォルダ内に存在するファイル/フォルダを名称の一部に含むキーワードで抽出するAppleScriptです。NSFileManager経由で高速に処理します。

指定フォルダ内のファイルやフォルダを求める処理は、初歩の初歩で行うものですが、macOS 10.6以降はFinder経由でこれを行うと処理速度が落ちるようになってきました。Finderのチューニングの問題だと思うのですが、AppleScriptからの問い合わせに対してあまり高速に結果を返さないようになってきました(時期的に見ると、ちょうどFinderがCocoa化されたタイミング。Finderのパフォーマンスを確保するための技術的な問題があったのでは?)。

macOS 10.6当時は「ゆくゆくはFile処理はすべてSystem Eventsに移管するのではないか?」と感じていましたが、どうもこのプランは立ち消えになったようで、Finder TabやらFinder TagがFinderのAppleScript用語辞書に実装されないまま、うやむやに。

今日、Finder経由でのファイル情報の取得はとてもコスト(=処理時間)のかかる処理になってきました(条件を何も指定しなければそれなりの速度は出ます。条件指定を行うとちょっと、、、)。逆に、ファイルの移動や削除については大幅に速度が落ちるといったことはありません。

さらに、macOS 10.13で試してみると、Finder経由で指定文字列をファイル名に含むファイルの抽出が遅くなっています(as alias listで処理結果をcastしても遅い)。数千ファイル存在するようなフォルダで実行すると、かーなり遅いです(4,300ファイルのフォルダに対して実行してみたら、SSD搭載機であってもAppleEvent Time Out(=180 sec)にひっかかるぐらい待たされる)。

# 搭載RAM 4GBのMacBook Air 2011でmacOS 10.13betaを試しているので、メモリが少なすぎてパフォーマンス低下をきたしている可能性もあります。ねんのため。

macOS 10.13上でもshell scriptやCocoa経由でのAppleScript処理は問題なく速いので、実用的な速度のAppleScriptを書こうとしたら「なるべくFinder経由でファイル処理を行わない」のがセオリーになりつつあります。

さらに、Xcode上のCocoa AppleScript appletでも各種ファイル処理(System Events経由で特定フォルダへのパスを求めるような気軽な処理)が実行できないケースもあります。指定フォルダ内のファイル/フォルダの一覧取得については、NSFileManager経由で処理することが妥当だと感じるようになりました。

処理結果についてはfileのリストで返すものとPOSIX pathで返すものを用意してみました。fileで返しておけばaliasに型変換してGUIアプリケーションに渡せますし、POSIX pathで返しておけばCocoa系のAPI経由での処理との相性がいいところです。

AppleScript名:指定フォルダ内の指定文字列を名称に含むファイル、フォルダを抽出する v2
– Created 2017-08-28 by Takaaki Naganoya
– 2017 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4794

property NSURLIsDirectoryKey : a reference to current application’s NSURLIsDirectoryKey
property NSDirectoryEnumerationSkipsHiddenFiles : a reference to current application’s NSDirectoryEnumerationSkipsHiddenFiles
property NSPredicate : a reference to current application’s NSPredicate
property NSMutableArray : a reference to current application’s NSMutableArray
property NSDirectoryEnumerationSkipsPackageDescendants : a reference to current application’s NSDirectoryEnumerationSkipsPackageDescendants
property NSFileManager : a reference to current application’s NSFileManager
property |NSURL| : a reference to current application’s |NSURL|
property NSDirectoryEnumerationSkipsSubdirectoryDescendants : a reference to current application’s NSDirectoryEnumerationSkipsSubdirectoryDescendants

set sourceFolder to POSIX path of (choose folder)
set aKeyword to “スクリーン”

set file1Res to my filterOutFilesByFileName:aKeyword fromDirectory:sourceFolder
–>  {file “Cherry:Users:maro:Desktop:スクリーンショット 2017-04-15 13.18.03.png”, file “Cherry:Users:maro:Desktop:スクリーンショット 2017-04-15 13.48.44.png”, …}
set file2Res to my filterOutPOSIXPathsByFileName:aKeyword fromDirectory:sourceFolder
–>  {”/Users/maro/Desktop/スクリーンショット 2017-04-15 13.18.03.png”, “/Users/maro/Desktop/スクリーンショット 2017-04-15 13.48.44.png”, ….}

set folder1Res to my filterOutFoldersByFolderName:aKeyword fromDirectory:sourceFolder
–>  {file “Cherry:Users:maro:Desktop:スクリーン”}
set folder2Res to my filterOutDirPOSIXPathsByFolderName:aKeyword fromDirectory:sourceFolder
–>  {”/Users/maro/Desktop/スクリーン”}

–Get files

–指定フォルダ内の指定文字列を含むファイル名のファイルをfileのlistで抽出する
on filterOutFilesByFileName:fileNameStr fromDirectory:sourceFolder
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer) + ((NSDirectoryEnumerationSkipsSubdirectoryDescendants) as integer)
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_(“lastPathComponent CONTAINS %@”, fileNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set theEnumerator to foundItemList’s objectEnumerator()
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue = missing value then exit repeat
    
set {theResult, isDirectory} to aValue’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value)
    
if (isDirectory as boolean) = false then
      anArray’s addObject:(aValue’s |path|() as «class furl»)
    end if
  end repeat
  
  
return anArray as list
end filterOutFilesByFileName:fromDirectory:

–指定フォルダ内の指定文字列を含むファイル名のファイルをPOSIX pathのlistで抽出する
on filterOutPOSIXPathsByFileName:fileNameStr fromDirectory:sourceFolder
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer) + ((NSDirectoryEnumerationSkipsSubdirectoryDescendants) as integer)
  
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_(“lastPathComponent CONTAINS %@”, fileNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set theEnumerator to foundItemList’s objectEnumerator()
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue = missing value then exit repeat
    
set {theResult, isDirectory} to aValue’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value)
    
if (isDirectory as boolean) = false then
      anArray’s addObject:(aValue’s |path|())
    end if
  end repeat
  
  
return anArray as list
end filterOutPOSIXPathsByFileName:fromDirectory:

–Get folders

–指定フォルダ内の指定文字列を含むフォルダ名のフォルダをfileのlistで抽出する
on filterOutFoldersByFolderName:folderNameStr fromDirectory:sourceFolder
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer) + ((NSDirectoryEnumerationSkipsSubdirectoryDescendants) as integer)
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_(“lastPathComponent CONTAINS %@”, folderNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set theEnumerator to foundItemList’s objectEnumerator()
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue = missing value then exit repeat
    
set {theResult, isDirectory} to aValue’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value)
    
if (isDirectory as boolean) = true then
      anArray’s addObject:(aValue’s |path|() as «class furl»)
    end if
  end repeat
  
  
return anArray as list
end filterOutFoldersByFolderName:fromDirectory:

–指定フォルダ内の指定文字列を含むフォルダ名のフォルダをPOSIX pathのlistで抽出する
on filterOutDirPOSIXPathsByFolderName:folderNameStr fromDirectory:sourceFolder
  set fileManager to NSFileManager’s defaultManager()
  
set aURL to |NSURL|’s fileURLWithPath:sourceFolder
  
set theOptions to ((NSDirectoryEnumerationSkipsPackageDescendants) as integer) + ((NSDirectoryEnumerationSkipsHiddenFiles) as integer) + ((NSDirectoryEnumerationSkipsSubdirectoryDescendants) as integer)
  
  
set directoryContents to fileManager’s contentsOfDirectoryAtURL:aURL includingPropertiesForKeys:{} options:theOptions |error|:(missing value)
  
set findPredicates to NSPredicate’s predicateWithFormat_(“lastPathComponent CONTAINS %@”, folderNameStr)
  
set foundItemList to directoryContents’s filteredArrayUsingPredicate:findPredicates
  
  
–Remove Folders From found URL Array
  
set theEnumerator to foundItemList’s objectEnumerator()
  
set anArray to NSMutableArray’s alloc()’s init()
  
repeat
    set aValue to theEnumerator’s nextObject()
    
if aValue = missing value then exit repeat
    
set {theResult, isDirectory} to aValue’s getResourceValue:(reference) forKey:(NSURLIsDirectoryKey) |error|:(missing value)
    
if (isDirectory as boolean) = true then
      anArray’s addObject:(aValue’s |path|())
    end if
  end repeat
  
  
return anArray as list
end filterOutDirPOSIXPathsByFolderName:fromDirectory:

★Click Here to Open This Script 

2017/03/13 Finderの最前面のウィンドウで選択中のアイテムと非選択アイテムを切り替える

Finderの最前面のウィンドウ中で選択中のアイテムと選択対象外のアイテムを切り替えるAppleScriptです。

finder_anime2.gif

どういう場面において便利なのかは不明ですが、、、、

AppleScript名:Finderの最前面のウィンドウで選択中のアイテムと非選択アイテムを切り替える
– Created 2017-03-12 by Takaaki Naganoya
– 2017 Piyomaru Software
–http://piyocast.com/as/archives/4526
tell application "Finder"
  if (count every window) = 0 then return
  
set aSel to selection as alias list
  
  
tell window 1
    set aList to (every file) as alias list
  end tell
  
  
set newSel to {}
  
repeat with i in aList
    set j to contents of i
    
if j is not in aSel then
      set the end of newSel to j
    end if
  end repeat
  
  
set selection to newSel
end tell

★Click Here to Open This Script 

2016/10/31 指定ファイルをFinderで選択表示

指定のファイルをFinder上で選択状態で表示するAppleScriptです。

何か処理結果をファイルに書き出したような場合、どのファイルに出力したかを明示的に表現するために、Finder上で新規ウィンドウを作成して対象ファイルを選択状態にすることはよくあります。

そのためのAppleScriptで、Pure AppleScript版とCocoaの機能を用いたAppleScriptObjC版です。

見比べてみて、このぐらいの素朴な処理&用途ではASOCを使う意義が見出せないですね。

AppleScript名:指定ファイルをFinderで選択表示_pure_as
– Created 2016-10-31 by Takaaki Naganoya
– 2016 Piyomaru Software
–http://piyocast.com/as/archives/4297

set aFile to choose file
tell application “Finder”
  activate
  
reveal aFile
end tell

★Click Here to Open This Script 

AppleScript名:指定ファイルをFinderで選択表示_asoc
– Created 2016-10-31 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”
–http://piyocast.com/as/archives/4297

set aFile to POSIX path of (choose file)
set pathStr to current application’s NSString’s stringWithString:aFile
set parentPath to pathStr’s stringByDeletingLastPathComponent()
set aRes to current application’s NSWorkspace’s sharedWorkspace()’s selectFile:pathStr inFileViewerRootedAtPath:parentPath

★Click Here to Open This Script 

2016/10/05 SDカードを検出

マウント中のドライブのマウントポイント(例:/Volumes/ドライブ名)を指定すると、当該ドライブがSDカードかどうかを判定するAppleScriptです。

system_profilerコマンドを呼び出して詳細な情報を取得して判定するようにして、手元にある2枚のSDカードはこれで判定できています。MacBook Pro Retina 2012の内蔵SDカードリーダー、およびUSB接続のSDカードリーダーの両方で判定できることを確認しています。

system_profilerの実行結果は、

mountedsd1.jpg

MacBook Pro Retina内蔵SDカードリーダーに入れたSDXCカード「JVCCAM_SD」
-> {writable:”yes”, _name:”JVCCAM_SD”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, size_in_bytes:3.3179041792E+10, bsd_name:”disk3s1″, free_space_in_bytes:2.0819673088E+10, mount_point:”/Volumes/JVCCAM_SD”, physical_drive:{is_internal_disk:”yes”, device_name:”Built In SDXC Reader”, protocol:”Secure Digital”, media_name:”Apple SDXC Reader Media”, partition_map_type:”master_boot_record_partition_map_type”}}

USB接続SDカードリーダーに入れたSDカード「RICOHDCX」
–> {writable:”yes”, _name:”RICOHDCX”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, volume_uuid:”3BD2F468-5B42-327C-AB96-2812A02E7126″, size_in_bytes:7.939817472E+9, bsd_name:”disk4s1″, free_space_in_bytes:5.415960576E+9, mount_point:”/Volumes/RICOHDCX”, physical_drive:{is_internal_disk:”no”, device_name:”SD Transcend”, protocol:”USB”, media_name:”TS-RDF5 SD Transcend Media”, partition_map_type:”master_boot_record_partition_map_type”}}

mountedsd2.jpg

MacBook Pro Retina内蔵SDカードリーダーに入れたSDカード「RICOHDCX」
–> {writable:”yes”, _name:”RICOHDCX”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, volume_uuid:”3BD2F468-5B42-327C-AB96-2812A02E7126″, size_in_bytes:7.939817472E+9, bsd_name:”disk3s1″, free_space_in_bytes:5.415960576E+9, mount_point:”/Volumes/RICOHDCX”, physical_drive:{is_internal_disk:”yes”, device_name:”Built In SDXC Reader”, protocol:”Secure Digital”, media_name:”Apple SDXC Reader Media”, partition_map_type:”master_boot_record_partition_map_type”}}

USB接続SDカードリーダーに入れたSDXCカード「JVCCAM_SD」
–> {writable:”yes”, _name:”JVCCAM_SD”, ignore_ownership:”yes”, file_system:”MS-DOS FAT32″, size_in_bytes:3.3179041792E+10, bsd_name:”disk4s1″, free_space_in_bytes:2.0819673088E+10, mount_point:”/Volumes/JVCCAM_SD”, physical_drive:{is_internal_disk:”no”, device_name:”SD Transcend”, protocol:”USB”, media_name:”TS-RDF5 SD Transcend Media”, partition_map_type:”master_boot_record_partition_map_type”}}

となっており、device_nameとmedia_nameを単語ごとにリスト化し、「SD」「SDHC」「SDXC」の単語が入っているかどうかを判定しています。

同一名称のSDカードが複数枚同時にマウントされた場合の挙動については検証していないため、そういうケースには対応しきれていないと思われます。ご注意ください。

AppleScript名:SDカードを検出
– Created 2016-10-04 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
–http://piyocast.com/as/archives/4251

tell application “Finder”
  set driveList to every disk whose format is (MSDOS format) and ejectable is true and startup is false
  
  repeat with i in driveList
    set myDisk to disk of (first item of i)
    
set myMountPoint to POSIX path of (myDisk as alias)
    
–> “/Volumes/JVCCAM_SD/”
    
–> “/Volumes/RICOHDCX/”
    
set sdRes to detectSDCard(myMountPoint) of me
    
–> true –SD Card, false –Not SD Card
  end repeat
end tell

on detectSDCard(myMountPoint as string)
  
  set resData to runCommandString(“system_profiler -xml SPStorageDataType”) of me
  
set aaDict to (readPlistFromStr(resData) of me) as list
  
set aDictList to (_items of first item of aaDict)
  
  repeat with i in aDictList
    set j to contents of i
    
    set aMountPoint to (mount_point of j) as string
    
–> “/Volumes/JVCCAM_SD”
    
–> “/Volumes/RICOHDCX”
    
    if aMountPoint is not equal to “/” then
      if ((aMountPoint & “/”) is equal to myMountPoint) then
        set aDevName to words of (device_name of physical_drive of j)
        
set aMediaName to words of (media_name of physical_drive of j)
        
        –SD/SDHC/SDXCのカード検出
        
set aDevF to (“SD” is in aDevName) or (“SDHC” is in aDevName) or (“SDXC” is in aDevName)
        
set aMediaF to (“SD” is in aMediaName) or (“SDHC” is in aMediaName) or (“SDXC” is in aMediaName)
        
        if (aDevF and aMediaF) then return true
      end if
    end if
  end repeat
  
  return false
end detectSDCard

–文字列で与えたシェルコマンドを実行する
on runCommandString(commandStr as string)
  set aPipe to current application’s NSPipe’s pipe()
  
set aTask to current application’s NSTask’s alloc()’s init()
  
aTask’s setLaunchPath:“/bin/sh”
  
aTask’s setArguments:{“-c”, current application’s NSString’s stringWithFormat_(“%@”, commandStr)}
  
aTask’s setStandardOutput:aPipe
  
set aFile to aPipe’s fileHandleForReading()
  
aTask’s |launch|()
  
return current application’s NSString’s alloc()’s initWithData:(aFile’s readDataToEndOfFile()) encoding:(current application’s NSUTF8StringEncoding)
end runCommandString

–stringのplistを読み込んでRecordに
on readPlistFromStr(theString)
  set aSource to current application’s NSString’s stringWithString:theString
  
set pListData to aSource’s dataUsingEncoding:(current application’s NSUTF8StringEncoding)
  
set aPlist to current application’s NSPropertyListSerialization’s propertyListFromData:pListData mutabilityOption:(current application’s NSPropertyListImmutable) |format|:(current application’s NSPropertyListFormat) errorDescription:(missing value)
  
return aPlist
end readPlistFromStr

★Click Here to Open This Script 

2016/10/04 Finderの最前面のフォルダを取得して、choose folderの始点フォルダに指定する

Finderの最前面のWindowで表示しているフォルダ(target)を取得して、後続のchoose folderコマンドで表示するデフォルトフォルダに指定するAppleScriptです。

targwin1_resized.png
▲最前面のWindowの内容

targwin2_resized.png
▲最前面のWindowの対象フォルダ(target)を取得してchoose folder

こういうのは、技術的にすごいとかどーのとかいうことは全然なくて、「おもてなし度が向上する」ワザ。過剰にやりすぎると嫌われることもあるので、用法・容量を守って過剰に使いすぎない程度に使ってみるといいのでしょう。

既存のPDFに対して指定フォルダ内のJPEG画像を末尾に追加するAppleScriptを書いたときに、「指定フォルダ」が最前面にあるのになんでこれを毎回指定しないとダメなんだろうか、と思って作ってみたものです。

AppleScript名:Finderの最前面のフォルダを取得して、choose folderの始点フォルダに指定する
– Created 2016-10-04 by Takaaki Naganoya
– 2016 Piyomaru Software
–http://piyocast.com/as/archives/4244

set targAlias to retFrontFinderWindowsTargetIfExits(path to desktop) of me
set aFol to choose folder default location targAlias

on retFrontFinderWindowsTargetIfExits(aDefaultLocation)
  tell application “Finder”
    set wCount to count every window
    
if wCount 1 then
      tell front window
        set aTarg to target as alias
      end tell
      
return aTarg
    else
      return aDefaultLocation
    end if
  end tell
end retFrontFinderWindowsTargetIfExits

★Click Here to Open This Script 

2016/07/28 Bookフォルダリネーム

書籍の原稿管理用のAppleScriptです。原稿は章ごとにフォルダ分けしており、

 9999 フォルダ名あるいはファイル名

といったように、仮想ノンブルを前に付けています。この仮想ノンブルで前後関係を制御しており、基本的に数字の大小だけが重要です。

whole.png

最後に1冊分PDFにまとめてビルドするAppleScriptで、書き出し対象のフォルダ以下のMarkdownファイルとPagesのファイルをピックアップし、ファイル名でソート。ソート順は仮想ノンブルによってコントロールされることになります。

Finderの並べ替えを使っているので、シンプルでわかりやすく、使い勝手もよいのですが……ただひとつ難点が。

途中にコンテンツを挿入したい場合にも仮想ノンブルでコントロールしつつ、途中に入るように操作するのですが……場合によってはフォルダおよび書類の仮想ノンブル部分だけリネームする必要が出てきます。

とくに、目下改定作業中の「AppleScript最新リファレンス」のコマンドリファレンス部分は、初版から大幅に改定・補充を行っているため、ひんぱんにリネームが発生して大変です(ーー;;

そこで、こんなScriptを組んでリネーム作業だけ自動化してみました。

Finder上でフォルダを選択して本Scriptを実行すると、選択中のフォルダ名から仮想ノンブルの番号を取得し、

before2.png

内包するフォルダに入っているファイルについても、すべて仮想ノンブル部分を書き換えます。

after2.png

AppleScript名:Bookフォルダリネーム
– Created 2016-07-28 by Takaaki Naganoya
– 2016 Piyomaru Software
use AppleScript version “2.4″
use framework “Foundation”
use scripting additions
# http://piyocast.com/as/archives/4177

set aStep to 10

tell application “Finder”
  set aSel to (selection as alias list)
end tell
if aSel = {} or aSel = “” then return
set aFol to first item of aSel

tell application “Finder”
  –選択中のフォルダの名称を取得
  
set aName to name of aFol
  
  
–選択中のフォルダに入っている書類を取得
  
tell folder (aFol as string)
    set fList to name of every file as alias list
  end tell
end tell

set {startNumStr, folderNameStr} to parseNumAndString(aName) of me
set startNum to startNumStr as integer

set aRes to sort1DListOrder(fList, true) of me –昇順でファイル名をソート

set aFolStr to aFol as string
set aCount to startNumStr
tell application “Finder”
  repeat with i in aRes
    set anAlias to (aFolStr & (i as string)) as alias
    
set tmpName to name of anAlias
    
    
set {curNum, curName} to parseNumAndString(tmpName) of me
    
set newName to (retZeroPaddingText(aCount, length of curNum) of me) & ” “ & curName
    
    
set name of anAlias to newName
    
    
set aCount to aCount + aStep
  end repeat
end tell

–文字列から先頭にあるとおぼしき数値と、スペースで区切られた後続の文字列を分離する
on parseNumAndString(aName)
  set aOffset to offset of ” “ in aName
  
if aOffset = 0 then error “Illigal Format”
  
set aNum to text 1 thru (aOffset - 1) of aName
  
set aText to text (aOffset + 1) thru -1 of aName
  
return {aNum, aText}
end parseNumAndString

–1D Listをsort / ascOrderがtrueだと昇順ソート、falseだと降順ソート
on sort1DListOrder(theList, aBool)
  set aDdesc to current application’s NSSortDescriptor’s sortDescriptorWithKey:“self” ascending:aBool selector:“localizedCaseInsensitiveCompare:”
  
set theArray to current application’s NSArray’s arrayWithArray:theList
  
return (theArray’s sortedArrayUsingDescriptors:{aDdesc}) as list
end sort1DListOrder

on retZeroPaddingText(aNum as integer, aDigitNum as integer)
  if aNum > (((10 ^ aDigitNum) as integer) - 1) then return “” –Range Check
  
set aFormatter to current application’s NSNumberFormatter’s alloc()’s init()
  
aFormatter’s setUsesGroupingSeparator:false
  
aFormatter’s setAllowsFloats:false
  
aFormatter’s setMaximumIntegerDigits:aDigitNum
  
aFormatter’s setMinimumIntegerDigits:aDigitNum
  
aFormatter’s setPaddingCharacter:“0″
  
set aStr to aFormatter’s stringFromNumber:(current application’s NSNumber’s numberWithFloat:aNum)
  
return aStr as string
end retZeroPaddingText

★Click Here to Open This Script 

2015/09/08 Dockアイコンをバウンドさせる

Cocoaの機能を用いて、DockのアイコンをバウンドさせるAppleScriptです。

実行中のアプリケーション(Script EditorやAppletを前提)が最前面に来ていない場合にDockのアイコンをバウンドさせます。

Finderを最前面に出しているのは動作確認のためで(最前面にScript EditorがいるとDockアイコンがバウンドしない)、それ以上でもそれ以下でもありません。

AppleScript名:ASOCでDockアイコンをバウンドさせる
– Created 2015-09-08 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

tell application “Finder” to activate –Script Editor/ASObjC Explorer 4を背面に

set anApp to current application’s NSApplication’s sharedApplication()
anApp’s requestUserAttention:(current application’s NSCriticalRequest)

★Click Here to Open This Script 

2015/08/04 指定の画像を別形式に変換する

指定の画像を別形式に変換するAppleScriptです。

OS XにはAppleScriptから制御できる専用の画像処理アプリケーション(GUIなし)である「Image Events」(sipsのラッパー)がありますが、なぜかImage EventsにはPDFへの変換機能が実装されていないため(sipsにはある)、著作権情報やファイルのオープンコントロール(パスワード設定)など細かいことを考えずにただPDFに変換するだけのものを作成。

さらにsipsがサポートしている画像形式かどうかのチェックを行うルーチンを付加して、「どの形式へ」変換するかを自由に( {{”jpeg”, “jpg”}, {”tiff”, “tif”}, {”png”}, {”gif”}, {”jp2″}, {”pict”}, {”bmp”}, {”qtif”}, {”psd”}, {”sgi”}, {”tga”}, {”pdf”}}の中でチェック)指定できるようにしてみました。

Photoshop上でR=255のRGB TIFF画像(真っ赤な単色塗り画像)を作成して、sRGBのICCプロファイルを添付。これを本ルーチンでPDF、JPEG、PNG、BMP、PSD、GIFに変換してみたところ、JPEGがR=254になった以外は色の変化はありませんでした。

red255tif.png

sips自体にはICC Profileの変換機能もあるため、もう少しいろいろチューニングが行えることでしょう。

AppleScript名:指定の画像を別形式に変換する
use AppleScript version “2.4″
use scripting additions

set targFormat to “pdf”
set aImage to choose file of type {“public.image”} with prompt (“Choose image file to convert to “ & targFormat & “.”)
set aRes to convertImageToANY(aImage, targFormat) of me
–> “Macintosh HD:Users:me:Desktop:1D1800F7-2755-4CFB-8322-F7EE0D64D570.jpg.pdf”

–指定の画像をsipsで別形式に変換する
on convertImageToANY(aImage as alias, outFormat as string)
  
  
set aExt to chkExtension(outFormat) of me
  
if aExt = false then return false
  
  
set bImage to (aImage as text) & “.” & aExt
  
tell application “Finder”
    set bExt to name extension of aImage
  end tell
  
  
–指定画像と変換後の画像フォーマットが同じだった
  
set bExt to chkExtension(bExt) of me
  
if aExt = bExt then return “There is no need to convert (same format)”
  
if bExt = false then return false –出力ファイルフォーマットエラー
  
  
set sText to “sips -s format “ & aExt & ” “ & quoted form of POSIX path of aImage & ” –out “ & quoted form of POSIX path of bImage
  
  
try
    do shell script sText
  on error erM
    return false –shell scriptの実行時にエラーが発生した
  end try
  
  
return bImage
  
end convertImageToANY

–複数表記形式のファイル名拡張子のチェックおよびsipsが受付可能なものに変換
on chkExtension(aExtension)
  –acceptable file name extensions
  
set formatList to {{“jpeg”, “jpg”}, {“tiff”, “tif”}, {“png”}, {“gif”, “giff”}, {“jp2″}, {“pict”}, {“bmp”}, {“qtif”}, {“psd”}, {“sgi”}, {“tga”}, {“pdf”}}
  
set hitF to false
  
  
ignoring case
    repeat with i in formatList
      set j to contents of i
      
if aExtension is in j then
        set aExt to contents of first item of j
        
set hitF to true
        
exit repeat
      end if
    end repeat
  end ignoring
  
  
if hitF = false then
    return false –No Hit  
  else
    return aExt –Hit
  end if
  
end chkExtension

★Click Here to Open This Script 

2015/07/23 ASOCでフォルダの連続作成1万個

ASOCでCocoaの機能を用いたフォルダ作成のテストを行いました。比較用にCocoaの機能を用いずにFinder経由で作成したものと、do shell script経由でシェルコマンドの機能を用いたものを用意して比較。

filegraph1.png

Cocoa経由で作成したものが非常に高速(Finder経由の7倍速ぐらい)ということが分かりました。ただし、テスト所要時間が実行するたびに変わり、ブレがあるのが特徴です(Finder経由で作成したときにはあまり見られない現象)。

テストはMacBook Pro Retina 2012 (Core i7 2.6GHz)、OS X 10.10.5上で実施。もちろん、HDDではなくSSDを使っていればこその速度です。

ASOCのテスト1は常識的なフォルダ作成のルーチンを作って呼び出したもの、テスト2は速度をかせぐために「連番のフォルダを作成する」という機能のかたまりを作成して、サブルーチンにはしなかったものです。ねらいどおり、若干後者の方が高速ですが・・・逆に、NSFileManager’s defaultManager() を1万回呼び出して無駄にオブジェクトを生成しても(テスト1)たいしたオーバーヘッドにならないんだなーということが体感的によく分かりました。

念のため、do shell scirpt経由でのフォルダ作成をためしてみたところ、予想外に時間がかかりました。do shell scirptコマンドによるシェルコマンド呼び出しのオーバーヘッドが大きいことが分かります(シェルコマンドが遅いのではなく、呼び出しのためのオーバーヘッドが大きい。シェル側からosascriptコマンドでAppleScriptを呼び出すと同様の現象が起こる)。

あまりにdo shell script使用バージョンが遅かったので、「mkdir -p」ではなく「mkdir」で実行してみたものの、大差はありませんでした。

Shane Stanleyが「do shell scriptコマンドなんか使うのやめようよ」(Cocoaの機能を呼び出したほうがいいぞ)と言っていた理由がよ〜くわかりました。

AppleScript名:ASOCでフォルダ作成テスト1
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

repeat with i from 1 to 10000
  set aDirStr to “Desktop/test/” & (i as string)
  
set aRes to makeDirUnderHome(aDirStr) of me
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 5.814414978027 (MacBook Pro Retina 2012, Core i7 2.6GHz)

on makeDirUnderHome(dirStr)
  
  
set homeDir to current application’s NSHomeDirectory()
  
set dirPath to homeDir’s stringByAppendingPathComponent:dirStr
  
set fileManager to current application’s NSFileManager’s defaultManager()
  
  
  
–Sample with continuation mark (¬) , similar to Objective-C format
  
set aRes to fileManager’s createDirectoryAtPath:dirPath ¬
    withIntermediateDirectories:true ¬
    
attributes:(missing value) ¬
    
|error|:(reference)
  
  
–Sample without continuation mark (¬)
  
–set aRes to fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference)
  
  
copy aRes to {aFlag, aError}
  
return aFlag as boolean
  
end makeDirUnderHome

★Click Here to Open This Script 

AppleScript名:ASOCでフォルダ作成テスト2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set homeDir to current application’s NSHomeDirectory()
set fileManager to current application’s NSFileManager’s defaultManager()

repeat with i from 1 to 10000
  
  
set aDirText to “Desktop/test/” & (i as string)
  
set dirPath to (homeDir’s stringByAppendingPathComponent:aDirText)
  
  
set aRes to (fileManager’s createDirectoryAtPath:dirPath withIntermediateDirectories:true attributes:(missing value) |error|:(reference))
  
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 4.124059021473 (MacBook Pro Retina 2012, Core i7 2.6GHz)

★Click Here to Open This Script 

AppleScript名:ASで(Finder経由で)フォルダ作成テスト(比較用)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”

tell application “Finder”
  
  
tell folder dtPath
    if (exists of folder “test”) = false then
      (make new folder with properties {name:“test”})
    end if
  end tell
  
  
repeat with i from 1 to 10000
    make new folder with properties {name:(i as string)} at folder tFolStr
  end repeat
  
end tell

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 37.555700004101

★Click Here to Open This Script 

AppleScript名:ASでフォルダ作成テスト(比較用2)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”
set tFolPosix to POSIX path of tFolStr

repeat with i from 1 to 10000
  set tPath to tFolPosix & (i as string)
  
do shell script “mkdir -p “ & tPath
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 272.857131958008

★Click Here to Open This Script 

AppleScript名:ASでフォルダ作成テスト(比較用3)
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”

–計時用
set a1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()

set dtPath to path to desktop
set dtPathStr to dtPath as string
set tFolStr to dtPathStr & “test:”
set tFolPosix to POSIX path of tFolStr

do shell script “mkdir -p “ & tFolPosix

repeat with i from 1 to 10000
  set tPath to tFolPosix & (i as string)
  
do shell script “mkdir “ & tPath
end repeat

–計時用
set b1Dat to current application’s NSDate’s timeIntervalSinceReferenceDate()
set c1Dat to b1Dat - a1Dat
–> 283.687083005905

★Click Here to Open This Script 

2015/07/17 Finderで選択中の戦場の絆のリプレイムービーのファイル名を取得して対戦人数を集計する

YouTubeからダウンロードした「戦場の絆」のリプレイムービー(複数)をFinder上で選択しておくと、ファイル名を取得してファイル名に記載されている「4VS4」などの文字列をもとに、各対戦人数(1VS1〜8VS8)の該当個数をカウントするAppleScriptです。

scrn1.png

scrn3.png

res.png

Finder上のファイルを処理するときに、

Finder上で選択中のファイルを取得する
choose fileコマンドで選択する
choose folderコマンドでフォルダを選択してその中に入っているファイルを処理する

といったあたりが定番です。ドラッグ&ドロップを受け付ける「ドロップレット」にすることも可能ですが、ドロップレットにするとデバッグが非常に困難になるため、最初はこのような形態のScriptにすることを強く推奨します。

AppleScript名:Finderで選択中の戦場の絆のリプレイムービーのファイル名を取得して対戦人数を集計する
tell application “Finder”
  set aSel to selection as alias list –Finder上で選択中のファイルを取得する定番の書き方
end tell

if aSel = {} then return –何もFinder上で選ばれていなかった場合

set outStr to “”

repeat with i from 1 to 8
  set aVSstr to makeVSstr(i) of me
  
set aCount to 0
  
  
repeat with ii in aSel
    tell application “System Events”
      set aName to name of ii
      
if aVSstr is in aName then
        set aCount to aCount + 1
      end if
    end tell
  end repeat
  
  
if aCount > 0 then
    set outStr to outStr & aVSstr & “:” & (aCount as string) & return
  end if
  
end repeat

outStr –「結果」への結果出力

– Sub-routine: 4 –> “4VS4″
on makeVSstr(aNum)
  set aNumStr to aNum as string
  
return aNumStr & “VS” & aNumStr
end makeVSstr

★Click Here to Open This Script 

2015/03/25 公開添削:サーバー上のファイル権限をローカルで書き換えて書き戻し

This is a special correction of my friend’s AppleScript. He is a beginner of AppleScript. So, I confused by his script and struggled with it.

At first, I didn’t understand original script at all. After several mail transactions, I understood his thought.

step1: Re-confirm the specification. Your spec is only in your brain. I don’t understand at all
step2: Where is comments?
step3: Make blank line to separate processing blocks
step4: Don’t make droplet. It is hard to debug!!
step5: The name of variables are important
step6: Mis-matching sub-routine’s spec
step7: Change the name of sub-routine


知り合いが「AppleScriptのバグが抜けない」と言っていたので、バーターで(未公開ソフトの評価)バグ抜き&公開指導をしてみることにしました。

「サーバ管理権限のないマシンから得意先が投げてきたアクセス権のおかしいファイルをフォルダごとローカルにコピーしてアクセス権を変更して元の階層に上書き、という内容のもと、ASを手習い中であります。

(Finderの)「内包している項目に適用」に相当する命令が見つからずループ仕立てにしてみています。まだまだAS自体つかめておらず、ほとんど拾ったコードのつぎはぎですが。」

▼Danger!!! Never Execute This Script.

AppleScript名:Original Script
【コメント】 Don’t Execute!!! This Script is incomplete!!!
————-
on open fileList
  tell application “Finder”
    set myDir to (parent of item 1 of fileList)
    
set aList to duplicate fileList to desktop
    
repeat with aItem in aList
      DiveFolder(aItem) of me
    end repeat
    
–move aList to myDir
    
–[true]
    
–delete aList
  end tell
end open

on DiveFolder(thePath)
  tell application “Finder”
    set theFiles to every file of thePath
    
repeat with aFile in theFiles
      set owner privileges of aFile to read write
      
set everyones privileges of aFile to read only
    end repeat
    
set theFolders to every folder of thePath
    
repeat with aFolder in theFolders
      DiveFolder(aFolder) of me
    end repeat
  end tell
end DiveFolder
————-

★Click Here to Open This Script 

チェック項目1:仕様がよくわからない

最初、すでにローカルにコピーしておいたものをScriptで処理するのかと思っていたのですが、違うとのこと。何度かメールをやりとりして、ようやく理解しました。

チェック項目2:コメントがない

自分の書いたScriptでも1日もたつと内容を忘れてしまいがちです。他人が書いたものだと、暗号解読に近いものになります。

コメントがないと、どういう意図のもとに書かれたのかが分からず、とても読みにくいです。

どういう意図で処理をしているのか、適度にコメントを入れましょう(サンプルでは、かなり多めに=過剰に入れています)。

チェック項目3:空行がなくて読みにくい

個人の趣味の問題ではありますが、処理上のまとまりが途切れたところで空行を入れると読みやすくなり、処理の意図が通じやすくなります。書き換え前後の比較を行うと一目瞭然です。

チェック項目4:Dropletをいきなり作るのはダメ

初心者にありがちな「実力不相応な高い到達点にいきなり行こうとする」パターンです。

Script Editorにはデバッグのための簡単な機能(ログ表示機能)がありますが、Dropletはアプリケーション形式でしか機能せず、アプリケーション形式で動かすとログを表示させたり動作内容を確認することができません。最後の最後でドロップレットにするとしても、最初はもっと低い到達点から、確実に動作することを確認しながら書き始めるべきです。

最初は、choose file/choose folderで選択するのがベスト。もうちょっとスマートにしたいなら、Finderのselectionを取得。Dropletにするのは、すべてが完成したあとの話です。

それでも、最初からDropletを作りたい場合にはScript Debuggerを買いましょう。Dropletもデバッグできます。初心者にこそ、Script Debuggerは有益です(そこらへんで、ニーズを持っているユーザー層とScript Debuggerの価格がマッチしていない)。

チェック項目5:変数名がさっぱりわからない

作者の意図が変数名からはさっぱり読み取れませんでした。小さなサブルーチン内でa、b、c、i、jといった変数名を使うこともありますが、極力、意図を反映したものにすべきでしょう。

チェック項目6:サブルーチンの仕様と呼び出す側の処理が合っていない

サブルーチン「DiveFolder」は、フォルダを渡すと再帰でその中身を探索しつつ、ファイルに対して所有者には読み書き可能な権限を、他のユーザーには読み出し可能な権限を与えるようになっています。

最初のプログラムでは、わざわざファイルを個別に渡すようになっており、サブルーチンを書いた人の意図を無視しています(サブルーチン側にもコメントがないことが問題。コメントを書かないのは本当に罪悪)。

チェック項目7:サブルーチン名が意味不明

だいたい、「DiveFolder」って、その中二病感満載の名前はなんじゃそりゃ(汗) 再帰でフォルダ内容の権限を書き換えるよ、という意図がわかる名前にしてほしいところです。あと、暗黙のルールでAppleScriptのハンドラ名(サブルーチン名)はCamel Caseで書くのがふつうです(DiveFolder→diveFolder)。

何度かやりとりをして、Keynote上で仕様を確認してみたりして、ようやく書き換えたのがこれ(↓)です。

AppleScript名:サーバー上の指定フォルダ内の全アクセス権限をローカルで書き換えて書き戻す
–  サーバー上にある特定フォルダ内のすべてのファイルを、ローカルにコピー(デスクトップ)し、
–  アクセス権限を変更して、サーバー上の元フォルダを消去して、ローカルのフォルダをアップロードする

set remoteFol to choose folder with prompt “サーバー上の、権限書き換え対象のフォルダを選択”

tell application “Finder”
  –権限を書き換えたフォルダを、あとでサーバーに書き戻すために準備
  
set returnFol to parent of remoteFol –最初に指定したフォルダの親フォルダを求めておく
  
  
–サーバー上のファイルをローカルのデスクトップ上にコピー
  
with timeout of 3600 seconds –遅いネットワーク(無線LANなど)を考慮(デフォルトでは180秒でタイムアウトでエラーに)
    set aLocalFol to (duplicate remoteFol to desktop with replacing) –同名のフォルダがあったら上書き
  end timeout
  
  
–ローカルのファイルをすべて権限書き換え
  
chmodRecursive(aLocalFol) of me
  
  
–サーバー上のフォルダを削除
  
delete remoteFol –shell scriptで”rm -rf”したくなる、、
  
empty trash
  
  
–サーバーにアップロード
  
with timeout of 3600 seconds –遅いネットワーク(無線LANなど)を考慮(デフォルトでは180秒でタイムアウトでエラーに)
    duplicate aLocalFol to returnFol
  end timeout
  
  
–あとしまつ。デスクトップ上にコピーしてきたフォルダを抹消
  
delete aLocalFol
  
empty trash
  
end tell

–指定フォルダ以下のすべてのファイルの権限を書き換える
on chmodRecursive(thePath) –サブルーチン名がぜんぜん意味不明だったのでリネーム
  tell application “Finder”
    
    
–ファイルに対しての処理を行う
    
set theFiles to every file of thePath
    
repeat with aFile in theFiles
      set owner privileges of aFile to read write
      
set everyones privileges of aFile to read only
    end repeat
    
    
–フォルダに対しての処理を再帰呼び出しで行う
    
set theFolders to every folder of thePath
    
repeat with aFolder in theFolders
      chmodRecursive(aFolder) of me –ここで再帰
    end repeat
    
  end tell
end chmodRecursive

★Click Here to Open This Script 

2015/02/08 Photoshop Action Setをインストールする

Photoshop Action SetをインストールするAppleScriptです。

・・・どこにもPhotoshopのコントロール部分がありませんが、これでいいんです。

Photoshopの自動化を行うにあたって、Script側に提供されている機能が決して多くはないため、Photoshop ActionでGUI上の操作を記録して、Actionを併用しつつScriptで処理するというスタイルが一般的です。つまり、Actionの有無や内容が期待した通りになっていることがたいへんに重要です。

以前に、海外の仕事で調査を行っていたときに、Photoshopがサポートしているファイルタイプ(=Photoshopがオープンできるファイルタイプ)に、Photoshop Action Setが存在していることに気づきました。

Photoshop Action Setはファイルに書き出すことができるのですが、これを各環境にインストールするあたりの操作を自動で行うことができずに、なかなかスマートな解決策がないもんだなぁ、と思っていました。

actionset.png

ところが、単にPhotoshop Action SetのファイルをPhotoshopのアプリケーションアイコンにドラッグ&ドロップすればPhotoshopにインストールできることがわかりました(盲点)。

そこで、その操作をAppleScriptで再現してみたら・・・問題なくインストールできました。アプリケーションバンドル中にPhotoshop Action Setのファイルを仕込んでおいて、Photoshop上に該当のAction Setが存在していなかったら、インストール・・・ということをやってみました。SandBox化したアプリ内でも実行できたので、割といい感じです。

Photoshop Actionの存在確認は(JavaScriptをまじえつつ)行えるので、存在していない場合にはAction Setを自動でインストールさせることが可能です。

AppleScript側で関知しないところでユーザーがAction Setの内容を改ざんするなどして、処理内容が維持できなくなる事態を防ぐべく、Action SetをScriptからインストール/アンインストールすることは重要です。

AppleScript名:Photoshop Actionをインストールする
set anActionFile to choose file with prompt “Choose Photoshop Action File”

tell application “Finder”
  set appFile to application file id “com.adobe.photoshop”
  
open anActionFile with appFile
end tell

★Click Here to Open This Script 

2015/02/08 起動中のプロセスの存在確認

Bundle IDで指定したプロセスが起動しているかどうかを確認するAppleScriptです。

AppleScript的には、System Eventsを経由してプロセスの起動中確認を行います。

AppleScript名:指定Bundle IDのプロセス存在確認(AS)
use AppleScript version “2.4″
use scripting additions

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshop”)
–> true

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshopoo!”)
–> false

on chkAppProcesByBundleID(aBundleID as string)
  tell application “System Events”
    set a to every process whose bundle identifier is aBundleID
    
if length of a 1 then
      return true
    else
      return false
    end if
  end tell
end chkAppProcesByBundleID

★Click Here to Open This Script 

しかし、この方法ではもしもMac App Storeに申請するアプリケーションで、System Eventsへのアクセスが封じられたらおしまいです。一応、shell script経由でpsコマンドを実行するという手段もありますが・・・

そこで、ASOCでCocoaの機能を用いてプロセスの存在確認を行ってみました。もうちょっと短く書けそうな気配もするのですが、とりあえず。

AppleScript名:指定Bundle IDのプロセス存在確認(ASOC)
– Created 2015-02-08 by Takaaki Naganoya
– 2015 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AppKit”

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshop”)
–> true

set aRes to chkAppProcesByBundleID(“com.adobe.Photoshopoo!”)
–> false

on chkAppProcesByBundleID(aBundleID as string)
  set appArray to current application’s NSWorkspace’s sharedWorkspace’s runningApplications()
  
set appList to appArray’s ASify() as list
  
  
repeat with i in appList
    set anID to (i’s bundleIdentifier()) as string
    
if anID = aBundleID then
      return true
    end if
  end repeat
  
  
return false
end chkAppProcesByBundleID

★Click Here to Open This Script 

2015/02/08 指定アプリケーションの存在確認

Bundle IDで指定したアプリケーションの存在確認を行うAppleScriptです。

古くから(Classic Mac OSの時代から)、AppleScriptにはFinder経由でアプリケーションの存在確認を行う手段が用意されてきました。いまはBunde IDで、Classic Mac OS時代にはcreator codeで存在確認を行ってきましたが、同様にFinderに対して問い合わせを行っていました。

AppleScript名:指定アプリケーションの存在確認(AS)
use AppleScript version “2.4″
use scripting additions

set aRes to chkAppIsInstalled(“com.adobe.photoshop”)
–> true

on chkAppIsInstalled(aBundleID as string)
  try
    tell application “Finder”
      set a to application file id “com.adobe.photoshop”
    end tell
    
return true
  on error
    return false
  end try
end chkAppIsInstalled

★Click Here to Open This Script 

そのため、一般的にはFinderで確認する方法で問題はないのですが、もしもMac AppStoreに申請を行うASOCのアプリケーションでFinder経由の確認を行っていた場合に、唐突に「この方法ではダメ」と言われかねません。

そこで、Cocoaの機能を利用して確認を行う方法について調査しておきました。速度面でとくに通常のASにくらべて有利ということはありませんが、SandBox内での実行が制限されづらいだろうということで書いておきました。

実際、有名なところではログイン時に自動起動されるStartup ItemsにAppleScript経由で登録する方法を採用すると、Mac App Store申請時にリジェクトされるという制限が知られています。

AppleScript名:指定アプリケーションの存在確認(ASOC)
– Created 2014-12-23 by Takaaki Naganoya
– 2014 Piyomaru Software
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “AppKit”

set aRes to chkAppIsInstalled(“com.adobe.photoshop”)
–>  true

on chkAppIsInstalled(aBundleID as string)
  set aRes to current application’s NSWorkspace’s sharedWorkspace()’s URLForApplicationWithBundleIdentifier:aBundleID
  
set bRes to (aRes is not equal to missing value)
  
return bRes
end chkAppIsInstalled

★Click Here to Open This Script 

2014/11/17 Finder Windowを円運動 v3

FinderのWindowを新規作成してぐるぐる回すAppleScriptです。

あまり手間をかけずに、複数Windowの回転に対応させました。

当初は、内部でスレッドを発生させて1Windowあたり1スレッドで管理するなど、いろいろ実装方法を検討してみましたが・・・何も、こんな意味のないくだらないScriptにそこまで血眼になる必要はないので、一番楽ができそうな方法で実現してみました。

プロパティ「maxWin」にWindow枚数を設定しておくと、その数だけFinder Windowを作成し、回転させます。手元のMacBook Pro Retina Mid 2012(寝床でバッテリ駆動中)では8ぐらいが上限っぽい感じでしたが(きれいに見える上限)、さらに強力なマシンでは数を増やして実行してみるとよいでしょう。

wins.png

AppleScript名:Finder Windowを円運動 v3
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AppKit” – for NSScreen

property aNum : 300
property aOffset : 0
property maxWin : 8
property winList : {}
property randList : {}
property winSize : 400

–Initialize
set winList to {}
set randList to {}

repeat maxWin times
  tell application “Finder”
    activate
    
set aWin to make new Finder window
    
    
tell aWin
      set toolbar visible to false
      
set sidebar width to 0
      
set statusbar visible to false
      
set position to {100, 100}
      
set bounds to {100, 100, 100 + winSize, 100 + winSize}
    end tell
    
    
set the end of winList to aWin
    
set the end of randList to {random number from 0 to 400, random number from 0 to 400, random number from 1 to 360}
    
  end tell
end repeat

–Main
repeat with i from 1 to 360 by 6
  
  
repeat with ii from 1 to maxWin
    
    
set aWinObj to contents of item ii of winList
    
set {aRandX, aRandY, aRandDegree} to item ii of randList
    
    
set aDeg to i + aRandDegree
    
    
set aSinNum to (current application’s SMSFord’s sinValueOf:aDeg) as real
    
set aCosNum to (current application’s SMSFord’s cosValueOf:aDeg) as real
    
    
set x to ((aNum * aSinNum) + aNum) * 2.0 + aOffset
    
set y to (aNum * aCosNum) + aNum + aOffset
    
    
ignoring application responses
      tell application “Finder”
        tell aWinObj
          set position to {(x as integer) + aRandX, (y as integer) + aRandY}
        end tell
      end tell
    end ignoring
    
  end repeat
  
end repeat

–Sweep
tell application “Finder”
  repeat with i in winList
    tell i
      close
    end tell
  end repeat
end tell

★Click Here to Open This Script 

2014/11/17 Finder Windowを円運動 v2

FinderのWindowを新規作成してぐるぐる回すAppleScriptです。

なぜか、海外から好評を博して、びみょーにアップデートしました。

変更点:
Finderをactivateするようにした(そんなに変わらない気も、、、)
Satimage OSAXの予約語とコンフリクトを起こすasin, acosの変数名を変更した

複数スレッドを生成して、複数のFinder Windowをズラしながらぐるぐる回せると「やったな!」という感じがしていいかもしれません。自己満足ですが・・・。

AppleScript名:Finder Windowを円運動 v2
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AppKit” – for NSScreen

property aNum : 300
property aOffset : 0

set {newX, newY} to (current application’s NSScreen’s mainScreen()’s frame()’s |size|()) as list

tell application “Finder”
  activate
  
set aWin to make new Finder window
end tell

repeat with i from 1 to 360 by 6
  
  
set aSinNum to (current application’s SMSFord’s sinValueOf:i) as real
  
set aCosNum to (current application’s SMSFord’s cosValueOf:i) as real
  
  
set x to ((aNum * aSinNum) + aNum) * 2.0 + aOffset
  
set y to (aNum * aCosNum) + aNum + aOffset
  
  
tell application “Finder”
    tell aWin
      set position to {x as integer, y as integer}
    end tell
  end tell
  
end repeat

tell application “Finder”
  tell aWin
    close
  end tell
end tell

★Click Here to Open This Script 

2014/11/17 Finder Windowを円運動

FinderのWindowを新規作成してぐるぐる回すAppleScriptです。

実行にはASObjCExtras.frameworkが必要なので、事前にインストールが必要です。楕円軌道でぐるぐるFinder Windowを回します。それだけです。

この手の、意味がぜんぜんないAppleScriptというのが割とエンドユーザー受けがよかったりするので、載せておきます。

Classic Mac OSの時代に、システムフォルダ内の名前に「A」を含むフォルダを開閉させる「open a」という意味のないScriptを書いてデモしたら、何かものすごい処理をしているように見えて受けがよかった、ということがありました。

ASObjCExtras.frameworkの三角関数機能を利用しています。一応、メイン画面のサイズを取得して円運動の大きさとかオフセット値とか扁平率に反映しようかと思ったものの、とくによいアイデアが思い浮かばなかったので、求めてそれっきり放置状態です。

img_0246_resized.png

1994年ごろに購入した「The Tao Of AppleScript」(日本語訳版)にFinderのウィンドウを四角く移動させるScriptが掲載されており、自分はこれを見て「なんて意味のない処理なんだ!」と呆れて登場当時のAppleScriptへの興味を失ってしまいましたが、他のユーザーがこの円運動Scriptを見て呆れないか心配です(^ー^;;

AppleScript名:Finder Windowを円運動
use AppleScript version “2.4″
use scripting additions
use framework “Foundation”
use framework “ASObjCExtras”
use framework “AppKit” – for NSScreen

property aNum : 300
property aOffset : 0

set {newX, newY} to (current application’s NSScreen’s mainScreen()’s frame()’s |size|()) as list

tell application “Finder”
  set aWin to make new Finder window
end tell

repeat with i from 1 to 360 by 6
  
  
set aSin to (current application’s SMSFord’s sinValueOf:i) as real
  
set aCos to (current application’s SMSFord’s cosValueOf:i) as real
  
  
set x to ((aNum * aSin) + aNum) * 2.0 + aOffset
  
set y to (aNum * aCos) + aNum + aOffset
  
  
tell application “Finder”
    tell aWin
      set position to {x as integer, y as integer}
    end tell
  end tell
  
end repeat

tell application “Finder”
  tell aWin
    close
  end tell
end tell

★Click Here to Open This Script 

2014/09/12 選択中のスクリーンショットから必要なもの以外削除

Shift-Command-3で作られる画面のスクリーンショットの画像ファイルを選択しておき、複数ディスプレイ接続時にできる「本来は必要ではない別のディスプレイ上のスクリーンショット」を削除するために作ったAppleScriptです。

Mac OS XではShift-Command-3で画面表示内容のスクリーンショットを画像に保存できます(常識レベルのお話)。これが、1つのディスプレイだけだと、

スクリーンショット 2014-09-12 13.01.59

といったファイル名で、デスクトップに日時を反映させたファイル名で作成されるわけですが、

bad_disp_arrange.png

といったように、複数台のディスプレイを接続している場合には、

・プライマリディスプレイ
スクリーンショット 2014-09-12 13.01.59

・セカンダリディスプレイ
スクリーンショット 2014-09-12 13.01.59(2)

といったように、プライマリ「以外の」ディスプレイのスクリーンショットにはその番号が末尾に付属します。OS Xでは6台までのディスプレイを認識するため、この数字の最大数は6ということになります。

きょうび、たいていのアプリケーションの表示は1台のディスプレイ内で完結していますので(昔みたいに、複数台のディスプレイにまたがった表示はしないので)、特定のアプリケーションのスクリーンショットをとっておきたい場合には、余計なディスプレイのスクリーンショットは「ゴミとして捨てる」場合がほとんどです。

そこで、デスクトップ上のスクリーンショットのファイルを選択しておいて、

screnshot1.png

本AppleScriptを実行。

screenshot2.png

どのディスプレイ用のスクリーンショットを残すか聞いてきますので、選択すると指定したディスプレイ以外のスクリーンショットはゴミ箱に移動されます。

Finder上で選択したファイルが「画面のスクリーンショットかどうか」の判定まではしていないので、不安であればそうした処理も追加するとよいでしょう。

スクリプト名:選択中のスクリーンショットから必要なもの以外削除
–選択中のファイル一覧をalias listで取得
tell application “Finder”
  activate
  
set aList to selection as alias list
end tell

–接続中のディスプレイをかぞえる
tell application “System Events”
  set dCount to count every desktop
end tell

–選択肢の作成
set dnList to {}
repeat with i from 1 to dCount
  set the end of dnList to (“(” & i as string) & “)” –全角カッコを指定「(」、[)」
end repeat

–残す項目を選ぶ
set aRes to choose from list dnList with prompt “どの画面のスクリーンショットを残しますか?” default items {contents of first item of dnList} with title “不要な画像をゴミ箱へ” without empty selection allowed
if aRes = false then
  display dialog “処理を中止します。” buttons {“OK”} giving up after 10 with title “キャンセルされました” with icon 2
  
return
end if

–選択された項目が何アイテム目かをサーチ(そういう命令があったかも、、、)
set aaRes to contents of first item of aRes

set aResNum to 1
repeat with i in dnList
  set j to contents of i
  
if j = aaRes then
    exit repeat
  end if
  
set aResNum to aResNum + 1
end repeat

–log {”aResNum”, aResNum}

if aResNum = 1 then
  –プライマリディスプレイ上のスクリーンショットを残すことを選択
  
set delList to (rest of dnList)
  
  
repeat with i in aList
    
    
tell application “Finder”
      set j to name of i
    end tell
    
    
–ファイル名に削除対象文字を含んでいるかどうかチェック
    
repeat with ii in delList
      set jj to (contents of ii) as string
      
–log {jj, j}
      
      
if j contains jj then
        tell application “Finder”
          delete i
        end tell
      end if
      
    end repeat
    
  end repeat
  
else
  –セカンダリディスプレイ上のスクリーンショットを残すことを選択
  
repeat with i in aList
    tell application “Finder”
      set j to name of i
    end tell
    
    
if j does not contain aaRes then
      tell application “Finder”
        delete i
      end tell
    end if
  end repeat
end if

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2014/09/03 ファイル名の冒頭部分でフォルダを作成してフォルダ分け

ファイル名が「xxxxx_yyyyyyyy.xxx」などとなっている場合に、ファイル名の冒頭部分の「xxxxx」でフォルダを新規作成し、ファイル名冒頭が同じファイルを作成したフォルダに移動させるAppleScriptです。

大量の画像を含むフリー画像素材集をダウンロードして、そのままだと整理しづらかったのでささっとScriptを書いて整理してみました。

finder_file_sep.png

……ささっと書いたのはよかったのですが、なぜか実行にやたらと時間がかかり…………OSのバグか? などと思ってみたものの、DiskUtility.appを実行してSSDの権限修復などをいったん行ってから実行したら、すぐに処理が終わるようになりました。

もしも、AppleScriptでファイル処理を行ってみて、異様なほど時間がかかる場合には(処理にもよりますが)、ディスクユーティリティーを実行してSSD/HDDのチェックを行ってみてください。

こういうScriptを書いて走らせていると、つくづくHDDの時代は終わったんだなーと感じます。

スクリプト名:ファイル名の冒頭部分でフォルダを作成してフォルダ分け
script spd
  
  
property aList : missing value –ファイルを入れる(alias list)
  
property cList : missing value –フォルダ名を入れる
  
end script

set aList of spd to {}
set cList of spd to {}

set aFol to choose folder with prompt “フォルダを選択してください”
set aFolStr to aFol as string

tell application “Finder”
  tell folder aFolStr
    set aList of spd to every file as alias list
  end tell
  
  
repeat with i in (aList of spd)
    
    
set j to contents of i
    
set aName to name of j
    
set fName to getFolNameBeforeSeparator(aName, “_”) of me
    
    
set tFol to aFolStr & fName & “:”
    
    
if fName is in (cList of spd) then
      –フォルダが存在する場合
      
ignoring application responses
        move j to folder tFol
      end ignoring
      
    else
      –フォルダが存在しない場合
      
set the end of (cList of spd) to fName
      
      
if not (exists of (folder tFol)) then
        set newFol to make new folder with properties {name:fName} at folder aFolStr
        
do shell script “sync”
      end if
      
      
ignoring application responses
        move j to folder tFol
      end ignoring
      
    end if
    
  end repeat
  
  
end tell

–与えられた文字列(たぶんファイル名)のうち、先頭から指定セパレータまでの間をフォルダ名として返す
on getFolNameBeforeSeparator(aName, aSeparator)
  set aLoc to offset of aSeparator in aName
  
if aLoc = 0 then
    return “”
  else
    set aStr to text 1 thru (aLoc - 1) of aName
    
return aStr
  end if
end getFolNameBeforeSeparator

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2014/07/23 Script Editorで最前面のアプリの用語辞書を表示する v3

AppleScriptエディタ/Script Editorで、最前面のアプリの用語辞書を表示するAppleScriptです。

バージョンアップして、最前面のアプリがAppleScript等のOSA(Open Scripting Architecture)言語によるScriptingに対応しているかどうかを判定したのちに、辞書のオープンを行います。

Script Menuに入れて、メニューから呼び出すことを前提としています。

前バージョンでは、とくに対象アプリケーションのScripting対応については調べていなかったため、非対応アプリを指定すると、Script Editorで対象アプリをバイナリモードでオープンしてしまうなどの問題があり、それに対処したものです。

本AppleScriptは、各アプリケーションのバンドル中にあるInfo.plistを走査してOSA対応の判定を行っていますが、これは「NSAppleScriptEnabled」のエントリがInfo.plist上に存在した場合、慣習的に「true」しか指定されていない(そもそも非対応である場合にはNSAppleScriptEnabledのエントリそのものを作らない)ことから、「NSAppleScriptEnabled」が入っていれば対応……という「手抜き判定」をやっています。

真剣に作るのであれば、NSAppleScriptEnabledのエントリを取得して属性値がどうなっているか判定すべきです。

スクリプト名:Script Editorで最前面のアプリの用語辞書を表示する v3
–最前面のプロセスのファイルシステム上の位置を取得してaliasに変換する
tell application “System Events”
  set frontProc to every process whose frontmost is true and visible is true
  
set aProc to contents of first item of frontProc
  
set aFile to file of aProc
  
set aBundleID to bundle identifier of aProc
end tell

set aFile to aFile as alias

–バンドルパッケージ中のInfo.plistファイルを走査してAppleScript対応かどうかを調べる
set aFilePosix to quoted form of POSIX path of aFile
set infoPath to aFilePosix & “Contents/Info.plist”
set aRec to extractInfoPlistAndFindString(infoPath, “NSAppleScriptEnabled”)

–スクリプト用語辞書をScript Editorでオープンする手順
if aRec = true then
  –OS X 10.10でScript Editor側からアプリをオープンしてもダメだったので、Finder側からScript Editorで指定アプリをオープンすることに
  
tell application “Finder”
    set apFile to (application file id “com.apple.scripteditor2″) as alias
    
open aFile using application file apFile
  end tell
else
  tell application id aBundleID
    display dialog “本アプリケーションは、各種OSA言語によるコントロールに対応していません。” buttons {“OK”} default button 1 with icon 2 with title “Scripting非対応”
  end tell
end if

–指定のInfoPlist(たぶん)をextractして指定文字列を含んでいるかチェック
on extractInfoPlistAndFindString(aPosix, findStr)
  
  
try
    set a to (do shell script “/usr/bin/plutil -p “ & aPosix)
  on error
    return 0 –エラー(指定のパスにファイルが存在しない)
  end try
  
  
set b to (a contains findStr)
  
return b
  
end extractInfoPlistAndFindString

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2014/07/22 AS対応、非対応のアプリ数をカウントする

Macのローカルに存在するアプリケーションをすべてリストアップし、AppleScript対応のもの、AppleScript非対応のもの、Info.plistが所定の位置にないものをカウントして返すAppleScriptです。

下調べのために作ってみました。

すぐに「entire contentsで指定フォルダ以下のファイルをすべて取得してアプリのみ抽出するバージョン」に作り替えていますが……まだ、処理が返ってきません、、、

スクリプト名:AS対応、非対応のアプリ数をカウントする
script spd
  
  
property aList1 : {} –/Applications フォルダ以下のアプリケーションファイル一覧
  
property aList2 : {} –/Applications/Utilities フォルダ以下のアプリケーションファイル一覧
  
  
property bList : {} –aList1とaList2を連結
  
  
property asEnable : {} –AS対応のアプリ一覧
  
property asDisable : {} –AS非対応のアプリ一覧
  
property infoNotPresent : {} –Info.plistが所定の場所に指定のファイル名で存在しないもの一覧
  
end script

set aList1 of spd to {}
set aList2 of spd to {}

set bList of spd to {}

set asEnable of spd to {}
set asDisable of spd to {}
set infoNotPresent of spd to {}

set apPath1 to (path to applications folder) as string
set apPath2 to (path to utilities folder) as string

tell application “Finder”
  –/Applications Folder
  
tell folder apPath1
    set aList1 of spd to (every application file) as alias list
  end tell
  
  
–/Applications/Utilities Folder
  
tell folder apPath2
    set aList2 of spd to (every application file) as alias list
  end tell
end tell

set bList of spd to (contents of (aList1 of spd)) & (contents of (aList2 of spd))

repeat with i in (bList of spd)
  
  
set aFilePosix to quoted form of POSIX path of i
  
set infoPath to aFilePosix & “Contents/Info.plist”
  
  
set aRec to extractInfoPlistAndFindString(infoPath, “NSAppleScriptEnabled”)
  
  
if aRec = true then
    set the end of (asEnable of spd) to i
  else if aRec = false then
    set the end of (asDisable of spd) to i
  else
    set the end of (infoNotPresent of spd) to i
  end if
end repeat

return {length of (asEnable of spd), length of (asDisable of spd), length of (infoNotPresent of spd)}

–> {125, 235, 0}

–指定のInfoPlist(たぶん)をextractして指定文字列を含んでいるかチェック
on extractInfoPlistAndFindString(aPosix, findStr)
  
  
try
    set a to (do shell script “/usr/bin/plutil -p “ & aPosix)
  on error
    return 0 –エラー(指定のパスにファイルが存在しない)
  end try
  
  
set b to (a contains findStr)
  
return b
  
end extractInfoPlistAndFindString

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2014/05/26 Finderのフィルタ参照にファイル名の0を無視するバグ

US AppleがホスティングしているAppleScript Users MLで話題になっていた件で、あとからMLの話題に気付いて自分でも確認してみました。

フォルダ中に、

  Test_1.png
  Test_01.png
  Test_001.png
  Test_0001.png
  Test_00001.png

などのファイルが存在している状態で、

スクリプト名:ファイルの存在確認 10.9
set aFol to choose folder
set aFolStr to aFol as string

tell application “Finder”
  tell folder aFolStr
    set aList to name of every file whose name is “Test_1.png”
  end tell
end tell
–> {”Test_00001.png”, “Test_0001.png”, “Test_001.png”, “Test_01.png”, “Test_1.png”}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

といったAppleScriptを実行すると…………すべてのファイルが取得できてしまいます。

数字の後ろに「0」が付いた場合は同一視しないのですが(例:「Test_10.png」はきちんと区別する)、正確にいえばプレフィックスの「0」を無視するという症状のようです(逆に、高度かつ余計な処理を行ってしまっているような……)。

実戦レベルのAppleScriptでは、たいてい1つのフォルダだけの内容を取得することは少なくて……指定フォルダ以下のすべての階層のフォルダからファイルを取得して抽出、といった処理が多いので、逆に単純な処理すぎて見過ごしていました。

なお、本症状はMac OS X 10.6.8、10.7.5、10.8.5、10.9.3で確認されています(それ以下のOSバージョンはさすがに実行環境が……)。

迂回方法

いくつか考えられます。do shell script経由でファイル名を取得して文字列比較を行えば、とくに問題はありません。フォルダ中に入っているファイル数が数千とか数万のオーダーにのぼる場合には、do shell script経由で取得して判断しています。

ただ……そこまで大げさではない場合には、ちょっと書き換えれば大丈夫です。

スクリプト名:ファイルの存在確認 10.9_recover
set aFol to choose folder
set aFolStr to aFol as string

tell application “Finder”
  tell folder aFolStr
    set aList to name of every file whose name is “Test_1.png” and name ends with “Test_1.png”
  end tell
end tell
–> {”Test_1.png”}

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に

2014/05/13 選択中のフォルダ内のすべてのファイルの作成日、変更日をcurrent dateに変更 v2

Finder上で選択中のフォルダ内のすべてのファイル/フォルダの作成日、変更日を現在時刻に変更するAppleScriptの、OS X 10.9対応版です。

実行のためには、Xcodeのインストールが必要です(/usr/bin/SetFileコマンド)。

currentdater.png

Script Menuに入れて、サンプルのXcodeプロジェクトの作成日/修正日の日付をまとめて変更するために作ったものです。

スクリプト名:選択中のフォルダ内のすべてのファイルの作成日、変更日をcurrent dateに変更 v2
set aCurDate to current date
set aDateStr to makeMMDDYYYYhhmmssStr(aCurDate) of me

tell application “Finder”
  set aSel to selection as alias list
  
if aSel = {} or aSel is equal to missing value then
    display dialog “何も選択されていません。” buttons {“OK”} default button 1
    
return
  end if
end tell

repeat with i in aSel
  
  
set j to contents of i
  
set aKind to detectFolder(j) of me
  
  
–選択中のアイテムがフォルダの場合にはその中のファイル一覧を取得
  
set fList to {}
  
  
if aKind = true then
    tell application “Finder”
      tell folder (j as string)
        set fList to entire contents as alias list
      end tell
    end tell
  else
    –選択中のアイテムがファイルの場合にはそのままリストに入れる
    
set fList to {j}
  end if
  
  

  
repeat with i in fList
    set j to contents of i
    
set aFres to detectFolder(i) of me
    
changeCreationDate(j, aDateStr) of me
  end repeat
  
end repeat

–aliasがFolderかどうか判定
on detectFolder(aSelFol)
  set aInfo to info for aSelFol as alias
  
tell application “Finder”
    try
      set aFol to kind of aInfo
    on error
      return false
    end try
  end tell
  
  
return (aFol = “フォルダ”) –”Folder” in Japanese, change here to each localized string
end detectFolder

–作成日と修正日を変更する
on changeCreationDate(aFile, aDateStr)
  set bP to POSIX path of aFile
  
  
try
    do shell script “/usr/bin/SetFile -d “ & quoted form of aDateStr & ” “ & quoted form of bP –作成日
    
do shell script “/usr/bin/SetFile -m “ & quoted form of aDateStr & ” “ & quoted form of bP –修正日
  end try
  
end changeCreationDate

–DateオブジェクトからMM/DD/YYYY hh:mm:ssの形式の文字列を返す
on makeMMDDYYYYhhmmssStr(aDate)
  –Dateオブジェクトから各要素を取り出す
  
set yStr to (year of aDate) as string
  
set mStr to (month of aDate as number) as string
  
set dStr to (day of aDate) as string
  
set hhStr to (hours of aDate) as string
  
set mmStr to (minutes of aDate) as string
  
set ssStr to (seconds of aDate) as string
  
  
–桁数を合わせる
  
set y2Str to retZeroPaddingText(yStr, 4) of me
  
set m2Str to retZeroPaddingText(mStr, 2) of me
  
set d2Str to retZeroPaddingText(dStr, 2) of me
  
set hh2Str to retZeroPaddingText(hhStr, 2) of me
  
set mm2Str to retZeroPaddingText(mmStr, 2) of me
  
set ss2Str to retZeroPaddingText(ssStr, 2) of me
  
  
return (m2Str & “/” & d2Str & “/” & y2Str & ” “ & hh2Str & “:” & mm2Str & “:” & ss2Str)
end makeMMDDYYYYhhmmssStr

–数値にゼロパディングしたテキストを返す
on retZeroPaddingText(aNum, aLen)
  set tText to (“0000000000″ & aNum as text)
  
set tCount to length of tText
  
set resText to text (tCount - aLen + 1) thru tCount of tText
  
return resText
end retZeroPaddingText

▼新規書類に ▼カーソル位置に ▼ドキュメント末尾に