Ne yazık ki her ikisinin de tuhaflıkları var.
Her ikisi de POSIX için gereklidir, bu nedenle aralarındaki fark bir taşınabilirlik sorunu değildir¹.
Yardımcı programları kullanmanın basit yolu
base=$(basename -- "$filename")
dir=$(dirname -- "$filename")
--
Dosya adının bir tire işareti ile başlaması durumunda her zamanki gibi değişken ikameler ve komuttan sonra gelen çift tırnaklara dikkat edin (aksi takdirde komutlar dosya adını bir seçenek olarak yorumlar). Bu hala bir kenar durumunda başarısız olur, bu nadirdir, ancak kötü niyetli bir kullanıcı² tarafından zorlanabilir. Bir dosya adı denir Yani eğer foo/bar
o zamanbase
ayarlanır bar
yerine bar
. Geçici bir çözüm, yeni satır olmayan bir karakter eklemek ve komutun değiştirilmesinden sonra onu kaldırmaktır:
base=$(basename -- "$filename"; echo .); base=${base%.}
dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
Parametre ikamesi ile, garip karakterlerin genişlemesi ile ilgili son durumlarla karşılaşmazsınız, ancak eğik çizgi karakterinde bir takım zorluklar vardır. Bir uç durum olmayan bir şey, dizin parçasının hesaplanmasının, yok olduğu durumda farklı kod gerektirmesidir /
.
base="${filename##*/}"
case "$filename" in
*/*) dirname="${filename%/*}";;
*) dirname=".";;
esac
Kenar durumu, bir eğik çizgi olduğunda (tüm eğik çizgiler olan kök dizinin durumu dahil). basename
vedirname
onlar işlerini yapmadan önce komutlar eğik çizgileri kapalı şerit. POSIX yapılarına yapışırsanız sondaki eğik çizgileri tek seferde kesmenin bir yolu yoktur, ancak iki adımda yapabilirsiniz. Girdi eğik çizgilerden başka bir şey içermiyorsa, duruma dikkat etmeniz gerekir.
case "$filename" in
*/*[!/]*)
trail=${filename##*[!/]}; filename=${filename%%"$trail"}
base=${filename##*/}
dir=${filename%/*};;
*[!/]*)
trail=${filename##*[!/]}
base=${filename%%"$trail"}
dir=".";;
*) base="/"; dir="/";;
esac
Uç bir durumda olmadığınızı biliyorsanız (ör. find
. Başlangıç noktasından başka sonuç her zaman bir dizin parçası içerir ve iz bırakmaz /
), parametre genişletme dizesi manipülasyonu basittir. Tüm uç durumlarla başa çıkmanız gerekiyorsa, yardımcı programların kullanımı daha kolaydır (ancak daha yavaştır).
Bazen, foo/
gibi davranmak foo/.
yerine davranmak isteyebilirsiniz foo
. Eğer bir dizin girişinde davranıyorsun o zaman foo/
eşdeğer olması gerekiyordu foo/.
değil foo
; bu, bir foo
dizine sembolik bağlantı olduğunda fark yaratır : foo
sembolik bağlantı, foo/
hedef dizin anlamına gelir. Bu durumda, sonunda eğik çizgi bulunan bir yolun taban adı avantajlıdır .
ve yol kendi dizin adı olabilir.
case "$filename" in
*/) base="."; dir="$filename";;
*/*) base="${filename##*/}"; dir="${filename%"$base"}";;
*) base="$filename"; dir=".";;
esac
Hızlı ve güvenilir yöntem, tarih değiştiricileri ile zsh kullanmaktır (bu, ilk olarak yardımcı programlar gibi eğik çizgileri çıkarır):
dir=$filename:h base=$filename:t
Solar Solaris 10 ve eskisi gibi POSIX öncesi mermileri kullanmadığınız sürece /bin/sh
(hala üretimde olan makinelerde parametre genişletme dizesi manipülasyon özelliklerinden yoksundu - ancak her zaman sh
kurulumda çağrılan bir POSIX kabuğu var , sadece /usr/xpg4/bin/sh
değil /bin/sh
).
² Örneğin: buna foo
karşı korumasız bir dosya yükleme hizmetine çağrılan bir dosya gönderin , ardından silin ve foo
bunun yerine silinmesine neden olun
base=$(basename -- "$filename"; echo .); base=${base%.}; dir=$(dirname -- "$filename"; echo .); dir=${dir%.}
? Dikkatle okuyordum ve herhangi bir dezavantajdan bahsettiğinizi fark etmedim.