bashの変数展開によるファイル名や拡張子の取得

  • 53
    いいね
  • 0
    コメント

沢山のファイルの名前を変更したり、拡張子を変更する際には、シェル上での操作が便利なことがあります。この場合、ファイル名や拡張子、ファイル名から拡張子を取り除いたものを取得できると便利です。

basenameとdirnameの利用

MacOSやLinuxではbasenameコマンドやdirnameコマンドを利用すると、パス名からのファイル名の取り出しやパス名からのディレクトリ部分の取り出しが出来ます。この2つのコマンドの動作は

$ basename "/foo/bar/baz.txt"
baz.txt
$ dirname "/foo/bar/baz.txt"
/foo/bar

となっています。次のようなシェルスクリプトを実行すると、

#!/bin/sh

var="/foo/bar/baz.txt"
basename $var
dirname $var

次のような実行結果となります。

baz.txt
/foo/bar

ファイル名からの拡張子の取り除きや拡張子の取得

sedコマンドを利用した場合

sedコマンドを使うと、ファイル名からの拡張子の取り除きができます。例えば、「's/.[^.]*$//'」というパターンでsedコマンドを使えばファイル名からの拡張子の取り除きが出来ます。「.ではじまっていてい.以外の文字が最後まで続く」を空文字列に置き換えると言った意味になっています。

$ ls
a.bak   a.txt   b.c.txt b.doc
$ for f in * ; do 
>  echo $f | sed 's/\.[^\.]*$//'
> done
a
a
b.c
b

sedコマンドのパターンを「's/^..([^.])$/\1/'」に変えると拡張子だけを取り出すことができます。拡張子を持っていないファイル名のファイルに対しては、変更が行われないので、注意が必要です。

for f in * ; do
>  echo $f | sed 's/^.*\.\([^\.]*\)$/\1/'
> done
bak
txt
txt
doc

シェルの変数展開を利用した場合

いちいちsedコマンドを利用するのは面倒な気がします。シェルの持っている変数展開の機能を利用するともう少し簡単にできるようになります。簡単に言うと、変数展開とは変数に保存されいる文字列を置き換えることです。下の表のようなことができます。他にもありますが、省いてあります。

変数展開の記述 動作    
${変数名#パターン} 前方一致での削除(最短マッチ)
${変数名##パターン} 前方一致での削除(最長マッチ)
${変数名%パターン} 後方一致での削除(最短マッチ)
${変数名%%パターン} 後方一致での削除(最長マッチ)
${変数名/置換前文字列/置換後文字列} 文字列置換(最初に一致したもののみ)
${変数名//置換前文字列/置換後文字列} 文字列置換(一致したものすべて)

注:最初の4つの#や%を使った変数展開は/bin/shでも利用することができます。

拡張子の取り除きは「後方一致のでの削除(最短マッチ)」を行えば、拡張子を取り除くことができます。つまり「${変数名%.*}」という変数展開を利用します。下に実行例を示します。

$ for f in * ; do
> echo ${f%.*}
> done
a
a
b.c
b

変数の値が"/foo/bar/bar.txt"となっている場合には、"/foo/bar/bar"となります。

例えば、あるフォルダ内にあるファイル名の拡張子「cvs」となっていものを拡張子「.txt」に変更するためには、

$ for f in *.cvs; do
>  mv $f ${f%.*}.txt
> done

とすれば可能です。これは、

$ for f in *.cvs; do
>  mv $f ${f/.cvs/.txt/}
>done

でも可能です。

「${変数名##*/}」という変数展開を利用すると、basenameコマンドのようにパス名からファイル名だけを取り出すことができます。例えば、次のようになります。

$ for f in ./junk/*;do 
>   echo $f
> done
./junk/a.bak
./junk/a.txt
./junk/b.c.txt
./junk/b.doc
$ for f in ./junk/*;do 
> echo ${f##*/}
>done
a.bak
a.txt
b.c.txt
b.doc

「${変数名##*.}」という変数展開を利用すると、拡張子だけを取り出すことができます。ただし、拡張子を持っていないファイル名のファイルに対しては、変更が行われないので、注意が必要です。

$ for f in ./junk/*; do 
>  echo $f
> done
./junk/a.bak
./junk/a.txt
./junk/b.c.txt
./junk/b.doc
./junk/c
$ for f in ./junk/*; do 
>  echo ${f##*.}
> done
bak
txt
txt
doc
/junk/c

一気にファイル名などを変更する際には便利な機能ですが、思ったように変数展開ができないと、面倒なことになります。自信がない場合には、echoなどを利用して変数展開の結果を確認してから、実行して下さい。

まとめ

bashやshの変数展開を利用したパス名からのファイル名の取り出しなどは、以下のようにすれば可能です。

  • ファイル名の取得:${変数名##*/}
  • 拡張子の削除:${変数名%.*}
  • 拡張子の取得:${変数名##*.}