bash olmadan birden fazla koşul değilse bash?


23

Bir if if ifadesinde birden çok koşulu birleştirmek ve kombinasyonu olumsuzlamak istiyorum. Koşulların basit bir kombinasyonu için aşağıdaki çalışma kodu var:

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ; then
  # do stuff with the files
fi

Bu iyi çalışıyor. Eğer bunu reddetmek istersem, aşağıdaki çalışma kodunu kullanabilirim:

if ! ( [ -f file1 ] && [ -f file2 ] && [ -f file3 ] ) ; then
  echo "Error: You done goofed."
  exit 1
fi
# do stuff with the files

Bu da beklendiği gibi çalışıyor. Ancak bana öyle geliyor ki parantezlerin aslında orada ne yaptığını bilmiyorum. Ben istiyorum sadece gruplandırmak için bunları kullanmak için, ama aslında bir altkabuk üretiyor? (Nasıl söyleyebilirim?) Öyleyse, bir alt kabuk ortaya çıkmadan koşulları gruplamanın bir yolu var mı?


Eşdeğer ifadeyi belirlemek için de mantıksal mantık kullanabileceğimi düşünüyorum: if ! [ -f file1 ] || ! [ -f file 2 ] || ! [ -f file3 ] ; thenama daha genel bir cevap istiyorum.
Wildcard

Ayrıca parantez içinde de olumsuz olabilir, örneğinif [[ ! -f file1 ]] && [[ ! -f file2 ]]; then
DopeGhoti

1
Evet, yeni test ettim if [ ! 1 -eq 2 ] && [ ! 2 -eq 3 ]; then echo yep; five işe yarıyor. Her zaman alışkanlık olarak çift ayraçlı testler yazarım. Ayrıca, olmadığından emin olmak için bashdaha fazla test yaptım if /bin/test ! 1 -eq 2 && /bin/test ! 2 -eq 3 ; then echo yep; five bu şekilde de çalışıyor.
DopeGhoti

1
@Wildcard - [ ! -e file/. ] && [ -r file ]dizinleri bırakır . İstediğiniz gibi reddedin. Tabii ki, işte -dböyle.
mikeserv

1
İlgili soru (benzer ancak çok fazla tartışma ve yanıt çok yararlı değil): unix.stackexchange.com/q/156885/135943
Wildcard

Yanıtlar:


32

Bunun { list;}yerine kullanmanız gerekir (list):

if ! { [ -f file1 ] && [ -f file2 ] && [ -f file3 ]; }; then
  : do something
fi

Her ikisi de Gruplama Komutlarıdır , ancak { list;}geçerli kabuk ortamında komutları çalıştırır.

Unutmayın, ; içinde { list;}listelerinden ayırmak için gerekli olan }ters sözcük, sen de diğer sınırlayıcı kullanabilirsiniz. Sonraki boşluk (veya başka bir sınırlayıcı) {da gereklidir.


Teşekkür ederim! İlk başta beni harekete geçiren bir şey (kıvırcık parantezleri içeriye göre awkdaha sık kullandığım bashiçin) açık kıvırcık ayraçtan sonra boşluk ihtiyacı. "Diğer sınırlayıcıları da kullanabilirsiniz"; herhangi bir örnek?
Wildcard

@Wildcard: Evet, açık süslü ayraçtan sonra boşluk gereklidir. Diğer sınırlayıcı için bir örnek, sık sık bir işlev tanımlamanızı istediğiniz için yeni bir satırdır.
cuonglm

@don_crissti: İçinde zsh, boşluk kullanabilirsiniz
cuonglm

@ don_crissti: &aynı zamanda bir ayırıcı, kullanabilirsiniz { echo 1;:&}, birden fazla satırsonu da kullanılabilir.
cuonglm

2
Manuel herhangi meta karakter alıntı yaparak kullanabilirsiniz they must be separated from list by whitespace or another shell metacharacter.bash onlar şunlardır: metacharacter: | & ; ( ) < > space tab . Bu özel görev için herhangi birinin & ; ) space tabişe yarayacağına inanıyorum . .... .... zsh (bir yorum olarak) each sublist is terminated by :; ', &', & |',&!', or a newline.

8

İstediğinizi testelde etmek için tamamen işlevselliği kullanabilirsiniz . Man sayfasından test:

 ! expression  True if expression is false.
 expression1 -a expression2
               True if both expression1 and expression2 are true.
 expression1 -o expression2
               True if either expression1 or expression2 are true.
 (expression)  True if expression is true.

Durumunuz şöyle görünebilir:

if [ -f file1 -a -f file2 -a -f file3 ] ; then
    # do stuff with the files
fi

Kaçan parantezleri reddetmek için:

if [ ! \( -f file1 -a -f file2 -a -f file3 \) ] ; then
    echo "Error: You done goofed."
    exit 1
fi
# do stuff with the files

6

To portably kabuk içinde bir kompleks koşullu boşa, ya uygulamanız gerekir De Morgan kanununu ve iç aşağı tüm yol olumsuzlamasıydı itmek [aramalar ...

if [ ! -f file1 ] || [ ! -f file2 ] || [ ! -f file3 ]
then
    # do stuff
fi

... ya da kullanmalısın then :; else...

if [ -f file1 ] && [ -f file2 ] && [ -f file3 ]
then :
else
  # do stuff
fi

if ! commandtaşınabilir olarak kullanılamıyor ve ikisi de mevcut değil [[.

Toplam taşınabilirliğe ihtiyacınız yoksa , bir kabuk komut dosyası yazmayın . Aslında , rastgele seçilmiş bir Unix'te bulunma olasılığınız daha yüksektir ./usr/bin/perlbash


1
!günümüzde taşınabilir olan POSIX. Hala Bourne kabuğuyla birlikte gelen sistemlerde bile dosya sisteminde standart sözdiziminizi yorumlamak için kullanabileceğiniz başka bir yerde POSIX sh vardır.
Stéphane Chazelas

@ StéphaneChazelas Bu teknik olarak doğrudur, ancak benim deneyimime göre Perl'de bir komut dosyasını yeniden yazmak, POSIX uyumlu kabuk ortamını bulma ve etkinleştirme zorluğuyla uğraşmaktan daha kolaydır /bin/sh. Autoconf komut dosyaları önerdiğiniz gibi çalışır, ancak bence hepimiz bunun ne kadar az destek olduğunu biliyoruz.
zwol

5

diğerleri {bileşik komut ;}gruplamasını not etti , ancak bir kümede özdeş testler yapıyorsanız farklı bir tür kullanmak isteyebilirsiniz:

if  ! for f in file1 file2 file3
      do  [ -f "$f" ] || ! break
      done
then  : do stuff
fi

... başka bir yerde gösterildiği gibi, { :;}bileşik komutları yerleştirmede herhangi bir zorluk yoktur ...

Yukarıdaki (genellikle) normal dosyalar için test edildiğine dikkat edin . Yalnızca dizin olmayan mevcut , okunabilir dosyaları arıyorsanız :

if  ! for f in file1 file2 file3
      do  [ ! -d "$f" ] && 
          [   -r "$f" ] || ! break
      done
then  : do stuff
fi

Dizin olup olmadıklarını umursamıyorsanız:

if  ! command <file1 <file2 <file3
then  : do stuff
fi

... okunabilir , erişilebilir herhangi bir dosya için çalışır , ancak muhtemelen yazarlar hariç beşlilere asılır.


2

Bu, ana sorunuza tam bir cevap değil, ancak bir yorumda bileşik testinden (okunabilir dosya) bahsettiğinizi fark ettim; Örneğin,

if [ -f file1 ] && [ -r file1 ] && [ -f file2 ] && [ -r file2 ] && [ -f file3 ] && [ -r file3 ]

Bir kabuk işlevi tanımlayarak bunu biraz birleştirebilirsiniz; Örneğin,

readable_file()
{
    [ -f "$1" ]  &&  [ -r "$1" ]
}

Tatmak için (örn [ $# = 1 ].) Hata işleme ekleyin . Yukarıdaki ilk ififade artık

if readable_file file1  &&  readable_file file2  &&  readable_file file3

ve işlevin adını kısaltarak bunu daha da kısaltabilirsiniz. Benzer şekilde, işlevi not_readable_file()(veya nrfkısaca) tanımlayabilir ve olumsuzlamayı işleve dahil edebilirsiniz.


Güzel, ama neden sadece bir döngü eklemiyorsun? readable_files() { [ $# -eq 0 ] && return 1 ; for file in "$@" ; do [ -f "$file" ] && [ -r "$file" ] || return 1 ; done ; } (Feragatname: Bu kodu test ettim ve işe yarıyor, ancak tek katmanlı değildi. Burada yayınlamak için noktalı virgül ekledim, ancak yerleşimlerini test etmedim.)
Wildcard

1
İyi bir nokta. Fonksiyondaki kontrol (bağlaç) mantığının daha fazlasını gizlediği konusunda ufak bir endişe yaratacağım; Senaryoyu okur ve görür birileri if readable_files file1 file2 file3olmaz biliyorum işlevi yaptığını olsun VE ya VEYA - her ne kadar (a) oldukça sezgisel tahmin, (b) Senaryoyu dağıtarak değilseniz eğer, iyi yeter sen bunu anlamak, ve (c) işlev adını belirsizliğe kısaltmayı önerdiğim için, okunabilirlik hakkında konuşacak pozisyonum yok. … (Devam ediyor)
G-Man, '

1
(Devamı) ... BTW, fonksiyon ince sen yazdın yoldur, ama sen yerine for file in "$@" ; dosahip for file do- için o varsayılan in "$@"ve (biraz sağduyumuzun) ;sadece gereksiz ama aslında cesaretini değildir.
G-Man

0

Brace testlerinin içinde de reddedebilirsiniz, böylece orijinal kodunuzu yeniden kullanmak için:

if [[ ! -f file1 ]] || [[ ! -f file2 ]] || [[ ! -f file3 ]] ; then
  # do stuff with the files
fi

2
Bunun ||yerine&&
cuonglm

Test " orada olmayan dosyalardan herhangi biri değil", test " orada dosyalardan hiçbiri değil ". Kullanılması ||halinde yüklemi yürütülür herhangi dosyaların mevcut değildir ve sadece IFF değilse, tüm dosyaların mevcut değildir. DEĞİL (A VE B VE C) (DEĞİL A) VE (DEĞİL B) VE (DEĞİL C) ile aynıdır; orijinal soruya bakın.
DopeGhoti

@DopeGhoti, cuonglm haklı. Değilse (oradaki tüm dosyalar), bu yüzden bölüştüğünüzde bunun ||yerine ihtiyacınız var &&. Sorumun kendisinin altında ilk yorumuma bakın.
Wildcard

2
@DopeGhoti: not (a and b)ile aynıdır (not a) or (not b). Bkz en.wikipedia.org/wiki/De_Morgan%27s_laws
cuonglm

1
Perşembe olmalı. Perţembeleri asla asamadým. Düzeltildi ve ben gelecek kuşak için ağzımı ayağımda bırakacağım.
DopeGhoti

-1

Onları sadece gruplandırma için kullanmak istiyorum, ama aslında bir alt kabuk ortaya çıkıyor mu? (Nasıl söyleyebilirim?)

Evet, içindeki komutlar (...)alt kabukta çalıştırılır.

Alt kabukta olup olmadığınızı test etmek için geçerli kabuğunuzun PID'sini etkileşimli kabuğun PID'si ile karşılaştırabilirsiniz.

echo $BASHPID; (echo $BASHPID); 

Parantezlerin alt kabuk oluşturduğunu ve kıvırcık parantezlerin olmadığını gösteren örnek çıktı:

$ echo $BASHPID; (echo $BASHPID); { echo $BASHPID;}
50827
50843
50827
$ 

6
(){}
subshell

@heemayl, bu benim diğer kısmı ben bazı yolu var sorusuna-mi bakın veya göstermek kökenli olan ekranımda bir kabuktaki gerçeğini? (Bu gerçekten ayrı bir soru olabilir ama burada bilmek iyi olurdu.)
Wildcard

1
@heemayl Haklısın! Yaptığım echo $$ && ( echo $$ )PID en karşılaştırmak ve aynı vardı ama yanlış Bir şey daha denedim vardı söylüyorum yorumunu ibraz hemen önce. echo $BASHPID && ( echo $BASHPID )farklı PID'ler veriyor
David King

1
@heemayl durumun echo $BASHPID && { echo $BASHPID; }önerdiği ile aynı PID'yi verin. Eğitim için teşekkürler
David King

@DavidKing, test komutları için teşekkürler $BASHPID, bu benim eksik olduğum başka bir şey.
Wildcard
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.