Bir komutun boş bir dize çıkıp çıkmadığını test edin


237

Bir komutun boş bir dize çıkıp çıkmadığını nasıl sınayabilirim?


7
Bir komutu (başarı ve başarısızlık üzerine 1 veya 2 için küçük bir tamsayı çıkış kodu, genellikle 0 döndürür) bir dize dönmez, ancak olabilir çıktı bazı veriler, bir dizede koymak.
Basile Starynkevitch

@BasileStarynkevitch bu şey need.I biliyorum komut döner çıkış Ben çıkış kontrol etmek gerek 0.so benim komutlar çıkış kodu daima code.But olduğunu
Barp

1
Bazı Bash komut dosyası öğreticisini okumalısınız tldp.org/LDP/abs/html
Basile Starynkevitch

Joey Hess bunun için bir yardımcı programa sahip ifne. joeyh.name/code/moreutils
tripleee

Yanıtlar:


309

Önceden, soru bir dizinde dosya olup olmadığını nasıl kontrol edeceğinizi soruyordu. Aşağıdaki kod bunu başarır, ancak daha iyi bir çözüm için rsp'nin cevabına bakın .


Boş çıktı

Komutlar değer döndürmez - çıktı verir. Komut değiştirme kullanarak bu çıktıyı yakalayabilirsiniz ; örn $(ls -A). Bash'te boş olmayan bir dizeyi şu şekilde test edebilirsiniz:

if [[ $(ls -A) ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Bunun -Ayerine -asembolik current ( .) ve parent ( ..) dizin girişlerini atladığı için kullandığımı unutmayın .

Not: Yorumlarda belirtildiği gibi, komut ikamesi sondaki satırları yakalamaz . Bu nedenle, komut yalnızca yeni satırlar çıkarırsa, ikame hiçbir şey yakalamaz ve sınama false değerini döndürür. Çok olası olmasa da, yukarıdaki örnekte bu mümkündür, çünkü tek bir yeni satır geçerli bir dosya adıdır! Bu cevapta daha fazla bilgi .


Çıkış kodu

Komutun başarıyla tamamlandığını kontrol etmek isterseniz $?, son komutun çıkış kodunu içeren (başarı için sıfır, hata için sıfır olmayan) inceleyebilirsiniz. Örneğin:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

Daha fazla bilgi burada .


4
Sen atlayabilirsiniz -n sadece olacağım böylece,if [[ $(ls -A) ]]; then
kullanıcı

6
Bu yöntem, yalnızca yeni satırlar çıkaran komutlar için false değerini verir.
Ciro Santilli 法轮功 11: 病 六四 事件 法轮功

89

TL; DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

Orijinalimi geliştirmek için bir öneri için netj'e teşekkürler :
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi


Bu eski bir soru ama bazı iyileştirme veya en azından açıklığa kavuşturulması gereken en az iki şey görüyorum.

İlk sorun

Gördüğüm ilk sorun, burada verilen örneklerin çoğunun işe yaramadığı . Her ikisi de boş dizinlerde boş olmayan dizeler çıkaran ls -alve ls -Alkomutlarını kullanırlar . Bu örnekler her zaman dosya olmasa bile dosya olduğunu bildirir .

Bu nedenle sadece kullanmalısınız ls -A- Neden herkes -lherhangi bir çıktı olup olmadığını test etmek için "uzun bir liste formatı kullan" anlamına gelen anahtarı kullanmak istesin ki, yine de herhangi bir çıktı olup olmadığını test etmek?

Yani buradaki cevapların çoğu yanlış.

İkinci sorun

İkinci sorun ise olmasıdır bazı cevaplar cezası çalışmak (o bu değil kullanmak ls -alveya ls -Alancak ls -Ahepsi böyle bir şey yapmak yerine):

  1. bir komut çalıştır
  2. tüm çıktısını RAM'de tamponlar
  3. çıktıyı büyük bir tek satırlık dizeye dönüştürmek
  4. bu dizeyi boş bir dizeyle karşılaştır

Bunun yerine ne önerirsiniz:

  1. bir komut çalıştır
  2. çıkışındaki karakterleri saklamadan sayın
    • veya daha iyisi - maksimum 1 karakter sayısını sayın using head -c1
      ( bu fikri aşağıdaki yorumlarda paylaştığı için netj sayesinde )
  3. bu sayıyı sıfırla karşılaştır

Örneğin, yerine:

if [[ $(ls -A) ]]

Kullanmak istiyorum:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

Onun yerine:

if [ -z "$(ls -lA)" ]

Kullanmak istiyorum:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

ve bunun gibi.

Küçük çıktılar için sorun olmayabilir, ancak daha büyük çıktılar için fark önemli olabilir:

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

Şununla karşılaştırın:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

Ve daha da iyisi:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

Tam örnek

Will Vousden'in cevabından güncellenen örnek:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Netj tarafından yapılan önerilerden sonra tekrar güncellendi :

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

Jakeonfire tarafından ek güncelleme :

grepeşleşme yoksa hata ile çıkılır. Sözdizimini biraz basitleştirmek için bundan yararlanabiliriz:

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

Boşluğu atma

Test ettiğiniz komut boş bir dize olarak değerlendirmek istediğiniz bazı boşluklar verebilirse, bunun yerine:

| wc -c

kullanabilirsin:

| tr -d ' \n\r\t ' | wc -c

veya head -c1:

| tr -d ' \n\r\t ' | head -c1 | wc -c

ya da böyle bir şey.

özet

  1. İlk olarak, çalışan bir komut kullanın .

  2. İkincisi, RAM'de gereksiz yere saklamaktan ve potansiyel olarak büyük verilerin işlenmesinden kaçının.

Cevap, çıktının her zaman küçük olduğunu belirtmedi, bu nedenle büyük çıktı olasılığının da dikkate alınması gerekiyor.


3
[[ $(... | head -c | wc -c) -gt 0 ]]veya [[ -n $(... | head -c1) ]]daha iyidir çünkü wc -ckomutun tüm çıktısını tüketmek zorunda kalacaktır.
netj

@netj Bu çok iyi bir fikir. Güncellenmiş cevabımı görün - Önerinizi ekledim. Çok teşekkürler!
rsp

Çıktının nasıl ele geçirileceği hakkında bir fikrin var mı?
Fahrradflucht

Bence orijinal (başsız) genel durumda daha iyidir. Çıktı yazmaya başlar başlamaz komutu öldürmek her zaman iyi bir fikir değildir!
stk

@stk Çoğu program bir öldürme sinyali aldıktan sonra güvenli bir şekilde çıkacaktır.
cambunctious

40
if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

Bu, komutu çalıştıracak ve döndürülen çıktının (dize) sıfır uzunlukta olup olmadığını kontrol edecektir. Diğer bayraklar için 'test' kılavuz sayfalarını kontrol etmek isteyebilirsiniz .

Kontrol edilen bağımsız değişkenin etrafında "" kullanın, aksi takdirde boş sonuçlar (denetlenecek) ikinci bir bağımsız değişken olmadığından bir sözdizimi hatasına neden olur!

Not: Bu ls -lahep döner .ve ..böylece olmaz çalışması sonuçlarını kullanarak bkz ls manuel sayfaları . Dahası, bu rahat ve kolay görünse de, kolayca kırılacağını düşünüyorum. Sonuca bağlı olarak 0 veya 1 döndüren küçük bir komut dosyası / uygulama yazmak çok daha güvenilirdir!


$($(
Arka

Haklısınız, her zaman burada kullanmak daha iyi bir uygulamadır, ancak burada yuvaya ihtiyacımız yoktur.
Veger

23

Zarif, bash sürümünden bağımsız bir çözüm isteyenler (aslında diğer modern kabuklarda çalışmalıdır) ve hızlı görevler için tek astar kullanmayı sevenler için. İşte başlıyoruz!

ls | grep . && echo 'files found' || echo 'files not found'

(bahsedilen yorumlardan biri olarak not edin ls -alve aslında, sadece -lve -ahepsi bir şey döndürecek, bu yüzden cevabımda basit kullanıyorumls


5
Eğer kullanırsanız grep -q ^bunun yerine, yeni satır karakterleri de eşleştirilecek ve grep standart çıkışa şey yazdırmaz. Ayrıca, giriş akışının bitmesini beklemek yerine herhangi bir girdi alır almaz çıkacaktır.
mortehu

İle başlamalıdır ls -AEğer dosyaları nokta (veya dizinleri) eklemek istiyorsanız
wrlee

13

Bash Referans Kılavuzu

6.4 Bash Koşullu İfadeleri

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

Steno versiyonunu kullanabilirsiniz:

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi

1
Parantez içinde -z veya -n eksik [[...]].
71GA

4
@ 71GA: Belki de göz ardı etmek kolaydır, ancak dikkatlice bakarsanız bunun sadece stringbir eşanlamlı olduğunu görürsünüz -n string.
kullanıcı

10

Jon Lin'in yorumladığı gibi, ls -alher zaman çıktı için ( .ve ..). ls -AlBu iki dizinden kaçınmak istiyorsunuz .

Örneğin, komutun çıktısını bir kabuk değişkenine koyabilirsiniz:

v=$(ls -Al)

Daha eski, tartışılamaz bir gösterim

v=`ls -Al`

ama seçilebilir gösterimi tercih ederim $(...)

Bu değişkenin boş olup olmadığını test edebilirsiniz

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

Ve her ikisini de if [ -n "$(ls -Al)" ]; then


5

ls -alKomutun çıktısını istediğinizi tahmin ediyorum , bu yüzden bash'da şöyle bir şey olurdu:

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi

Bu işe yaramaz: komut lshiçbir zaman yürütülmez. Yalnızca değişkenin genişlemesinin LS boş olup olmadığını kontrol edersiniz .
gniourf_gniourf

1
@gniourf_gniourf - bunu düşündüren nedir? Ters tırnak büyü kuşkusuz değil gibi güzel veya esnek $ () olarak, ama ... işleri
tink

-aAnahtar (orada dosyalardır olsun veya olmasın yani o içeriği döndürür) her zaman örtük olmadığı için hiçbir içeriği döndürür asla .ve ..dizinleri mevcut.
wrlee

3

İşte daha uç durumlar için bir çözüm:

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

Bu çalışacak

  • tüm Bourne mermileri için;
  • komut çıktısının tümü sıfırsa;
  • çıktı boyutundan bağımsız olarak verimli;

ancak,

  • komut veya alt süreçleri çıktı alındıktan sonra öldürülecektir.

1

Şimdiye kadar verilen tüm cevaplar boş olmayan bir dizeyi sonlandıran ve çıktı veren komutlarla ilgilidir .

Çoğu aşağıdaki duyularda kırılır:

  • Yalnızca satırsonu çıktıları veren komutlarla düzgün bir şekilde ilgilenmezler;
  • Bash≥4.4'ten başlayarak çoğu komut null bayt verirse (komut yerine koyma kullandıklarından) standart hata spam gönderir;
  • çoğu tam çıkış akışını keser, bu yüzden cevaplamadan önce komutun sona ermesini bekler. Bazı komutlar hiçbir zaman sona ermez (örneğin, deneyin yes).

Tüm bu sorunları gidermek ve aşağıdaki soruyu verimli bir şekilde cevaplamak için,

Bir komutun boş bir dize çıkıp çıkmadığını nasıl sınayabilirim?

kullanabilirsiniz:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Komutun read' -tseçeneğini kullanarak belirli bir süre içinde boş olmayan bir dize çıkıp çıkmadığını sınamak için biraz zaman aşımı da ekleyebilirsiniz . Örneğin, 2,5 saniyelik bir zaman aşımı için:

if read -t2.5 -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

Remark. Bir komutun boş olmayan bir dize çıkıp çıkmadığını belirlemeniz gerektiğini düşünüyorsanız, büyük olasılıkla bir XY sorununuz vardır.


Bu cevabın oldukça az takdir edildiğini düşünüyorum. Burada 3 giriş senaryonun her biri için 100 tekrarı kullanılarak bazı çözümler performans test edilmiştir ( seq 1 10000000, seq 1 20ve printf""). Bu okuma yaklaşımının kazanamadığı tek eşleşme -z "$(command)", boş bir girdi yaklaşımıydı. O zaman bile, sadece yaklaşık% 10-15 oranında kaybeder. Etrafta kırılıyor gibi görünüyorlar seq 1 10. Ne olursa olsun, giriş boyutu büyüdükçe -z "$(command)"yaklaşım o kadar yavaş olur ki , herhangi bir değişken boyutlu giriş için kullanmaktan kaçınırdım.
abathur

0

İşte bazı komutların std-out ve std-err'lerini geçici bir dosyaya yazan ve daha sonra bu dosyanın boş olup olmadığını kontrol eden alternatif bir yaklaşım. Bu yaklaşımın bir yararı, her iki çıkışı da yakalaması ve alt kabuklar veya borular kullanmamasıdır. Bu son hususlar önemlidir, çünkü yakalama bash çıkış işlemesine müdahale edebilirler (örneğin burada )

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"

0

Bazen çıktıyı, boş değilse, başka bir komuta geçirmek için kaydetmek istersiniz. Eğer öyleyse,

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

Bu şekilde, rmliste boşsa komut askıda kalmaz.

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.