shellcheckではbasenameの使用はお勧めできません。なぜですか?

shellcheckではbasenameの使用はお勧めできません。なぜですか?

頑張っています住宅検査

私は次のようなものを持っています

basename "${OPENSSL}" 

私は次のアドバイスを得ました

Use parameter expansion instead, such as ${var##*/}.

実用的な面ではあまり違いはないと思います。

$ export OPENSSL=/opt/local/bin/openssl
$ basename ${OPENSSL}
openssl
$ echo ${OPENSSL##*/}
openssl

basenameにあるからPOSIX仕様、なぜこれがベストプラクティスになるのかわかりません。どのようなヒントがありますか?

答え1

効率に関するものではなく、正確性に関するものです。basename印刷されるファイル名を区別するには、改行文字を使用してください。通常、ファイル名だけを渡すと、出力に改行文字が追加されます。ファイル名自体に改行文字を含めることができるため、これらのファイル名を正しく処理することは困難です。

basename人々が一般的に次のように使用するという事実により、状況はより複雑になります"$(basename "$file")"。これ$(command)により状況がさらに困難になります。みんな末尾の改行文字はから出ますcommand$file改行文字で終わる可能性が低い場合を考えてみてください。その後、basename改行が追加されますが削除さ"$(basename "$file")"れます。両方改行のため、間違ったファイル名が残ります。

別の問題は、(ダッシュ、別名マイナス記号)で始まるbasenameとオプションとして解釈されることです。この問題は簡単に解決できます。$file-$(basename -- "$file")

強力な使用法はbasename次のとおりです。

# A file with three trailing newlines.
file=$'/tmp/evil\n\n\n'

# Add an 'x' so we can tell where $file's newlines end and basename's begin.
file_x="$(basename -- "$file"; printf x)"

# Strip off two trailing characters: the 'x' added by us and the newline added by basename. 
base="${file_x%??}"

もう一つの方法はを使用することですが${file##*/}、この方法はより簡単ですが、独自のバグもあります。特に$fileis/またはの場合は間違っていますfoo/

答え2

shellcheck関連ラインソースコード例:

checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "dirname" =
    style id "Use parameter expansion instead, such as ${var%/*}."
checkNeedlessCommands (T_SimpleCommand id _ (w:_)) | w `isCommand` "basename" =
    style id "Use parameter expansion instead, such as ${var##*/}."
checkNeedlessCommands _ = return ()

明示的な説明はありませんが、関数名(checkNeedlessCommands)を見れば、@jordanmが新しいプロセスをフォークしないように勧めたのはかなり正しいようです。

答え3

dirnamebasenameなど(readlink@Marcoのおかげで修正されました。)セキュリティが重要になると(パスの安全性が必要です)、移植性の問題が発生する可能性があります。 Fedora Linuxなどの多くのシステム/binでは、/usr/bincygwin、msysなどのWindowsにBashがあります。 可能であれば、純粋なバッシュを維持することは常に良いです。(@Marcoコメントに基づく)

さて、shellcheckへのポインタを教えてくれてありがとう。以前は見たことがありません。

関連情報