bash yinelenen dosya listesi, boş olanlar hariç


33

Bunun basit olacağını düşünmüştüm - ama beklediğimden daha karmaşık.

Bir dizindeki belirli bir türdeki tüm dosyaları yinelemek istiyorum, bu yüzden şunu yazarım:

#!/bin/bash

for fname in *.zip ; do
   echo current file is ${fname}
done

Bu , dizinde en az bir eşleşen dosya olduğu sürece çalışır . Ancak eşleşen dosya yoksa, şunu alıyorum:

current file is *.zip

Sonra denedim:

#!/bin/bash

FILES=`ls *.zip`
for fname in "${FILES}" ; do
    echo current file is ${fname}
done

Döngü gövdesi dosya olmadığında çalışmaz, ancak ls'den hata alıyorum:

ls: *.zip: No such file or directory

Eşleşen dosyaları temiz bir şekilde işleyen bir döngü nasıl yazarım?


7
shopt -s nullglobFor döngüsünü çalıştırmadan önce ekleyin .
cuonglm

@ cuolnglm: spookily, bu RHEL5 kutusundaki (bash 3.2.25) boş bir listeden ziyade çalıştırılan betiğin ismini döndürüyor. FILES= ls * .zip ; for fname in "${FILES}"...fakat beklendiği gibi çalışırfor fname in *.zip ; do....
symcbean

3
Kullan for file in *.zip, kullan`ls ...` . @ cuonglm'in önerisi, *.zipkalıp herhangi bir dosyayla eşleşmediğinde hiçbir şeye genişlememesidir. lsargümanlar olmadan geçerli dizini listeler.
Stéphane Chazelas

1
Bu soru geçen neden çıktısını ayrıştırma ls: genellikle kaçınılması gereken bir neden değil ayrıştırmak ls? ; Ayrıca bu sayfanın üst kısmının yakınındaki BashGuide'un ParsingLs makalesine bakınız.
PM 2Ring

Yanıtlar:


34

İçinde seçeneği, değişmez bir dize olarak değerlendirilmek yerine, hiçbir şeyle eşleşen bir kalıbın "yok olacağı" bashşekilde ayarlayabilirsiniz nullglob:

shopt -s nullglob
for fname in *.zip ; do
   echo "current file is ${fname}"
done

POSIX kabuk komut dosyasında, var olduğunu doğrularsınız fname(ve aynı zamanda [ -f ], bunun normal bir dosya olup olmadığını (veya normal dosyaya işaretli) olup olmadığını ve dizin / fifo / aygıt ... gibi diğer türlerin olmadığını kontrol edin:

for fname in *.zip; do
    [ -f "$fname" ] || continue
    printf '%s\n' "current file is $fname"
done

Türüne bakılmaksızın adı biten tüm (gizli olmayan) dosyaların üzerine döngü yapmak istiyorsanız [ -f "$fname" ]bununla değiştirin .[ -e "$fname" ] || [ -L "$fname ].zip

Adı biten gizli dosyaları da dikkate almak istiyorsanız, *.zipile değiştirin ..*.zip .zip *.zip.zip


1
shopt -s nullglobUbuntu 17.04 benim için işe yaramadı, ama [ -f "$fname" ] || continueiyi çalıştı.
koppor

@koppor Kullanmıyormuşsunuz gibi geliyor bash.
chepner

2
set ./*                               #set the arg array to glob results
${2+":"} [ -e "$1" ] &&               #if more than one result skip the stat "$1"
printf "current file is %s\n" "$@"    #print the whole array at once

###or###

${2+":"} [ -e "$1" ] &&               #same kind of test
for    fname                          #iterate singly on $fname var for array
do     printf "file is %s\n" "$fname" #print each $fname for each iteration
done                                  

Buradaki bir yorumda, bir işlevi çağırmaktan bahsettiniz ...

file_fn()
    if     [ -e "$1" ] ||               #check if first argument exists
           [ -L "$1" ]                  #or else if it is at least a broken link
    then   for  f                       #if so iterate on "$f"
           do : something w/ "$f"
           done
    else   command <"${1-/dev/null}"    #only fail w/ error if at least one arg
    fi

 file_fn *

2

Kullan bul

export -f myshellfunc
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec bash -c 'myshellfunc "$0"' {} \;

Bunun için kabuk işlevinizi birlikte vermeniz GEREKİR export -f. Şimdi findyürütür bashyalnızca geçerli dir düzeyinde kabuk işlevi yürütür ve kalıntılar.


Hangi alt dizinler üzerinden recurses ve ben eşleşmeleri için bir bash işlevi (script değil) çağırmak istiyorum.
symcbean

@symcbean Tekli dizine sınırlama ve bash işlevlerini
yönetme için düzenleme yaptım

-3

Yerine:

FILES=`ls *.zip`

Deneyin:

FILES=`ls * | grep *.zip`

Bu şekilde eğer ls başarısız olursa (sizin durumunuzda olduğu gibi) başarısız çıktısını yakalar ve boş bir değişken olarak geri döner.

current file is      <---Blank Here

Buna "Hiçbir Dosya Bulunamadı" döndürmesi için buna bir mantık ekleyebilirsiniz.

#!/bin/bash

FILES=`ls * | grep *.zip`
if [[ $? == "0" ]]; then
    for fname in "$FILES" ; do
        echo current file is $fname
    done
else
    echo "No Files Found"
fi

Bu şekilde önceki komut başarılı olursa (0 değerinden çıkıldıysa) geçerli dosyayı yazdıracak, aksi takdirde "Dosya Bulunamadı" yazacaktır.


1
Daha grepiyi bir araç kullanarak sorunu gidermek yerine ( find) veya mevcut çözüm için ilgili ayarları değiştirerek (ile shopt -s nullglob)
Thomas Baruchel

1
OP’nin orijinal yazılarına yaptığı yorumuna göre, shopt -s nullglobçalışmaz. findCevabımı doğrularken denedim ve başarısız oldu. Dani'nin söylediği ihracat yüzünden düşünüyorum.
Kip K
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.