それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bashのパラメータのブレース展開での拡張子・ディレクトリ名・ファイル名の取得・拡張子の置換

bash のbrace 展開で文字列を置換する

文字の置換は、bashはとても楽ちんである。

NAME=www.example.com
echo ${NAME//./_} # www_example.com
echo ${NAME//./_} # www_example_com

これをもう少し掘り下げる。

bash でファイル名から拡張子を取出す方法

ファイル名から拡張子を取出すのはカンタンでした。

path=/etc/apache2/httpd.conf
extension=${path##*.}  #=> conf

魔法の記述方法 ${varname##*.} を使うと 手軽に拡張子を取得できる。

拡張子を書き換える方法

拡張子を書き換えるには、変数の書き換えを使えば直ぐ出来る。

f_name=httpd.conf
txt_name=${f_name/.*/.txt}   #=> httpd.txt

basename / dirname の取得

basename や dirname も文字列マッチで行うことが出来る。

path=/etc/apache2/httpd.conf
basename=${path##*/} #=> httpd.conf
dirname=${path%/*} #=>/etc/apache2

拡張子だけを取り除く

path=/etc/apache2/httpd.conf
basename=${path%.*} # /etc/apache2/httpd

仕組みについて

何でこんな不思議な事が出来るのか ${varname##*.} のような記述方法は何のか。

http://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html によると パラメータの展開 と呼ばれる機能だとわかった。

パラメータ展開について

bash には 変数の展開で、アレコレできるような機能が備わってる

記述方法 意味
${parameter%word} 後方マッチ削除
${parameter%%word} 後方マッチ削除(最長マッチ)
${parameter#word} 前方マッチ削除
${parameter##word} 前方マッチ削除(最長マッチ)
${parameter/search/replacement} マッチ置換

わかりにくいので、正規表現と対表作ってみる(bashはglobマッチなので厳密には一致しないがイメージとして覚えておくために)

記述方法 意味
${parameter%%word} parameter.replace( /(word)(.*)/ , '$1' )
${parameter##word} parameter.replace( /(word)(.*)/ , '$2' )
${parameter/search/replacement} parameter.replace( /word/ , replacement )

マッチの順番と考え方

パターンに依るパラメータ展開に目を通したので、冒頭の記号だらけの置換処理を見直してみる。

拡張子にマッチさせたときの bash の展開の流れ

最初の例の拡張子にマッチさせたときのbash展開の考え方について

path=/etc/apache2/httpd.conf
extension=${path##*.} #=> conf
  1. /etc/apache2/httpd.conf場合
  2. /etc/apache2/httpd.conf は *.とマッチさせる
  3. [ '/etc/apache2/httpd.' , 'conf'] に分割される
  4. マッチした前方は消される
  5. conf が残る

basenameと同等の文字列マッチの場合の考え方

冒頭の basename を擬似的に文字列マッチで行った場合の例は

path=/etc/apache2/httpd.conf
basename=${path##*/} #=> httpd.conf
  1. /etc/apache2/httpd.conf場合
  2. /etc/apache2/httpd.conf*/と先頭から最長マッチさせる
  3. [ '/etc/apache2/' , 'httpd.conf'] に分割される
  4. マッチした前方は消される
  5. httpd.conf が残る

次と同等になる。

'/etc/apache2/httpd.conf'.replace(/(^.*\/)(.*)/,'$2')

dirnameと同等の文字列マッチの場合の考え方

冒頭のdirnameと同等の文字列マッチの考え方。

path=/etc/apache2/httpd.conf

dirname=${path%/*}
echo $dirname #=>/etc/apache2
  1. /etc/apache2/httpd.conf場合
  2. /etc/apache2/httpd.conf/*と末尾からマッチさせる
  3. /httpd.confがマッチする
  4. [ '/etc/apache2' , '/httpd.conf'] に分割される
  5. マッチした後方は消される
  6. /etc/apache2 が残る

次と同等になる。

'/etc/apache2/httpd.conf'.replace(/(^.*\/)(.*)/,'$1')

変数展開で文字列を分割する

これらは、パスやファイル名の扱いというよりは、bash変数の文字列から部分文字列を取出す基本セットです。

man によると他にも

記述方法 意味
${parameter:offset} 部分文字列 ( 開始位置 )
${parameter:offset:length} 部分文字列(開始位置から指定長)
${#parameter} 文字列長取得

他にどんなときに使えば便利?

今回はパスだったけど、起動オプションの引数や、PATH変数の操作などにも使えて便利ですね

sed 使わずに文字列置換できるのは本当に助かる

参考資料

bashスクリプトだけで、ファイル名、拡張子を取得する - 作業ノート

http://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html