Test ve komutları birlikte bulursa bash'ı nasıl kullanabilirim?


25

Kilitlenme günlükleri olan bir dizine sahibim ve bir koşul komutunu bir find komutuna dayanarak bash betiğinde kullanmak istiyorum.

Günlük dosyaları bu biçimde saklanır:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

İf ifadesinin yalnızca son 5 dakikada değiştirilen belirli bir uygulamanın kilitlenme günlüğü varsa doğru dönmesini istiyorum. findBen kullanacağı konusunda komut:

find /var/log/crashes -name app-\*\.log -mmin -5

Bunu doğru bir ififadeyle nasıl birleştireceğimi bilmiyorum . Bunun işe yarayabileceğini düşünüyorum:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

Belirsiz olduğum birkaç alan var:

  • İf bayraklarına baktım ama hangisini kullanmam gerektiğinden emin değilim.
  • testYönetmeliğe mi ihtiyacım var yoksa doğrudan find komutunun sonuçlarına karşı mı işlem yapacağım, yoksa find... | wc -lsatır sayımı almak için mi kullanmalıyım?
  • Bu soruyu cevaplamak için% 100 gerekli değil, fakat testgeri dönüş komutlarını geri dönüş kodlarına karşı test etmek için mi? Ve de bunlar görünmez vardır - dış stdout/ ' stderr? manSayfayı okudum, ancak ne zaman kullanılacağı testve nasıl hata ayıklanacağı konusunda hala belirsizim .

Genel davaya asıl cevap kullanmaktır find ... -exec. Ayrıca neden
Wildcard

@Wildcard - ne yazık ki gelmez genel olayı çözmek: Orada birden fazla eşleşme olup, sadece eylem ihtiyaçları kez çalıştırın ve sen varken yapılacak bir işlemi gerekiyorsa o işi yapmazsa o işi yapmaz Eşleşme yok. İlki kullanarak çözülebilir ... -exec command ';' -quit, ancak sonuncusu için sonuç ayrıştırma dışında herhangi bir çözüm olduğuna inanmıyorum. Ayrıca, her iki durumda da find(örneğin sınırlayıcıları dosya adlarındaki karakterlerden ayırt edememe) sonucunun ayrıştırılmasıyla ilgili temel sorun , bu durumlarda sınırlayıcıları bulmanız gerekmediğinden geçerli değildir.
Jules

Yanıtlar:


27

[ve testeşanlamlıdır ( [zorunlu olanlar hariç ]), bu nedenle kullanmak istemezsiniz [ test:

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

testkoşul doğruysa sıfır değilse, bir sıfır durumu verir. Bu, çıkış durumunu kontrol etmek için herhangi bir programla değiştirilebilir, burada 0 başarıyı gösterir ve sıfır olmayan başarısızlığı gösterir:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

Bununla birlikte, yukarıdaki örneklerin tümü yalnızca programın çıkış durumuna karşı test eder ve programın çıktısını yok sayar.

Çünkü findherhangi bir çıktının üretilip üretilmediğini test etmeniz gerekecektir. -nboş olmayan bir dize için sınamalar:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

Komut satırında çağırarak help test, test argümanlarının tam listesi kullanılabilir bash.

Kullanıyorsanız bash(ve yapmıyorsanız sh), [[ condition ]]durumunuzda boşluklar veya diğer özel durumlar olduğunda daha öngörülebilir davranan kullanabilirsiniz . Aksi takdirde, genellikle kullanmakla aynıdır [ condition ]. [[ condition ]]Bu örnekte, mümkün olduğunca yaptığım gibi kullandım .

Ben de değişti `command`için $(command)de genel olarak benzer şekilde davranır, ancak iç içe komutlarla daha güzel olan.


echobaşarısız olabilir: deneyin echo 'oops' > /dev/full.
derobert,

Bu cevap sorunun kökünü aştı ancak neyin ne olduğunu tam olarak söylemekten kaçınıyor.
bahamat

9

findherhangi bir hata yoksa başarılı bir şekilde çıkacaktır, dolayısıyla herhangi bir dosya bulup bulamadığını bilmek için çıkış durumuna güvenemezsiniz. Ancak, dediğiniz gibi, kaç tane dosya bulduğunu sayabilir ve bu sayıyı test edebilirsiniz.

Böyle bir şey olurdu:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test(aka [) komutların hata kodlarını kontrol etmez, testler yapmak için özel bir sözdizimine sahiptir ve test başarılı olursa 0, veya hata kodu 1 ile çıkar. Ona ifilettiğiniz komutun hata kodunu kontrol eden ve cesedi temel alarak yürüten komuttur.

Bkz. man test(Veya help testkullanıyorsanız bash) ve help if(aynen).

Bu durumda, wc -lbir sayı çıkacaktır. Bu sayının büyük olup olmadığını sınamak için testseçeneğini -gtkullanın 0. Öyleyse, test(veya [) çıkış koduyla döner 0. ifbu çıkış kodunu başarı olarak yorumlayacak ve kodu kendi gövdesinde çalıştıracaktır.


1

Bu olabilir

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

veya

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

Komutlar testve [ … ]tam olarak eş anlamlıdır. Tek fark onların adı ve son argümanı olarak [kapanmayı gerektiren bir gerçektir ]. Her zaman olduğu gibi, komut değiştirme çevresinde çift tırnak işareti kullanın; aksi halde komutun çıktısı findsözcüklere bölünür ve burada birden fazla eşleşen dosya varsa (ve hiçbir argüman olmadığında [ -n ]doğru) Oysa ki [ -n "" ]hangisi yanlışsa).

Ksh, bash ve zsh'da, ancak külde değil [[ … ]], farklı ayrıştırma kurallarına sahip olanları da kullanabilirsiniz : [sıradan bir komuttur, oysaki [[ … ]]farklı bir ayrıştırma yapısıdır. İçinde çift alıntıya ihtiyacınız yoktur [[ … ]](incinmemelerine rağmen). Hala ;komuttan sonra ihtiyacın var .

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

Bu potansiyel olarak verimsiz olabilir: eğer birçok dosya varsa /var/log/crashes, find hepsini keşfedecektir. Bir eşleşme bulur bulmaz ya da hemen sonra bulmayı durdurmalısınız. GNU bulma (gömülü olmayan Linux, Cygwin) ile -quitbirincil olanı kullanın .

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

Diğer sistemlerde, boru ile findiçine headen azından ilk maçında kısa bir süre sonra çıkmak için (bulmak bir kırık borunun ölecek).

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

(Kullanabilirsiniz head -c 1sizin eğer headkomut bunu desteklemektedir.)


Alternatif olarak, zsh kullanın.

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then

1

Bu çalışmalı

if find /var/log/crashes -name 'app-\*\.log' -mmin -5 | read
then
  service myapp restart
fi

0
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

burada uygun bir çözümdür.

-exec service myapp restart ';'bir findşeyi yorumlamak için kabuğa ihtiyaç duymak yerine doğrudan çalıştırmak istediğiniz komutu çağırmaya neden olur .

-quitfindKomutun işlenmesinden sonra çıkmasına neden olur , böylece kritere uyan birden fazla dosya olması durumunda komutun tekrar çalıştırılmasını önler.

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.