shellcheck basename kullanmamanızı tavsiye ediyor: neden?


25

Dışarı çalışıyorum shellcheck .

Böyle bir şeyim var

basename "${OPENSSL}" 

ve aşağıdaki öneriyi alıyorum

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

Pratik açıdan hiçbir fark görmüyorum

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

Yana basenameolduğunu POSIX özellikleri , ben değil en iyi uygulama olmalıdır bir neden yok. Herhangi bir ipucu?


8
Gerekmediğinde yeni bir süreç gerektirir.
jordanm

@jordanm yeterince adil ... Verimlilik hakkında düşünmedim.
Matteo,

3
@jordanm Öte yandan, bash dışındaki kabukları ile çalışır
Matteo

1
@JosephR. Ben de öyle düşündüm ama işe yaramadığını öğrendim csh. Sanırım cshPOSIX değil o zaman.
terdon

3
@terdon - csh POSIX'ten çok uzakta.
jordanm

Yanıtlar:


25

Bu verimlilikle ilgili değil - doğrulukla ilgili. basenameÇıktılan dosya adlarını sınırlamak için newlines kullanır. Her zamanki dosyada yalnızca bir dosya adı geçtiğinde, çıktısına sonunda yeni bir satır ekler. Dosya adları yeni satırlar içerebildiğinden, bu dosya adlarının doğru bir şekilde ele alınmasını zorlaştırır.

Ayrıca insanlar genellikle kullandıkları yüzünden karmaşık bir hale basenameböyle: "$(basename "$file")". Bunun nedeni, işler daha da zor hale getirir $(command)şeritler tüm sondaki yeni satırların arasından command. Yeni $filebir satırla sona eren olası olmayan durumu düşünün . Sonra basenameekstra yeni satır eklemek, ancak "$(basename "$file")"şerit olacak hem yanlış dosya ile ayrılırken, yeni satır.

Diğer bir problem basenameise (tire eksi $fileile başlar -), bir seçenek olarak yorumlanacağıdır. Bunu düzeltmek kolaydır:$(basename -- "$file")

Kullanmanın sağlam yolu basenameşudur:

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

Bunun bir alternatifi, ${file##*/}daha kolay fakat kendine has böcekleri olan bir kullanımdır. Özellikle, vakalarda yanlış $fileolduğunu /veya foo/.


2
Çok iyi puanlar +1. OP'nin benim yerine bunu kabul etmesine sevindim.
terdon

2
Konturpuan: ne olur $fileise foo/? Ya öyleyse /?
Gilles 'SO- kötülük'

2
@Gilles: Haklısın. Her şeyden önce, basenameyaklaşımın daha iyi olduğunu düşünmeye başladım . Ben ile gelip en iyi alternatifler ${${${file}%%/#}##*/}ve [[ $file =~ "([^/]*)/*$" ]] && printf "%s" $match[1]zsh özgü ve ikisi de sapın her ikisi de, /doğru.
Matt

@ terdon Kişisel olarak almadığınız için teşekkürler :-). Yeni satırlı dosyalar yaygın değildir ancak Matt'in bir anlamı vardır. Tabii ki değişken ikame kullanımı da daha verimlidir.
Matteo

16

İlgili satırlar shellcheckbireyin kaynak kodu vardır:

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

Açıkça verilen hiçbir açıklama yok, ancak fonksiyonun ismine dayanarak ( checkNeedlessCommands) @jordanm oldukça doğru görünüyor ve yeni bir işlem yapmaktan kaçınmanız gerektiğini gösteriyor.


7
Kaynak seninle olsun :)
Joseph R.

3

dirname, basename , readlinkVb - güvenlik önemli hale geldiğinde taşınabilirlik sorun yaratabilir (teşekkürler @Marco bu düzeltilene) (yol güvenliğini gerektiren). Pek çok sistem (Fedora Linux gibi) onu yerleştirirken /bin, diğerleri (Mac OSX gibi) yerleştirmiştir /usr/bin. Sonra Windows'ta Bash var, örneğin cygwin, msys ve diğerleri. Mümkün olduğunda saf Bash'i kalmak her zaman daha iyidir. (@Marco yorumuna göre)

Btw, işaretçi denetlemek için teşekkürler, daha önce görmedim.


1
1) “Güvenlik” derken ne demek istiyorsunuz? Ayrıntılı olabilir misiniz? 2) OP hiç bir şey ifade etmiyor dirname. 3) Temel çekirdek hizmet programları, nerede depolandıkları yerde PATH içinde olmalıdır. basenamePATH'de olmayan bir sistem görmedim . 4) Bash'ın mevcut olduğunu varsaymak, bir hazirlik meselesidir. Taşınabilirlik gerektiğinde, bash'ten uzak durmak ve bir POSIX kabuğu kullanmak her zaman daha iyidir.
Marco,

@Marco Bu sorunları belirttiğiniz için teşekkür ederiz. Evet, yardımcı programların yolda olduğu konusunda haklısınız. Ancak, bir Bash komut dosyasına ek güvenlik sağlamak istediğinde, mutlak yolu sağlamak iyi bir uygulamadır. Böylece '/ bin / basename' ifadesi RedHat sistemleri için çalışacak, ancak Mac'te bir hata üretecektir. Bu, 600 sayfanın yaklaşık dörtte birinin bu konuya ayrıldığı Bash Cookbook'ta daha iyi açıklanmaktadır . Ücretsiz kaynak güvenli-lib'imizdeki güvenlik konularını ele almak için zor bir girişimde bulunduk .
AsymLabs

@Marco Point 4 geçerli bir yorumdur, ancak soru (OP) ile başlar ve her iki sh / bash betiğini kontrol etmek için tasarlanmış bir kabuk kontrolüyle yazılır . Bu nedenle, varsayılan olarak kesinlikle Posix olmadığını varsaymalıyız.
AsymLabs

@Marco Yol ortam değişkeninin güvenliği kolayca tehlikeye girebilir. Örneğin, birkaç yıl önce yerel olarak kurulan Ruby RVM'nin varsayılan olarak sistem yolundan önce yolunu yerleştirdiğini öğrendiğimde çok şaşırdım.
AsymLabs

OP özellikle POSIX’ten bahseder ve soru etiketlenir posixve etiketlenmez bash. OP'nin bir bash çözümü gerektirdiğine dair herhangi bir gösterge bulamadım. "Her zaman saf Bash'i kalmak daha iyi" ifaden sadece yanlış, üzgünüm.
Marco
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.