Finderでファイルやフォルダのパスをコピーする

Finderでコピー&ペースト、あるいはドラッグ&ドロップした時の挙動は、標準では以下のように振る舞うと思っている。(自分の知る限り)

  • Finderでファイルやフォルダを選択してコピーした場合...
    • Finderに対するペーストであれば、ファイルやフォルダそのものがペーストされる。
    • 標準テキストとしてペーストする環境であれば、ファイル名やフォルダ名がペーストされる。
    • リッチテキストとしてペーストする環境であれば、ファイルやフォルダが添付書類*1としてペーストされる。
    • ターミナルに対するペーストであれば、ファイルやフォルダのパスがペーストされる。
  • Finderからファイルやフォルダをドラッグ&ドロップした場合...
    • 標準テキストとしてドロップする環境であれば、ファイルやフォルダのパスがドロップされる。
    • それ以外は、コピー&ペーストと同じ動作。

ここで感じる自分の希望としては、標準テキスト・リッチテキストどちらであっても、ファイルやフォルダのパスをペーストするようにしたいのだ。標準テキストとしてドロップする環境であれば、パスがドロップされるのだが、Spacesなデスクトップではドラッグ&ドロップという操作はすごく面倒だし、途中で誤って放してしまって失敗する可能性も高い...。
そこでいつものAppleScriptQuicksilverで希望を叶えたいと、以下のようにやってみた。

基本に忠実バージョン

  • 選択アイテムは複数になる可能性もあるので、repeatで繰り返し処理をした。
set theSel to (selection of application "Finder")
set pathList to {}
repeat with anItem in theSel
    set the end of pathList to (POSIX path of (anItem as alias))
end repeat
set savedDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to return
set the clipboard to pathList as string
set AppleScript's text item delimiters to savedDelimiters
  • 素早く簡単に出来て、目出たし目出たしと思っていたが、問題発生!
  • 今、自分のダウンロードフォルダを見ると、794項目ある。すべて選択して、上記スクリプトを実行してみる。
    • 30秒経過、CPUメーターはフル稼働、スクリプト処理中な感じ。
    • 60秒経過、同上。
    • 90秒経過、同上。スクリプトがフリーズしたかと思って強制終了しようとしたら、その時完了した模様。

遅すぎるのだ!どうしようもなく...。10件程度なら何も問題ないが、100件でイライラ、1000件なんてやる気にならない。処理していることを忘れてしまう...。Core2Duo 2.1GHzのMacBookがこのスピードでは泣けてくる。(実際、そんなに多くのパスをコピーする需要はほとんど無いのだが。)

ループなし高速バージョン

  • AppleScriptをループさせると、スクリプトの処理ステップに比例して遅くなると考え、repeatを利用しない工夫をしてみた。
set savedDelimiters to AppleScript's text item delimiters
tell application "Finder"
    set AppleScript's text item delimiters to return
    set sel to the selection as text
    
    --起動ディスク(例 Leopard HD:)のパスを:に置き換え
    set theDisk to (startup disk as alias) as text
    set AppleScript's text item delimiters to theDisk
    set sel to text items of sel
    set AppleScript's text item delimiters to ":"
    set sel to (sel as text)
    
    --先頭の":"を削除
    if sel starts with ":" then
        set sel to text 2 thru -1 of sel
    end if
    
    set the clipboard to POSIX path of sel
end tell
set AppleScript's text item delimiters to savedDelimiters
  • 効果は抜群で、同じ794項目のコピーをたった1秒で完了した!自分でもビックリ。処理速度は100倍くらい速くなった。満足。

クリップボード拡張スクリプトに組み込む

以前の日記で、複数のクリップボードを管理するスクリプトを作った。

その中で解決できない問題として、ファイルやフォルダを複数選択してコピーしてFinder内でペーストした場合、先頭の1アイテムしかコピー出来ない現象が発生していた。この現象はクリップボードの内容を変数に代入すると必ず発生してしまうことまでは分かったのだが、それを回避する方法は未だ解決できずにいる。

  • で、どうせ解決できないなら、クリップボード拡張スクリプトとしてはFinder内のファイルやフォルダのコピー&ペースト機能は無効にしてしまおうと。
  • その代わり、Finderでコピーした時には上記スクリプトを利用して、ファイルやフォルダのパスをコピーするようにした方が自分の需要にはマッチしている。
  • もし本当にファイルやフォルダ自体をコピーしたい時は、通常のcommand-C、command-Vで処理すればOK。
  • そもそも、ファイルやフォルダ自体を複数のクリップボードに保存して、後でまとめてペーストする需要なんてほとんど無さそう。

ということで、以下のように修正してみた。

  --clip1_copy.scpt
on clipName()
    tell application "Finder"
        set myName to name of (path to me) as text --自分自身のファイル名
        --「_」で区切ったリストに変換
        set text item delimiters of AppleScript to "_"
        set nameList to text items of myName
        set text item delimiters of AppleScript to ""
        
        "clip:" & (item 1 of nameList) & ".scpt"
    end tell
end clipName

on clipPath()
    ((path to scripts folder) as text) & clipName()
end clipPath

  --aText中のword1をword2に置き換える
on replace(aText, word1, word2)
    set savedDelimiters to AppleScript's text item delimiters
    set AppleScript's text item delimiters to word1
    set aList to text items of aText
    set AppleScript's text item delimiters to word2
    set res to aList as text
    set AppleScript's text item delimiters to savedDelimiters
    res
end replace

  --パスをコピーする
on copyPath()
    set savedDelimiters to AppleScript's text item delimiters
    tell application "Finder"
        set AppleScript's text item delimiters to return
        set path_text to the selection as text
        set disk_name to (startup disk as alias) as text
    end tell
    
    --起動ディスク(例 Macintosh HD:)のパスを:に置き換え
    set path_text to replace(path_text, disk_name, ":")
    
    --先頭の":"を削除
    if path_text starts with ":" then
        set path_text to text 2 thru -1 of path_text
    end if
    
    set the clipboard to POSIX path of path_text
    set AppleScript's text item delimiters to savedDelimiters
end copyPath

delay 0.2 --Quickslverで実行するにはひと呼吸必要だった
set current_clip to (the clipboard as record)
set CB to load script file clipPath()

tell application "System Events"
    set appName to (name of every process whose frontmost is true) as text
    
    --操作対象のアプリを最前面に設定する(スクリプトメニューから実行する場合必要)
    --tell process appName to set frontmost to true
    
    if appName is "Finder" then
        tell me to copyPath()
    else
        keystroke "c" using command down
    end if
    delay 0.5
end tell

set msg to replace(the clipboard as text, return, ", ")
do shell script "/usr/local/bin/growlnotify Copy -m '" & msg & "'"

  --set pb of CB to (the clipboard as «class rtfd») --リッチテキスト添付ファイルのみ
  --set pb of CB to (the clipboard as «class RTF ») --リッチテキスト
  --set pb of CB to the clipboard--テキストグループ
set pb of CB to (the clipboard as record) --すべて
  --clipboard info
  --do shell script "/usr/local/bin/growlnotify " & pb of CB

store script CB in file clipPath() replacing yes
delay 1
set the clipboard to current_clip

Path Finderの挙動

Finder以外のアプリケーションに対しては通常のcommand-Cが実行されるのだが、それではFinderと同等の機能を提供するPath Finderについてはどのように対応しようかと考えていたのだが、心配は無用であった。

  • 最初から自分が望む通りのコピー動作をしてくれていた。(コピー&ペースト操作で、標準テキスト・リッチテキストどちらでもパスをコピーする)

さすが、Path Finder。理想的な機能を実装している。いつも自分が欲しいと思う機能は、すでにPath Finderが実装しているのだ。隙がない。

ダウンロード

以下のリンクからダウンロードページへ移動します。

*1:保存した.rtfdファイルのパーッケージの内容を表示してみると、そこにコピーされたファイルやフォルダが存在する。