残念ながら、どちらにも癖があります。
両方ともPOSIXで必要とされるため、両者の違いは移植性の問題ではありません¹。
ユーティリティを使用する簡単な方法は
base=$(basename -- "$filename")
dir=$(dirname -- "$filename")
--ファイル名がダッシュで始まる場合は、変数置換を囲む二重引用符と、コマンドの後も注意してください(そうでない場合、コマンドはファイル名をオプションとして解釈します)。これは1つのエッジケースでは依然として失敗しますが、これはまれですが、悪意のあるユーザー²によって強制される可能性があります。コマンド置換は末尾の改行を削除します。したがって、ファイル名が呼び出されるとfoo/bar、の代わりににbase設定されbarますbar。回避策は、改行以外の文字を追加し、コマンド置換後に削除することです。
base=$(basename -- "$filename"; echo .); base=${base%.}
dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
パラメータ置換を使用すると、奇妙な文字の展開に関連するエッジケースに遭遇することはありませんが、スラッシュ文字には多くの困難があります。エッジケースではないことの1つは、ディレクトリ部分の計算に、が存在しない場合とは異なるコードが必要なことです/。
base="${filename##*/}"
case "$filename" in
*/*) dirname="${filename%/*}";;
*) dirname=".";;
esac
エッジケースは、末尾のスラッシュがある場合です(すべてのスラッシュであるルートディレクトリの場合を含む)。basenameそしてdirname、彼らは彼らの仕事をする前にコマンドスラッシュを末尾取り除きます。POSIX構造に固執する場合、末尾のスラッシュを一度に削除する方法はありませんが、2つのステップで実行できます。入力がスラッシュ以外で構成されている場合は注意が必要です。
case "$filename" in
*/*[!/]*)
trail=${filename##*[!/]}; filename=${filename%%"$trail"}
base=${filename##*/}
dir=${filename%/*};;
*[!/]*)
trail=${filename##*[!/]}
base=${filename%%"$trail"}
dir=".";;
*) base="/"; dir="/";;
esac
エッジケースではないことがわかっている場合(たとえばfind、開始点以外の結果には常にディレクトリパーツが含まれ、末尾はありません/)、パラメーター展開文字列の操作は簡単です。すべてのエッジケースに対処する必要がある場合は、ユーティリティの方が使いやすい(ただし遅い)。
場合によっては、のfoo/ようにfoo/.ではなく、のように扱いたい場合がありますfoo。あなたはディレクトリエントリに作用している場合、その後foo/に相当することになっているfoo/.、ではありませんfoo。これは、fooディレクトリへのシンボリックリンクである場合に違いを生じます。fooシンボリックリンクをfoo/意味し、ターゲットディレクトリを意味します。その場合、スラッシュが付いたパスのベース名はであることが有利.であり、パスはそれ自体のディレクトリ名にすることができます。
case "$filename" in
*/) base="."; dir="$filename";;
*/*) base="${filename##*/}"; dir="${filename%"$base"}";;
*) base="$filename"; dir=".";;
esac
高速で信頼性の高い方法は、zshとその履歴修飾子を使用することです(これは、ユーティリティと同様に、最初に末尾のスラッシュを取り除きます)。
dir=$filename:h base=$filename:t
¹Solaris 10以前のようなPOSIX以前のシェル/bin/sh(まだ運用中のマシンにはパラメーター拡張文字列操作機能がありませんshが、インストールでは常にPOSIXシェルが呼び出されますが、それだけで/usr/xpg4/bin/shはありません/bin/sh)を使用している場合を除きます。
² 例:fooこれに対して保護されていないファイルアップロードサービスに呼び出されたファイルを送信し、それを削除して、foo代わりに削除する
base=$(basename -- "$filename"; echo .); base=${base%.}; dir=$(dirname -- "$filename"; echo .); dir=${dir%.}?私は注意深く読んでいたが、欠点に言及していることに気付かなかった。