Bir betiği çalıştırmak için bir kalıpla eşleşen dosyalar olup olmadığını test edin


29

ifBelirli bir kalıpla eşleşen dosya olup olmadığını test etmek için bir ifade yazmaya çalışıyorum . Bir dizinde bir metin dosyası varsa, verilen bir betiği çalıştırması gerekir.

Şu anda kodum:

if [ -f /*.txt ]; then ./script fi

Lütfen biraz fikir verin; Sadece .txtdizinde bir komut dosyası varsa çalıştırmak istiyorum .


3
"Dizin" in olması gerektiğinden emin misiniz /? Ayrıca, daha önce bir noktalı virgül eksik fi.
13'te

Karşılaştığım en temiz sağlam çözüm, yığın akışında buradafind açıklandığı şekilde kullanmaktır .
Joshua Goldberg

Yanıtlar:


39
[ -f /*.txt ]

orada sadece true dönecekti birini (ve tek) olmayan gizli dosyayı /adlarına uçları içinde .txto dosya normal bir dosya veya normal dosyaya bir sembolik bağdır ve eğer.

Bunun nedeni, joker karakterlerin komuta geçilmeden önce kabuk tarafından genişletilmesidir (burada [).

Orada bir parça varsa /a.txtve /b.txt, [5 argümanları geçilecek: [, -f, /a.txt, /b.txtve ]. [daha sonra -fçok fazla argüman verildiğinden şikayet ederdi .

*.txtDesenin en az bir gizli olmayan dosyaya genişlediğini kontrol etmek istiyorsanız (normal veya değil):

shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
  ./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"

shopt -s nullglobolduğu bashbelirli fakat kabukları gibi ksh93, zsh, yash, tcsheşdeğer ifadeler var.

Dizinin içeriğini okuyarak bu dosyaları bulduğunu, bu dosyalara erişmediğini ve bu dosyalara erişmediğini unutmayın; bu lsda stat, kabuk tarafından hesaplanan dosyaların listesi veya benzeri komutları çağıran çözümlerden daha verimli olmasını sağlar .

Standart sheşdeğer olacaktır:

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "$@"
esac

Sorun Bourne veya POSIX mermilerinde, eğer bir kalıp uyuşmuyorsa, kendine yayılıyor. Yani, *.txtgenişlerse , dizinde dosya *.txtolmadığı için .txtveya adı verilen bir dosya olduğu için bunun ne olduğunu bilmiyorsunuz *.txt. Kullanımı [*].txt *.txt, ikisi arasında ayrım yapılmasını sağlar.


[ -f /*.txt ]kıyasla oldukça hızlı compgen.
Daniel Böhmer

@ DanielBöhmer [ -f /*.txt ]yanlış olurdu, ama benim gizli olmayan txt dosyaları olan 3425dosyaları içeren bir dizindeki testimde (benim durumumda 10000 kez tekrarlandığında her ikisi için 20.5 saniye) olduğu kadar hızlı görünüyor . 94compgen -G "*.txt" > /dev/null 2>&1set -- *.txt; [ "$#" -gt 0 ]
Stéphane Chazelas

11

Her zaman kullanabilirsiniz find:

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

Açıklama:

  • find . : geçerli dizinde arama yap
  • -maxdepth 1: alt dizinlerde arama yapma
  • -type f : sadece normal dosyaları ara
  • name "*.txt" : biten dosyaları ara .txt
  • 2>/dev/null : hata mesajlarını yönlendirmek /dev/null
  • | grep -q . : Herhangi bir karakter için grep, hiçbir karakter bulunmazsa false döndürür.
  • && ./script: ./scriptSadece önceki komut başarılı olduysa çalıştır ( &&)

2
findyalnızca dosya ararken sorun yaşarsa yanlış döndürür, dosya bulamazsa döndürür. Bir grep -q .şey bulup bulmadığını kontrol etmek için çıkışı borulamak istiyorsunuz .
Stéphane Chazelas

@StephaneChazelas elbette oldukça haklısınız. Yine de garip, test ettim ve işe yarayacak gibiydi. Garip bir şey yapmış olmalı, çünkü artık değil. Ne zaman "dosyaları bulmakta zorluk çekersiniz"?
terdon

@terdon, bazı dizin erişilemez olduğunda veya G / Ç hataları veya yaptığı herhangi bir sistem çağrısı tarafından döndürülen herhangi bir hata gibi. Bu durumda, sonra deneyin chmod a-x ..
Stéphane Chazelas

8

Muhtemel bir çözüm de Bash yerleşiktir compgen. Bu komut, genel bir desen için olası tüm eşleşmeleri döndürür ve herhangi bir dosyanın eşleşip eşleşmediğini gösteren bir çıkış koduna sahiptir.

compgen -G "/*.text" > /dev/null && ./script

Daha hızlı olan çözümler ararken bu soruyu buldum.


1
İyi bulmak! Çok baytlı bir yerel ayardaysanız, biraz geliştirebilirsiniz LC_ALL=C compgen -G "*.txt" > /dev/null.
Stéphane Chazelas

7

İşte bunu yapmak için bir astar:

$ ls
file1.pl  file2.pl

dosya var

$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists

dosya yok

$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist

Bu yaklaşım ||ve &&operatörleri bash olarak kullanır. Bunlar "veya" ve "ve" operatörleridir.

Yani stat komutu $?0'a eşit döndürürse , ilk echoçağrılır, eğer 1 döndürürse, ikincisi echoçağrılır.

stat sonuçlarını döndür

# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1

# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0

Bu soru yığın akışında kapsamlı olarak ele alınmıştır:


1
Neden sigara standardını kullanabilirsiniz statzaman ls -daynı yapabilir?
Stéphane Chazelas

ls -dBir rehber listelerini düşündüm ? ls -d *.plMesela, içinde dosyaları olan bir dizini listelemeye çalıştığımda işe yaramadı .
slm

Sen solundaki ifadeyi değiştirebilir &&tarafından ls *.txtve hem de çalışacaktır. /dev/nullStdout ve stderr öğesini @slm tarafından önerilen şekilde gönderdiğinizden emin olun .
unxnut

1
Eğer kullanırsanız ls *.txtve hiçbir dosya dizin içinde mevcut bulunmaktadır bu dönecektir $? = 2, hangi hala iş sonra eğer alırım ama bu seçtiğiniz için benim nedenlerinden biriydi statüzerinde ls. Başarı için 0, başarısızlık için 1 istedim.
slm

ls -diçeriği yerine dizinleri listelemek içindir. Yani ls -dsadece lstatdosyada, tıpkı GNU statgibi. Sıfır olmayan çıkış durumu komutlarının başarısızlığa dönmesi sisteme özgüdür, bunlarda varsayımlarda bulunmak pek mantıklı değildir.
Stéphane Chazelas

4

Chazelas'ın işaret ettiği gibi, joker karakter genişletmesi birden fazla dosyayla eşleşirse betiğiniz başarısız olur.

Ancak, dolaşmak için kullandığım bir numara var ( hatta bundan pek hoşlanmam ).

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

Nasıl çalışır?

Joker karakter genişletmesi bir dosya isimleri dizisiyle eşleşir, eğer birincisi varsa birincisini alırız, aksi takdirde eşleşmezse null olur.


IMO buradaki en kötü cevap. Hepsi de çok korkunç görünüyor, sanki temel bir özellik dilde yokmuş gibi.
plugwash

@plugwash bu kasıtlı ... * nix kabuk komut dosyalarının bazı temel akış kontrolleri ve başka olasılıkları ve sonları var, ancak günün sonunda işi diğer komutları birbirine yapıştırmak. Eğer bash
berbatsa

2
Bu yanlış bir mantık (ve teklifleri kaçırıyorsunuz). İlk eşleşen dosyanın normal bir dosya olup olmadığını kontrol eder. Normal olmayan bir dosya olabilir, ancak normal.txt tipte olan birkaç başka dosya olabilir . Mesela sonra deneyin . mkdir a.txt; mkfifo b.txt; echo regular > c.txt
Stéphane Chazelas

1

Olarak basit:

cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi

wc -l genişletilmiş joker karakterdeki satırları sayar.


1
Aşağı oy: Bu, az miktarda kodda inanılmaz sayıda başlangıç ​​antipatterns toplar. Çıktıyı çözümlememelisinizls ve neredeyse hiçbir zaman $?doğrudan incelememelisiniz, çünkü ifzaten bunu yapıyor. Ayrıca, bir şey olup olmadığını görmek için kullanmawc benzer şekilde yanlış yönlendirilir.
üçlü

0

Önceki dizi çözümünü severim, ancak bu çok sayıda dosyayla israfa neden olabilir - kabuk diziyi oluşturmak için çok fazla bellek kullanır ve yalnızca ilk öğe test edilirdi.

İşte dün test ettiğim alternatif bir yapı:

$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi yes $ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi no

Grep'ten çıkış değeri kontrol yapısı içerisindeki yolu belirliyor gibi görünüyor. Bu aynı zamanda kabuk kalıplarından ziyade düzenli ifadelerle test eder. Sistemlerimden bazılarında çok daha karmaşık regex eşleşmelerine izin veren "pcregrep" komutu var.

(Bu cevabı ayrıştırma için yukarıdaki eleştiriyi okuduktan sonra komut yerine "ls" yi kaldırmak için düzenledim.)


-2

Eğer bir if cümlesi kullanmak istiyorsanız, sayımı değerlendirin:

if (( `ls *.txt 2> /dev/null|wc -l` ));then...
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.