ディレクトリ名、デフォルト名、およびパラメータ拡張

ディレクトリ名、デフォルト名、およびパラメータ拡張

ある形式を他の形式より好む客観的な理由はありますか?性能、信頼性、携帯性?

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␤ます。回避策は、改行以外の文字を追加し、コマンドの置き換え後に削除することです。basebarbar␤

base=$(basename -- "$filename"; echo .); base=${base%.}
dir=$(dirname -- "$filename"; echo .); dir=${dir%.}

パラメータ置換を使用すると、奇妙な文字拡張に関連する極端なケースは発生しませんが、スラッシュ文字には多くの困難があります。まったくエッジケースではないことの1つは、noに対してディレクトリ部分計算を実行する必要があることです/

base="${filename##*/}"
case "$filename" in
  */*) dirname="${filename%/*}";;
  *) dirname=".";;
esac

エッジケースは後ろにスラッシュがあるケースです(すべてスラッシュであるルートケースを含む)。basenameand コマンドは、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/.foofoo/foo/.foofoofoofoo/.

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削除します。

おすすめ記事