Kaynak yapmadan önce dosya varlığını neden kontrol etmelisiniz?


13

Bir dosyayı kaynak oluşturmaya çalışırken, dosyanın var olmayacağını söyleyen bir hata istemez miydiniz?

Örneğin, nvm bunu profilinize / rc'nize eklemenizi önerir:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

Yukarıdakilerle birlikte yoksa nvm.sh"sessiz hata" alırsınız. Ama denerseniz . "$NVM_DIR/nvm.sh", çıktı olur FILE_PATH: No such file or directory.


3
Yapmamalısın. Açık saçık. Kaynağı deneyin ve varsa hatayı
Mikel

2
@Mikel doğru! o zaman neden her yerde görüyorum?
JBallin

3
@FaheemMitha, yaptığınız şey güvenlik açısından hassassa (yani programınız başkası adına çalışıyorsa), yarış koşullarına ( TOCTOU ) gerçekten dikkat etmeniz gerekir . Muhtemelen burada durum böyle değil, çünkü birisi dosyaları değiştirirse daha büyük sorunlarınız var HOME.
ilkkachu

8
@FaheemMitha Önce varlığını kontrol etmek asla daha iyi değildir. Durum test ve kullanım arasında değişebilir, hem yanlış pozitif hem de yanlış negatifler verir: ve yine de kullanımdaki hatayı yine de ele almanız gerekir. Ve performanstan bahsettiğiniz gibi, kullanımdan önce test yapmak endemik olarak bunu yapmamaktan ve sistemin bunu yapmasına izin vermekten iki kat daha yavaştır. Olamaz.
user207421

1
@SimonRichter, bu durumda nvm çalışmaz. Kullanıcının dosyanın kendi başına eksik olduğunu anlaması gerekir.
JBallin

Yanıtlar:


25

POSIX mermilerinde .özel bir yerleşiktir, bu nedenle başarısızlığı kabuğun çıkmasına neden olur (bazı mermilerde bashsadece POSIX modundayken yapılır).

Hata olarak nitelendirilen şey kabuğa bağlıdır. Hepsi dosyayı ayrıştırırken bir sözdizimi hatasıyla çıkmaz, ancak çoğu kaynak dosya bulunamadığında veya açılamadığında çıkar. errexitKaynak dosyadaki son komut sıfır olmayan bir çıkış durumu ( seçenek elbette açık olmadığı sürece) ile döndürülürse çıkacak herhangi bir bilmiyorum .

İşte yapıyor:

[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

Eğer varsa, dosyayı kaynaklamak istediğiniz bir durumdur ve yoksa (veya burada boşsa -s) yapmayın .

Yani, dosya orada değilse bir hata (POSIX kabuklarında ölümcül hata) olarak değerlendirilmemelidir, bu dosya isteğe bağlı bir dosya olarak kabul edilir.

Dosya okunamazsa veya bir dizin olsaydı yine de (ölümcül) bir hata veya ayrıştırılırken sözdizimi hatası olsaydı rapor edilmesi gereken gerçek hata koşulları olurdu.

Bazıları bir yarış koşulu olduğunu iddia eder. Ama bu demektir tek şey dosya arasına kaldırılırsa kabuk hata vererek terk edeceği olacağını [ve ., ama senaryo ise bu sabit yolu dosya ortadan aniden olacağını o bir hata göz önünde bulundurulacak geçerli olduğunu iddia ediyorum çalışan.

Diğer yandan,

command . "$NVM_DIR/nvm.sh" 2> /dev/null

burada command¹ , komutun özel niteliğini kaldırır .(bu nedenle hatada kabuktan çıkmaz) şu şekilde çalışmaz:

  • .hatalarını gizler, ancak kaynaklı dosyada çalıştırılan komutların hatalarını da gizler
  • dosya yanlış izinlere sahip gibi gerçek hata koşullarını da gizler.

Diğer yaygın sözdizimleri (örneğin henüz grep -r /etc/default /etc/init*dönüştürülmemiş init komut dosyaları için Debian sistemlerinde systemd(burada EnvironmentFile=-/etc/default/serviceisteğe bağlı ortam dosyası belirtmek için kullanılır) şunları içerir):

  • [ -e "$file" ] && . "$file"

    Dosyayı kontrol edin, boşsa yine de kaynak yapın. Hala açılamıyorsa ölümcül hata (orada olmasına rağmen veya orada olmasına rağmen). (Var [ -f "$file" ]ve normal bir dosya), [ -r "$file" ](okunabilir) veya bunların kombinasyonları gibi daha fazla varyant görebilirsiniz .

  • [ ! -e "$file" ] || . "$file"

    Biraz daha iyi bir versiyon. Dosyanın mevcut değil durumda olduğunu netleştirir. Bu aynı zamanda $?çalıştırılan son komutun çıkış durumunu da yansıtacağı anlamına gelir $file(önceki durumda, alırsanız 1, bunun var olup olmadığı $fileveya bu komutun başarısız olup olmadığını bilmiyorsunuz ).

  • command . "$file"

    Dosyanın orada olmasını bekleyin, ancak yorumlanamıyorsa çıkmayın.

  • [ ! -e "$file" ] || command . "$file"

    Yukarıdakilerin birleşimi: dosya orada değilse ve POSIX mermileri için, dosyayı açma (veya ayrıştırma) başarısızlıkları rapor edilir, ancak ölümcül değildir (bu daha arzu edilir olabilir ~/.profile).


¹ Not: In zshancak, kullanamaz commandöyle içeri sürece shöykünme; Korn kabuğunda, sourceaslında bir takma ad command ., özel olmayan bir varyant.


İlginç! POSIX sh hakkında bilmiyordum. Ama soru hakkındaydı .bash_profile. Üzgünüm daha güvenli sanırım, ama bash .bash_profilekaynak zaman POSIX modunda ?
Mikel

(Bu soruyu, sorudaki nvm kaynak bağlantısını okumaya dayalı olarak tüm POSIX mermilerine daha geniş bir şekilde uygulayabileceğinizi anlıyorum.)
Mikel

@Mikel, cevabım bashPOSIX modunda olmadığında hala geçerli . [ -e /file ] && . /fileDosya mevcut olmadığında bunun bir hata olduğunu düşünmezseniz istersiniz . Varsa deneyin kaynağı ardından, hatayı işlemek burada yapılamaz.
Stéphane Chazelas

1
@Mikel, bu karşı üretken. 1) POSIX kabuklarında (veya POSIX modunda bash) hata oluştuğunda çıkmayı engellemeyen 2), hata mesajını çoğaltan (stdout'taki) .zaten bir hata (stderr'de) bildirir. Ve eğer amaç dosya mevcut olmadığında bir hata olarak düşünmüyorsa, bu doğru değildir (ve çıkış durumundan ., dosyanın var olmadığı veya okunamıyor olduğu veya başarısız olduğu için başarısız olup olmadığını söylemek mümkün değildir. ayrıştırılamıyor veya son komut başarısız oldu) bu cevapta burada belirttiğim noktalar.
Stéphane Chazelas

1
Yarış durumu ile ilgili olarak - girişin bir kez başarısız olması (sistemde garip bir şey olurken) IMHO'nun girişin tutarlı bir şekilde başarısız olmasına (kullanıcının yapılandırmasında tam olarak doğru olmayan bir şey olsa bile ) çok daha az sorun -Dosyalar). Yani bu kontroldeki bir yarış koşulu, kontrole sahip olmama konusunda hala bir gelişme.
ruakh

5

nvmYanıtını koruyan :

sadece dosyayı silerek nvm kaldırmak kolaydır; ek çalışmaları zorlamak (satır (lar) ın kaynak nvm nerede olduğunu bulmak için) özellikle değerli görünmüyor.

Benim yorumum (Stéphane'nin mükemmel açıklaması ve Kusalananda'nın yorumu ile birlikte):

Daha basit ve daha güvenli.

Eksik dosya nedeniyle (çeşitli nedenlerle) başlangıçta çıkan POSIX mermilerine karşı savunur. POSIX olmayan (örn. Bash) mermileri kullananlar istedikleri takdirde koşulluları kaldırabilirler.


1
Bunun "yeni başlayanlara yönelik" olduğu yorumuna karşıyım. Savunma. Bir kullanıcının giriş kabuğunun başlangıçta beklenmedik bir şekilde sona ermesini istemezsiniz, çünkü bu dosya kaybolmuşsa ( herhangi bir nedenle). Bu kod satırı altındaki kabuk init dosyalarından birindeyse /etc, bazı kullanıcıların dosyaya sahip olmasına ve bazılarının dosyaya sahip olmamasına izin verir. IMHO, bakımcının nvmyanıtı sadece bir yönden dokunuyor.
Kusalananda

1

As JBallin ve Stéphane Chazelas başarısız giriş yapmaya neden olur var olmayan bir dosyayı kaynak, POSIX kabuklarda, dikkat çekmişlerdir.

Ancak dosyanın var olup olmadığını görmek için bir test eklemek ve daha sonra kaynak yapmaya çalışmak, yarış durumu adı verilen bir şeye neden olabilir. Bir gelişme olduğunda nvm.sharadaki [ -s nvm.sh ]ve . nvm.shbu tam olarak onlar çok daha nadiren de olsa önlemek için çalışıyoruz hata neden olur.

Genel olarak, yarış koşullarını önlemenin yolu sadece yapmak istediğiniz şeyi denemek, ardından başarısız olursa hatayı ele almaktır, örn.

. "$NVM_DIR/nvm.sh" || echo "Sourcing $NVM_DIR/nvm.sh failed" >&2

Bu, POSIX mermilerinde işe yaramaz, çünkü yukarıdaki gibi .başarısız olursa, herhangi bir hata ele alınmadan önce kabuğun hemen çıkmasına neden olur.

Cevabım POSIX mermilerinin bu soru ile ilgili olmadığını, çünkü .bash_profilePOSIX modunda çalışmaması gerektiğini savunuyor . Bu yüzden yukarıdaki kodu yine de yapabiliriz.

En güvenli olması için, /unix//a/383581/3169'da açıklanan tekniği kullanarak POSIX modunun etkin olmadığından emin olabilir veya POSIX modunun devre dışı bırakıldığından emin olabiliriz .

Stéphane'nin cevabı, nvm yazarının amacı olduğunu düşündüğüm tüm POSIX mermilerinin nasıl ele alınacağına dair bazı yararlı önerilere sahip, ancak buradaki sorunun sorduğundan biraz farklıydı, bu yüzden hedefinizin ne olduğuna bağlı olarak birden fazla olası yaklaşımımız var. .

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.