Beide haben leider ihre Macken.
Beide werden von POSIX benötigt, daher ist der Unterschied zwischen ihnen kein Problem der Portabilität¹.
Der einfache Weg, um die Dienstprogramme zu verwenden, ist
base=$(basename -- "$filename")
dir=$(dirname -- "$filename")
Beachten Sie die doppelten Anführungszeichen bei Variablensubstitutionen wie immer und auch --
nach dem Befehl, falls der Dateiname mit einem Bindestrich beginnt (andernfalls würden die Befehle den Dateinamen als Option interpretieren). Dies schlägt in einem Randfall immer noch fehl, was selten vorkommt, aber möglicherweise von einem böswilligen Benutzer erzwungen wird²: Durch das Ersetzen von Befehlen werden nachgestellte Zeilenumbrüche entfernt. Also , wenn ein Dateiname genannt wird , foo/bar
dann base
wird gesetzt werden bar
statt bar
. Eine Problemumgehung besteht darin, ein Nicht-Zeilenumbruchzeichen hinzuzufügen und es nach der Befehlsersetzung zu entfernen:
base=$(basename -- "$filename"; echo .); base=${base%.}
dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
Mit der Parametersubstitution stoßen Sie nicht auf Randfälle, die mit der Erweiterung von seltsamen Zeichen zusammenhängen, aber es gibt eine Reihe von Schwierigkeiten mit dem Schrägstrich. Eine Sache, die überhaupt kein Randfall ist, besteht darin, dass für die Berechnung des Verzeichnisteils ein anderer Code erforderlich ist als für den Fall, dass es keinen gibt /
.
base="${filename##*/}"
case "$filename" in
*/*) dirname="${filename%/*}";;
*) dirname=".";;
esac
Der Kantenfall liegt vor, wenn ein abschließender Schrägstrich angezeigt wird (einschließlich des Stammverzeichnisses, bei dem es sich ausschließlich um Schrägstriche handelt). Die Befehle basename
und dirname
entfernen abschließende Schrägstriche, bevor sie ihre Arbeit erledigen. Es gibt keine Möglichkeit, die abschließenden Schrägstriche auf einmal zu entfernen, wenn Sie sich an POSIX-Konstrukte halten, aber Sie können dies in zwei Schritten tun. Sie müssen sich um den Fall kümmern, dass die Eingabe nur aus Schrägstrichen besteht.
case "$filename" in
*/*[!/]*)
trail=${filename##*[!/]}; filename=${filename%%"$trail"}
base=${filename##*/}
dir=${filename%/*};;
*[!/]*)
trail=${filename##*[!/]}
base=${filename%%"$trail"}
dir=".";;
*) base="/"; dir="/";;
esac
Wenn Sie zufällig wissen, dass Sie sich nicht in einer Randbedingung befinden (z. B. enthält ein find
anderes Ergebnis als der Startpunkt immer einen Verzeichnisteil und weist keine nachfolgenden Elemente auf /
), ist die Manipulation der Parametererweiterungszeichenfolge unkompliziert. Wenn Sie alle Randfälle bewältigen müssen, sind die Dienstprogramme einfacher zu verwenden (aber langsamer).
Manchmal können Sie behandeln wollen foo/
wie foo/.
anstatt wie foo
. Wenn Sie auf einem Verzeichniseintrag sind handeln dann foo/
sollte äquivalent sein foo/.
, nicht foo
; Dies macht einen Unterschied, wenn foo
es sich um eine symbolische Verknüpfung zu einem Verzeichnis handelt: foo
bedeutet die symbolische Verknüpfung, foo/
bedeutet das Zielverzeichnis. In diesem Fall ist der Basisname eines Pfads mit einem abschließenden Schrägstrich vorteilhaft .
und der Pfad kann ein eigener Verzeichnisname sein.
case "$filename" in
*/) base="."; dir="$filename";;
*/*) base="${filename##*/}"; dir="${filename%"$base"}";;
*) base="$filename"; dir=".";;
esac
Die schnelle und zuverlässige Methode besteht darin, zsh mit seinen Verlaufsmodifikatoren zu verwenden (in diesem Beispiel werden abschließende Schrägstriche wie in den Dienstprogrammen entfernt):
dir=$filename:h base=$filename:t
¹ Wenn Sie pre-POSIX - Shells wie Solaris verwenden 10 und älter ist /bin/sh
(die Parameter Expansion String - Manipulation fehlte noch in der Produktion von Maschinen bietet - aber es gibt immer eine POSIX genannt shell sh
in der Installation, nur es ist /usr/xpg4/bin/sh
nicht /bin/sh
).
² Zum Beispiel: Übermitteln Sie eine aufgerufene Datei foo
an einen Datei-Upload-Dienst, der nicht davor schützt, und löschen Sie sie dann und veranlassen Sie foo
stattdessen, dass sie gelöscht wird
base=$(basename -- "$filename"; echo .); base=${base%.}; dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
? Ich habe sorgfältig gelesen und habe nicht bemerkt, dass Sie irgendwelche Nachteile erwähnt haben.