Hatena::ブログ(Diary)

ザリガニが見ていた...。 このページをアンテナに追加 RSSフィード

2010-09-21

ファイルパスからファイル名や拡張子を自由に取り出す

いつも、いざ使う時になって、どうやるんだっけ?と探し回ってしまうので、忘れないようにメモ。(以前の日記でも、所々分散して書いているかもしれないが、ここで総括的に記録することにした)

シェルスクリプト

$ fpath='/a/b/c.d.e'

# ファイル名を取り出す(拡張子あり)
$ fname_ext="${fpath##*/}"
$ echo $fname_ext
c.d.e

# ファイル名を取り出す(拡張子なし)
$ fname="${fname_ext%.*}"
$ echo $fname
c.d

# 拡張子を取り出す
$ fext="${fpath##*.}"
$ echo $fext
e

# ディレクトリを取り出す
$ fdir="${fpath%/*}"
$ echo $fdir
/a/b

なるほど!


そして、仕組みとしては、以下のルールしたがって変換されているようだ。

${変数#パターン}	先頭から最短一致した部分を取り除く
${変数##パターン}	先頭から最長一致した部分を取り除く
${変数%パターン}	末尾から最短一致した部分を取り除く
${変数%%パターン}	末尾から最長一致した部分を取り除く

複合技としてファイルパスの拡張子だけ変更するなんてことも簡単なのだ。

$ echo "${fpath%.*}.x"
/a/b/c.d.x

また、ファイル名とディレクトリだけなら、専用コマンドも用意されてる。

$ basename '/a/b/c.d.e'
c.d.e

$ dirname '/a/b/c.d.e'
/a/b

AppleScript

  • 自分自身のファイルパスを取得

 path to me 
--alias "Macintosh HD:Users:zari:Library:Scripts:test_path_to.me.scpt"


  • ファイル名を取得

 tell application "Finder" to (path to me)'s name 
--"test_path_to.me.scpt"


  • ディレクトリを取得

 tell application "Finder" to (path to me)'s folder as text 
--"Macintosh HD:Users:zari:Library:Scripts:"


  • UNIX形式のパスに変換

 (path to me)'s POSIX path 
--"/Users/zari/Library/Scripts/test_path_to.me.scpt"


  • AppleScript形式のパスに変換

 POSIX file "/Users/zari/Library/Scripts/test_path_to.me.scpt" 
--file "Macintosh HD:Users:zari:Library:Scripts:test_path_to.me.scpt"


  • ファイル名と拡張子に分解するのはちょっと面倒くさい...。

 tell application "Finder" to set fname_ext to (path to me)'s name
 set my text item delimiters to "."
 set fext to fname_ext's text items's item -1 --"scpt"
 set fname to fname_ext's text items's items 1 thru -2 as text --"test_path_to.me"
 set my text item delimiters to ""


もっとシンプルに書けたのであった。(感謝です!)

fnameが、fname_ext・fextに依存しないように修正。

 tell application "Finder"
   set select_list to selection
   repeat with f in select_list
     set fname_ext to f's name --ファイル名&拡張子
     set fext to f's name extension --拡張子
     --set fname to fname_ext's characters 1 thru -((count fext's character) + 2) as text --ファイル名
     set fname to (f's name as text)'s items 1 thru -(((f's name extension as text)'s length) + 2) as text --ファイル名
     set fdir to f's folder as alias --ディレクトリ
   end repeat
 end tell
 
 fname_ext & return & fext & return & fname & return & fdir

AppleScriptからシェルスクリプトを呼び出してしまう方法

 set fpath to (path to me)'s POSIX path
--"/Users/zari/Library/Scripts/test_path_to.me.scpt"

 set fname_ext to do shell script "n=" & fpath's quoted form & ";echo \"${n##*/}\""
--"test_path_to.me.scpt"

 set fname to do shell script "n=" & fname_ext's quoted form & ";echo \"${n%.*}\""
--"test_path_to.me"

 set fext to do shell script "n=" & fpath's quoted form & ";echo \"${n##*.}\""
--"scpt"

 set fdir to do shell script "n=" & fpath's quoted form & ";echo \"${n%/*}\""
--"/Users/zari/Library/Scripts/"

      • 半角¥は半角\に置き換える

Ruby

$ irb
irb(main):001:0> File::basename('/a/b/c.d.e')
=> "c.d.e"
irb(main):002:0> File::basename('/a/b/c.d.e', '.*')
=> "c.d"
irb(main):003:0> File::extname('/a/b/c.d.e')
=> ".e"
irb(main):004:0> File::dirname('/a/b/c.d.e')
=> "/a/b"

なるほど!(Rubyが最も手間最小で自然な文法だと思った)

AppleScriptからRubyを呼び出してしまう方法

 set fpath to "/a/b/c.d.e"
 do shell script "ruby -e \"puts File::basename(" & fpath's quoted form & ")\""
 do shell script "ruby -e \"puts File::basename(" & fpath's quoted form & ", '.*')\""
 do shell script "ruby -e \"puts File::extname(" & fpath's quoted form & ")\""
 do shell script "ruby -e \"puts File::dirname(" & fpath's quoted form & ")\""

      • 半角¥は半角\に置き換える

AppleScriptのライブラリにしておく

シェルコマンドと組み合わせて、使い回せる関数ライブラリにしてみた。

 --(*
 tell application "Finder"
   set select_list to selection
 end tell
 repeat with f in select_list
   my fname_ext(f as text)
   my fname(f as text)
   my fext(f as text)
   my fdir(f as text)
 end repeat
 --*)
 --ファイル名&拡張子を返す
 on fname_ext(f)
   tell application "Finder" to (f as alias)'s name
 end fname_ext
 
 --ファイル名のみ返す(拡張子なし)
 on fname(f)
   do shell script "n=" & my fname_ext(f)'s quoted form & ";echo \"${n%.*}\""
 end fname
 
 --拡張子を返す
 on fext(f)
   do shell script "n=" & f's POSIX path's quoted form & ";echo \"${n##*.}\""
 end fext
 
 --ディレクトリを返す
 on fdir(f)
   tell application "Finder" to (f as alias)'s folder as text
 end fdir

      • 半角¥は半角\に置き換える

bsdhackbsdhack 2010/09/22 09:59 はじめまして、
いつも楽しく拝見させて頂いています。

既にご存じかも知れませんが basename(1) コマンドは拡張子を除外する機能があって、これも便利です。

basename '/path/to/file' → file
basename '/path/to/file.ext' → file.ext
basename '/path/to/file.ext' .ext → file

Shell Script の中などで有用です

zariganitoshzariganitosh 2010/09/23 09:27 コメントありがとうございます!
そうでした、そういえば拡張子を除外する機能、ありましたよね。
書いてる時点ではすっかり忘れていましたが、今思い出しました。
以前、拡張子を直接入力して除外はできるけれど、
Rubyのようなワイルドカード指定はできない仕様だと気付いたのでした。
その時、basenameは忘れて、どんなパスでも抽出できるシェル変数のパターン展開を利用しようと決意したのでした。
でも、ワイルドカード使わなくてもいい状況も多々あるので、basenameも便利ですよね。分かりやすいし。
ありがとうございました。

小泉小泉 2010/10/26 22:20 初めまして。色々為になります。

Apple Script だけで。

tell application "Finder"
set a1 to selection
repeat with a2 in a1
set a3 to name of a2 --ファイル名&拡張子
set a4 to name extension of a2 --拡張子
set a5 to a3's characters 1 thru ((count character of a3) - (count character of a4) - 1) as Unicode text --ファイル名
set a6 to container of a2 as alias --ディレクトリ
end repeat
end tell

zariganitoshzariganitosh 2010/10/27 05:28 コメントありがとうございます!
name extension、知りませんでした。
勉強になります。
これで拡張子が取り出せればずいぶん楽になりますね。
文字位置をマイナス指定にしてみました。

set a5 to a3's characters 1 thru -((count a4's character) + 2) as text --ファイル名

zariganitoshzariganitosh 2010/10/27 05:47 小泉さん、

name extensionを知れば、ライブラリなんて不要になる便利さなので、追記させてもらいました。
これからこの書き方、多用しそうです。

小泉小泉 2010/10/27 19:33 zariganitoshさん
コメントありがとうございます。

name extensionといえば、extension hiddenやdisplayed nameなんてのもできましたね。
たしかOS X 10.1か.2ぐらいだったような。

これを使えばこんな書き方もできますが、ファインダー上でファル名がポコポコするので、
あんまりスマートじゃ無いですね。character数を数えて計算ってのもスマートではありませんが。。

tell application "Finder"
set a1 to selection
repeat with a2 in a1
set extension hidden of a2 to true
copy properties of a2 to {name:a3, name extension:a4, displayed name:a5, container:a6}
set extension hidden of a2 to false
end repeat
end tell
--a3:ファイル名&拡張子、a4:拡張子、a5:ファイル名、a6:ディレクトリ

zariganitoshzariganitosh 2010/10/28 06:54 > copy properties of a2 to {name:a3, name extension:a4, displayed name:a5, container:a6}
たった一行で、欲しい情報が取得できてしまうんですね!
自分の場合、Finderの環境設定の「すべてのファイル名拡張子を表示」を入にしていたので、
最初はファイル名のみにならなかったのですが、それに気付いて切にしたらうまくいきました。
またしても素敵な方法を教えて頂きました。(ありがとうございます!)

いろいろ検証もしてみました。
- 環境に依存しないコードにするためには、Finderの環境設定を操作する必要もありそうですね。

- extension hidden属性は、拡張子によってtrue・falseがデフォルト設定されているようなので、変数にバックアップしておく必要もありそうです。
.appについては、falseにしても常にtrueが設定されていましたが、
.pngについては、falseにしてしまうとデフォルトのtureが失われてしまいました。

AppleScriptというのは、それぞれの環境にかなり依存してしまう所が、使いにくい所ですね。
やはり、文字数を数えてしまうのが確実かもしれません。
でも1行で書ける上記の方法は、とってもスマートです。
そのうち、without extension なんて属性が追加されないかな、なんて期待しちゃいます。(無理でしょう、笑)

小泉小泉 2010/10/28 11:47 「すべてのファイル名拡張子を表示」これが影響するんですね。
関係ないと勝手に思い検証してませんでした。。
.appとかもありますね。。

そうすると拡張子なしだけは前のやつを利用して(文字位置をマイナス指定使わせてもらいました)、こんな感じでよさそうです。

tell application "Finder" to repeat with a2 in selection as alias list
copy properties of a2 to {name:a3, name extension:a4, container:a6}
set a5 to a3's characters 1 thru -((count character of a4) + 2) as Unicode text
end repeat--a3:ファイル名&拡張子、a4:拡張子、a5:ファイル名、a6:ディレクトリ

「without extension」欲しいですねー。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

リンク元