ある形式を他の形式より好む客観的な理由はありますか?性能、信頼性、携帯性?
filename=/some/long/path/to/a_file
parentdir_v1="${filename%/*}"
parentdir_v2="$(dirname "$filename")"
basename_v1="${filename##*/}"
basename_v2="$(basename "$filename")"
echo "$parentdir_v1"
echo "$parentdir_v2"
echo "$basename_v1"
echo "$basename_v2"
生産:
/some/long/path/to
/some/long/path/to
a_file
a_file
(v1はシェルパラメータ拡張を使用し、v2は外部バイナリを使用します。)
ベストアンサー1
残念ながら、どちらにも独自の特徴があります。
POSIX には両方が必要なので、2 つの違いは移植性の問題ではありません。
これらのユーティリティを使用する簡単な方法は次のとおりです。
base=$(basename -- "$filename")
dir=$(dirname -- "$filename")
通常のように、変数置換の周りに二重引用符があり、ファイル--
名がダッシュで始まる場合、コマンドの後に二重引用符があります(そうでない場合、コマンドはファイル名をオプションとして解釈します)。これはまれですが、悪意のあるユーザーによって強制される可能性がある極端なケースではまだ失敗します。コマンド置換は末尾の改行を削除します。したがって、ファイル名がと呼ばれる場合は代わりに設定されfoo/bar
ます。回避策は、改行以外の文字を追加し、コマンドの置き換え後に削除することです。base
bar
bar
base=$(basename -- "$filename"; echo .); base=${base%.}
dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
パラメータ置換を使用すると、奇妙な文字拡張に関連する極端なケースは発生しませんが、スラッシュ文字には多くの困難があります。まったくエッジケースではないことの1つは、noに対してディレクトリ部分計算を実行する必要があることです/
。
base="${filename##*/}"
case "$filename" in
*/*) dirname="${filename%/*}";;
*) dirname=".";;
esac
エッジケースは後ろにスラッシュがあるケースです(すべてスラッシュであるルートケースを含む)。basename
and コマンドは、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
1 Solaris 10 以前などの事前 POSIX シェルを使用しない限り(まだ生産中のシステムにはパラメータ拡張文字列操作機能はありませんが、POSIX シェルはインストール時に常に呼び出されますが、そうではありませ/bin/sh
ん)。sh
/usr/xpg4/bin/sh
/bin/sh
²はい:foo
ファイルアップロードサービスにファイルを送信しましたが、サービスがこの問題から保護していない場合は、そのファイルを削除して結果をfoo
削除します。