Döngü için boşluk bırakan dosya isimleri, find komutu


33

Birden fazla alt klasördeki tüm dosyaları arayan ve bir tar için arşivleyen bir betiğim var. Benim senaryom

for FILE in `find . -type f  -name '*.*'`
  do
if [[ ! -f archive.tar ]]; then

  tar -cpf archive.tar $FILE
else 
  tar -upf archive.tar $FILE 
fi
done

Find komutu bana aşağıdaki çıktıyı verir

find . -type f  -iname '*.*'
./F1/F1-2013-03-19 160413.csv
./F1/F1-2013-03-19 164411.csv
./F1-FAILED/F2/F1-2013-03-19 154412.csv
./F1-FAILED/F3/F1-2011-10-02 212910.csv
./F1-ARCHIVE/F1-2012-06-30 004408.csv
./F1-ARCHIVE/F1-2012-05-08 190408.csv

Ancak FILE değişkeni, yalnızca yolun ilk bölümünü depolar. / F1/F1-2013-03-19 ve sonra bir sonraki kısmı 160413.csv .

readBir süre döngü ile kullanmayı denedim

while read `find . -type f  -iname '*.*'`;   do ls $REPLY; done

ama aşağıdaki hatayı alıyorum

bash: read: `./F1/F1-2013-03-19': not a valid identifier

Birisi alternatif bir yol önerebilir mi?

Güncelleştirme

Aşağıdaki cevaplarda önerildiği gibi scriptleri güncelledim

#!/bin/bash

INPUT_DIR=/usr/local/F1
cd $INPUT_DIR
for FILE in "$(find  . -type f -iname '*.*')"
do
archive=archive.tar

        if [ -f $archive ]; then
        tar uvf $archive "$FILE"
        else
        tar -cvf $archive "$FILE"
        fi
done

Aldığım çıktı

./test.sh
tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory
tar: Exiting with failure status due to previous errors

4
IFS=$'\n'`Her döngü tarafından ayrıştırmak için döngü
için`

Yanıtlar:


36

Kullanımı forile findörneğin bkz Burada yanlış bir yaklaşımdır bu writeup Açacağınız solucanların kutu hakkında.

Önerilen yaklaşım kullanımına olduğunu find, whileve readolarak tarif burada . Aşağıda sizin için çalışması gereken bir örnek verilmiştir:

find . -type f -name '*.*' -print0 | 
while IFS= read -r -d '' file; do
    printf '%s\n' "$file"
done

Bu şekilde, dosya adlarını null ( \0) karakterleriyle sınırlarsınız, bu, boşluktaki ve diğer özel karakterlerdeki çeşitliliğin sorunlara neden olmayacağı anlamına gelir.

Bir arşivi findbulan dosyalar ile güncellemek için , çıktısını doğrudan şuraya aktarabilirsiniz tar:

find . -type f -name '*.*' -printf '%p\0' | 
tar --null -uf archive.tar -T -

Arşivin var olup olmadığına göre ayırma zorunluluğunuz olmadığını unutmayın tar. Ayrıca -printf, ./bitin arşive eklenmesini önlemek için burada kullanımına dikkat edin .


Teşekkürler, neredeyse işe yarıyor. Tek şey onun ./katran olarak arşivlenmesi . ./.tar tar: ./archive.tar: file is the archive; not dumped
Ubuntuser

Sen görmek için basit bir kontrol ekleyebilirsiniz @Ubuntuserif [[ "$FILE" == "./" ]]; then continue
kiri

@Ubuntuser: Sen önleyebilirsiniz ./ile biraz -printfbkz güncellenen cevap. Ancak sadece mevcut dizine atıfta bulunduğundan, dahil edilmişse de olmasa da herhangi bir fark yaratmaması gerekir. Ayrıca find/tarkullanmak isteyebileceğiniz alternatif bir kombinasyonu da ekledim.
Thor

sortYinelemeden önce sonuçları almak isteyenler için sort -zboş ayırıcıya ihtiyacınız olacak.
Adambean

13

forDöngüyü şöyle alıntılamayı deneyin :

for FILE in "`find . -type f  -name '*.*'`"   # note the quotation marks

Tırnak işaretleri olmadan bash, boşlukları ve newlines ( \n) işlevini iyi yapmaz ...

Ayrıca ayarı deneyin

IFS=$'\n'

1
IFS için +1. Bu, ayırıcı karakteri bozar.
Ray

1
Bu benim için işe yaradı çözüm. commSıralanan dosya listelerini karşılaştırmak için kullanıyordum ve dosya isimleri çalışmadı. Sonra cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html ve IFS = $ (echo -en "\ n \ b") ayarını yaptığım çözümün benim için çalıştığını gördüm .
pbhj

Çift tırnak eklemeli, şık, sade, güzel - teşekkürler!
Büyük Zengin


4

Düzgün alıntılamaya ek olarak, findNULL ayırıcı kullanmasını söyleyebilir ve sonuçları bir whiledöngü halinde okuyup işleyebilirsiniz.

while read -rd $'\0' file; do
    something with "$file"
done < <(find  . -type f -name '*.*' -print0)

Bu, POSIX uyumlu tüm dosya adlarını işlemelidir - bkz. man find

   -print0
          True; print the full file name on the standard output, followed by a null character (instead of the newline character that  -print  uses).   This  allows  file
          names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output.  This option corresponds to the
          -0 option of xargs.

bu benim için çalışan tek çözüm. Teşekkürler.
codefreak


1

Boşluk içerebilecek dosyaları bulmak için böyle bir şey yaptım.

IFS=$'\n'
for FILE in `/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf`; do
    file $FILE | tee -a $LOG
done

Bir cazibe gibi çalıştı :)



0

Kullanımda daha iyi olabileceğini düşünüyorum find'S -exec seçeneğini .

find . -type f -name '*.*' -exec tar -cpf archive.tar {} +

Bul sonra komutu bir sistem çağrısı kullanarak yürütür, böylece boşluklar ve yeni satırlar korunur (özel karakterlerin alıntılanması gereken bir boru). "Tar -c" nin arşivin zaten var olup olmadığına dikkat edin ve (en azından bash ile) ne {} ne de + 'nın alıntılanması gerekmediğine dikkat edin.


-1

Minerz029'un önerdiği gibi, findkomutun genişletilmesini alıntılamanız gerekir . Ayrıca, döngünüzdeki tüm değiştirmeleri de alıntılamanız gerekir $FILE.

for FILE in "$(find . -type f  -name '*.*')"
do
    if [ ! -f archive.tar ]; then
        tar -cpf archive.tar "$FILE"
    else 
        tar -upf archive.tar "$FILE" 
    fi
done

$()Sözdiziminin backticks kullanımı için tercih edilmesi gerektiğini unutmayın ; bkz bu U & L soru . Ayrıca, [[anahtar kelimeyi kaldırdım ve [komutla değiştirdim çünkü POSIX.


Hakkında [[ve [, [[daha yeni ve globbing ve regex eşleştirme gibi daha fazla özelliği destekliyor gibi görünüyor . [[sadece içinde basholsa da değilsh
kiri

@ minerz029 Evet. Benim dediğim de o. [[Küreselleşme ile ne demek istediğini anlamadım . Göre Greg'in wiki , hiçbir globbing yer içini alır [[.
Joseph R.

Deneyin [ "ab" == a? ] && echo "true"o zaman[[ "ab" == a? ]] && echo "true"
kiri

@ minerz029 Bu küresel değil. Bunlar düzenli ifadelerdir (serbestçe yorumlanır). Bu bir glob değildir çünkü a*"isimleri başlayan ave daha sonra herhangi bir sayıda karakter içeren tüm dosyalar" yerine "herhangi bir sayıda karakter izler" anlamına gelir . Deneyin [ ab = a* ] && echo true vs [[ ab == a* ]] && echo true.
Joseph R.

Ah iyi, [[hala yapmıyorken hala düzenli ifadeler [.
Kafası
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.