rcmdnk's blog

Basename

ちょっと人のシェルスクリプトを見てたら basenameに引数を2つ渡してて 何やら、と思ったら2つ目の引数を後ろから削れる、ということを今更知りました。

usage: basename string [suffix]

言わずと知れたパスの最後の部分を切り取る basenameですが、簡単なコマンド過ぎてコマンドさえ知ったら ヘルプも見ることもまず無いかと。

MacでおもむろにBSD版basenameを引数無しで実行してみると

$ basename
usage: basename string [suffix]
       basename [-a] [-s suffix] string [...]

な感じ。 そもそもヘルプな引数すら無い模様。

LinuxなどでGNU版だと引数なしだとmissing operandが出ますが、 --helpで見れます。

$ basename --help
Usage: basename NAME [SUFFIX]
  or:  basename OPTION
Print NAME with any leading directory components removed.
If specified, also remove a trailing SUFFIX.

      --help     display this help and exit
      --version  output version information and exit

Examples:
  basename /usr/bin/sort       Output "sort".
  basename include/stdio.h .h  Output "stdio".

Report basename bugs to [email protected]
GNU coreutils home page: <http://www.gnu.org/software/coreutils/>
General help using GNU software: <http://www.gnu.org/gethelp/>
For complete documentation, run: info coreutils 'basename invocation'

こちらは親切にExamplesまで載せてくれてます。

SUFFIXの削除

まあGNU版のヘルプを見ると一目瞭然なんですが、 2つ目の引数に拡張子などを渡してあげれば その分を後ろ側から除いてファイルの名前だけを出力する様な事が簡単に出来るわけですね。

ちなみに後ろから見て該当の文字列がなければエラーが出るわけではなく何もしません。

$ basename /aaa/bbb/ccc.cxx .cxx
ccc
$ basename /aaa/bbb/ddd.h .cxx
ddd.h

複数の文字列を同時に操作

GNU版の方は基本的に1つの引数を取ってそれのベースネームを取るだけです。 後はSUFFIXを削除する事が出来るだけ。

一方BSD版の方は複数の文字列を一度に変換することが出来ます。 この際、デフォルトでは2つの引数を与えるとGNU版同様2つ目をSUFFIXと捉えて 1つ目の引数から消そうとしてしまいますが、 オプション-aを与える事でそれぞれの引数に別途basenameをかけることが出来ます。

$ basename /aaa/bbb/ccc /ddd/eee/fff
ccc

これだと1つ目の引数のbasenameのみ返る。 /ddd/eee/fffはSUFFIXとみなされ、この場合はcccからは消せないので何もしない。

一方、以下のものだと

$ basename -a /aaa/bbb/ccc /ddd/eee/fff
ccc
fff

/aaa/bbb/ccc/ddd/eee/fffの両方にbasenameをかけた結果が返る。

3つ以上渡すと-aを与えても与えなくても全てを独立の文字列とみなす様です。

$ basename /aaa/bbb/ccc /ddd/eee/fff /ggg/hhh/iii
ccc
fff
iii
$ basename -a /aaa/bbb/ccc /ddd/eee/fff /ggg/hhh/iii
ccc
fff
iii

こういった複数の文字列を一気に扱いたい時に、さらにSUFFIXを差っ引きたい場合は-sを使います。

$ basename -a -s .h /aaa/bbb/ccc.cxx /ddd/eee/fff.h /ggg/hhh/iii.h
ccc.cxx
fff
iii

-sを渡すと、-s <suffix>以外の引数が2つの場合にも -aを使わずとも両方共文字列として扱われbasenameが2つにかけられます。

GNUで3つ以上渡すと、

$ basename /aaa/bbb/ccc /ddd/eee/fff /ggg/hhh/iii
basename: extra operand `/ggg/hhh/iii'
Try `basename --help' for more information.

な感じでエラーになります。 2つならエラーにはなりませんが、 -aを渡せばエラーになりますし、 2つの文字列を渡すだけだと後ろ側はSUFFIXとして評価されるだけです。

BSD版の方が便利ですね。

manを見るとBSD版がApril 18, 1994、GNU版がGNU coreutils 8.4 March 2017 と書いてあったりするのですが、GNU版はBSD版の機能を追随しないのでしょうか? (GNUの方はcoreutilsの全体のアップデートに従って日付がアップデートされてて basename自体は手を付けられてない状態なんだとは思いますが。)

この複数渡すものも知りませんでしたが、 GNUとの互換性が無いのでこれは知らなくてもそれ程問題ないかな、とも。

ちなみにdirname

ちなみに逆にパスの最後の部分だけ落としてディレクトリを返す dirnameコマンドですが、 これはGNU/BSDどちらの場合も1つの引数を取るだけみたいです

2つ以上渡そうとすれば

$ dirname aaa bbb # BSD
usage: dirname path

$ dirname aaa bbb # GNU
dirname: extra operand `bbb'
Try `dirname --help' for more information.

と、共にエラーが出ます またdirnameには基本的にオプション引数はありません。 GNUの方に--help--versionがあるだけです。

また、BSD版の方はbasenameと同一パッケージになっていて man basenameman dirnameも同じものが表示され、 2つのコマンドについて書かれたマニュアルが表示されます。

まとめ

こんな簡単で良く使うコマンドでも知らない事がまだあるとは 自分が残念です。

まあ簡単過ぎて調べもしないのと、 拡張子除くのもcutでもawkでもなんでも簡単に出来るので考えもしないのと。

でも知ってたら断然便利ですね。。。

多分今まで作ったスクリプトの中でも 結構な量の無駄があると思います。

これって一体どれだけの人が知ってるんでしょうか? (意外と知らないんだと思いたいのですが

ま、多分他にも色々あるんでしょう。

Sponsored Links
Sponsored Links

« AutoHotkeyで特定の条件下で設定したキー以外全てを無効にする簡単な方法 Vim以外でVimする: Mac+Karabiner-Elements編 »

}