Sayangnya, keduanya memiliki kebiasaan masing-masing.
Keduanya diperlukan oleh POSIX, jadi perbedaan di antara mereka bukan masalah portabilitas¹.
Cara sederhana untuk menggunakan utilitas adalah
base=$(basename -- "$filename")
dir=$(dirname -- "$filename")
Perhatikan tanda kutip ganda di sekitar substitusi variabel, seperti biasa, dan juga --
setelah perintah, jika nama file dimulai dengan tanda hubung (jika tidak, perintah akan menafsirkan nama file sebagai opsi). Ini masih gagal dalam satu kasus tepi, yang jarang tetapi mungkin dipaksakan oleh pengguna yang berbahaya ²: substitusi perintah menghapus baris baru. Jadi jika nama file dipanggil foo/bar
maka base
akan diatur ke bar
bukan bar
. Solusinya adalah menambahkan karakter non-baris baru dan menghapusnya setelah penggantian perintah:
base=$(basename -- "$filename"; echo .); base=${base%.}
dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
Dengan penggantian parameter, Anda tidak mengalami kasus tepi yang terkait dengan perluasan karakter aneh, tetapi ada sejumlah kesulitan dengan karakter garis miring. Satu hal yang bukan kasus tepi sama sekali adalah bahwa komputasi bagian direktori memerlukan kode yang berbeda untuk kasus di mana tidak ada /
.
base="${filename##*/}"
case "$filename" in
*/*) dirname="${filename%/*}";;
*) dirname=".";;
esac
Kasus tepi adalah ketika ada garis miring (termasuk kasus direktori root, yang semuanya adalah garis miring). The basename
dan dirname
perintah menanggalkan garis miring sebelum mereka melakukan pekerjaan mereka. Tidak ada cara untuk menghapus garis miring trailing sekaligus jika Anda tetap menggunakan konstruksi POSIX, tetapi Anda bisa melakukannya dalam dua langkah. Anda harus mengurus kasing ketika input hanya terdiri dari garis miring.
case "$filename" in
*/*[!/]*)
trail=${filename##*[!/]}; filename=${filename%%"$trail"}
base=${filename##*/}
dir=${filename%/*};;
*[!/]*)
trail=${filename##*[!/]}
base=${filename%%"$trail"}
dir=".";;
*) base="/"; dir="/";;
esac
Jika Anda mengetahui bahwa Anda tidak berada dalam kasus tepi (misalnya find
hasil selain dari titik awal selalu berisi bagian direktori dan tidak memiliki trailing /
) maka manipulasi string string ekspansi sangat mudah. Jika Anda perlu mengatasi semua kasing tepi, utilitas lebih mudah digunakan (tetapi lebih lambat).
Terkadang, Anda mungkin ingin memperlakukan foo/
suka foo/.
daripada suka foo
. Jika Anda bertindak pada entri direktori maka foo/
seharusnya setara dengan foo/.
, bukan foo
; ini membuat perbedaan ketika foo
tautan simbolik ke direktori: foo
berarti tautan simbolik, foo/
berarti direktori target. Dalam hal ini, nama dasar dari sebuah jalan dengan garis miring lebih menguntungkan .
, dan jalur tersebut dapat menjadi dirname sendiri.
case "$filename" in
*/) base="."; dir="$filename";;
*/*) base="${filename##*/}"; dir="${filename%"$base"}";;
*) base="$filename"; dir=".";;
esac
Metode cepat dan andal adalah menggunakan zsh dengan pengubah riwayatnya (strip pertama ini mengikuti garis miring, seperti utilitas):
dir=$filename:h base=$filename:t
¹ Kecuali jika Anda menggunakan shell pra-POSIX seperti Solaris 10 dan yang lebih lama /bin/sh
(yang tidak memiliki fitur manipulasi string ekspansi parameter pada mesin yang masih dalam produksi - tetapi selalu ada shell POSIX yang dipanggil sh
dalam instalasi, hanya saja itu /usr/xpg4/bin/sh
, bukan /bin/sh
).
² Misalnya: mengirim file yang dipanggil foo
ke layanan unggah file yang tidak melindungi dari ini, kemudian hapus dan menyebabkan foo
dihapus sebagai gantinya
base=$(basename -- "$filename"; echo .); base=${base%.}; dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
? Saya membaca dengan seksama dan saya tidak melihat Anda menyebutkan kekurangan.