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 basename
ist 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. basename
verwendet 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 basename
so verwenden: "$(basename "$file")"
. Das macht die Sache noch schwieriger, denn $(command)
Streifenallenachgestellte Zeilenumbrüche von command
. Betrachten Sie den unwahrscheinlichen Fall, dass $file
mit einem Zeilenumbruch endet. Dann basename
wird ein zusätzlicher Zeilenumbruch hinzugefügt, aber "$(basename "$file")"
entferntbeideZeilenumbrüche, wodurch Sie einen falschen Dateinamen erhalten.
Ein weiteres Problem basename
ist, dass es als Option interpretiert wird, wenn $file
es mit einem -
(Bindestrich, auch Minuszeichen genannt) beginnt. Dies lässt sich leicht beheben:$(basename -- "$file")
Die robuste Verwendungsweise basename
ist 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 $file
ist ./
foo/
Antwort2
Die entsprechenden Zeilen shellcheck
inQuellcodeSind:
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
, readlink
usw. (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 , /bin
wä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.