Shellcheck rät davon ab, den Basisnamen zu verwenden: Warum?

Shellcheck rät davon ab, den Basisnamen zu verwenden: Warum?

Ich probiere ausShellcheck.

Ich habe sowas

basename "${OPENSSL}" 

und ich bekomme den folgenden Vorschlag

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

Aus praktischer Sicht sehe ich keinen Unterschied

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

Da basenameist in derPOSIX-Spezifikationen, ich sehe keinen Grund, warum das die beste Vorgehensweise sein sollte. Irgendein Tipp?

Antwort1

Es geht nicht um Effizienz, sondern um Korrektheit. basenameverwendet Zeilenumbrüche, um die ausgegebenen Dateinamen abzugrenzen. Im Normalfall, wenn Sie nur einen Dateinamen übergeben, wird der Ausgabe ein abschließender Zeilenumbruch hinzugefügt. Da Dateinamen selbst Zeilenumbrüche enthalten können, ist es dadurch schwierig, diese Dateinamen korrekt zu verarbeiten.

Es wird noch komplizierter durch die Tatsache, dass die Leute normalerweise basenameso verwenden: "$(basename "$file")". Das macht die Sache noch schwieriger, denn $(command)Streifenallenachgestellte Zeilenumbrüche von command. Betrachten Sie den unwahrscheinlichen Fall, dass $filemit einem Zeilenumbruch endet. Dann basenamewird ein zusätzlicher Zeilenumbruch hinzugefügt, aber "$(basename "$file")"entferntbeideZeilenumbrüche, wodurch Sie einen falschen Dateinamen erhalten.

Ein weiteres Problem basenameist, dass es als Option interpretiert wird, wenn $filees mit einem -(Bindestrich, auch Minuszeichen genannt) beginnt. Dies lässt sich leicht beheben:$(basename -- "$file")

Die robuste Verwendungsweise basenameist diese:

# 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%??}"

Eine Alternative besteht darin, zu verwenden ${file##*/}, was einfacher ist, aber seine eigenen Fehler hat. Insbesondere ist es in den Fällen falsch, in denen oder $fileist ./foo/

Antwort2

Die entsprechenden Zeilen shellcheckinQuellcodeSind:

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 ()

Es wird keine explizite Erklärung gegeben, aber basierend auf dem Namen der Funktion ( checkNeedlessCommands) scheint @jordanm völlig recht zu haben und vorzuschlagen, dass Sie die Aufspaltung eines neuen Prozesses vermeiden.

Antwort3

dirname, basename, readlinkusw. (danke @Marco – das ist korrigiert) kann Portabilitätsprobleme verursachen, wenn Sicherheit wichtig wird (Sicherheit des Pfads erforderlich). Viele Systeme (wie Fedora Linux) platzieren es unter , /binwährend andere (wie Mac OSX) es unter platzieren /usr/bin. Dann gibt es Bash unter Windows, z. B. Cygwin, Msys und andere. Es ist immer besser, wenn möglich beim reinen Bash zu bleiben.(laut @Marco-Kommentar)

Übrigens, danke für den Hinweis auf Shellcheck, das habe ich noch nicht gesehen.

verwandte Informationen