Dosya adlarında boşluk olduğunda find komutunun çıktısını nasıl ayrıştırabilirim?


12

Gibi bir döngü kullanma

for i in `find . -name \*.txt` 

bazı dosya adlarında boşluk varsa kırılır.

Bu sorunu önlemek için hangi tekniği kullanabilirim?


1
Dosyaların dosya adlarında da yeni satırlar olabileceğini unutmayın. Bu yüzden find -print0ve var xargs -0.
Daniel Beck

Yanıtlar:


12

İdeal olarak bunu böyle yapmazsınız, çünkü bir kabuk betiğinde dosya adlarını düzgün bir şekilde ayrıştırmak her zaman zordur (boşluklar için düzeltin, diğer gömülü karakterlerle, özellikle de yeni satırla ilgili sorunlarınız olacaktır). Bu, BashPitfalls sayfasındaki ilk giriş olarak bile listelenir .

Bununla birlikte, neredeyse istediğinizi yapmanın bir yolu var:

oIFS=$IFS
IFS=$'\n'

find . -name '*.txt' | while read -r i; do
  # use "$i" with whatever you're doing
done

IFS=$oIFS

$iDaha sonra boşlukları yorumlamaktan kaçınmak için, kullanırken alıntı yapmayı da unutmayın . Ayrıca $IFSkullandıktan sonra tekrar ayarlamayı unutmayın , çünkü bunu yapmamak daha sonra şaşırtıcı hatalara neden olacaktır.

Buna bir uyarı daha eklenir: whileKullandığınız kabuğa bağlı olarak , döngü içinde bir alt kabukta olanlar olabilir, bu nedenle değişken ayarları devam etmeyebilir. forAma fiyata, uygulamak bile döngü versiyonu önler $IFSboşluklarla önlemek konulara çözüm, daha sonra sorun varsa geçecektir finddönüşleri çok fazla dosya var.

Bir noktada tüm bunlar için doğru düzeltme bunu kabuk yerine Perl veya Python gibi bir dilde yapıyor.


1
Tüm bunlardan kaçınmak için sadece Python kullanma fikrini seviyorum.
Scott C Wilson

12

Kullanın find -print0ve xargs -0kendi küçük C programınıza yazın veya kendi küçük C programınıza yazın. Bu -print0ve bunun -0için icat edildi.

Kabuk komut dosyaları, içindeki boşluklarla dosya adlarını işlemenin en iyi yolu değildir: bunu yapabilirsiniz, ancak tıkanır.


Makinemde çalışıyor ^ TM!
mcandre

2

"Dahili alan ayırıcısını" ( IFS), döngü bağımsız değişkeni bölmesi için alandan başka bir şeye ayarlayabilirsiniz;

ORIGIFS=${IFS}
NL='
'
IFS=${NL}
for i in $(find . -name '*.txt'); do
    IFS=${ORIGIFS}
    #do stuff
done
IFS=${ORIGIFS}

Bulunduktan IFSsonra sıfırladım , çoğunlukla güzel göründüğünden, sanırım. Yeni satıra ayarlanmasında herhangi bir sorun görmedim, ama bunun "daha temiz" olduğunu düşünüyorum.

Diğer bir yöntem, aralarından çıkışı ile ne yapmak istediğinize bağlı olarak find, ya doğrudan kullanmaktır -execile findkomuta veya kullanımına -print0içine ve boru bunu xargs -0. İlk durumda findkaçan dosya adı ile ilgilenir. In -print0durumda, findboş ayırıcı ile çıkış yazdırır ve sonra xargsbu konuda böler. Hiçbir dosya adı bu karakteri (bildiklerimi) içeremediğinden, bu da her zaman güvenlidir. Bu çoğunlukla basit durumlarda yararlıdır; ve genellikle tam bir fordöngü yerine geçmez .


1

Kullanımı find -print0ilexargs -0

find -print0Kombine ile kullanmak xargs -0yasal dosya adlarına karşı tamamen sağlamdır ve mevcut en genişletilebilir yöntemlerden biridir. Örneğin, geçerli dizindeki her PDF dosyasının bir listesini istediğinizi varsayalım. Yazabilirsin

$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 echo

Bu -iname '*.pdf', geçerli dizindeki ( .) ve herhangi bir alt dizindeki her PDF'yi (via ) bulur ve her birini echokomut için bağımsız değişken olarak iletir . Çünkü -n 1seçeneği belirttik , xargsher seferinde yalnızca bir argüman iletecek echo. Bu seçeneği atlamış xargsolsaydık, mümkün olduğunca çok şey geçmiş olurdu echo. ( echo short input | xargs --show-limitsKomut satırında kaç bayta izin verildiğini görebilirsiniz.)

xargsTam olarak ne yapar ?

Etkilerini xargsgirdilerine - ve -nözellikle etkisini - açıkça , argümanlarını daha kesin bir şekilde yansıtan bir betik kullanarak görebiliriz echo.

$ cat > echoArgs.sh <<'EOF'
#!/bin/bash
echo "Number of arguments: $#"

[[ $# -eq 0 ]] && exit

for i in $(seq 1 $#); do
    echo "Arg $i: <$1>"
    shift
done
EOF

$ find . -iname '*.pdf' -print0 | xargs -0 ./echoArgs.sh
$ find . -iname '*.pdf' -print0 | xargs -0 -n 1 ./echoArgs.sh

Boşlukları ve satırsonlarını mükemmel bir şekilde ele aldığını unutmayın,

$ touch 'A space-age
new line of vending machines.pdf'
$ find . -iname '*space*' -print0 | xargs -0 -n 1 ./echoArgs.sh

Aşağıdaki ortak çözümle özellikle sorun yaratacaktır:

chmod +x ./echoArgs.sh
for file in $(ls *spacey*); do
  ./echoArgs.sh "$file"
done
notlar

1

Ben bashbashers ile aynı fikirde değilim , çünkü bash* nix araç seti ile birlikte, dosyaları (adları boşluk gömülü olanlar da dahil olmak üzere) işleme konusunda oldukça usta.

Aslında, findhangi dosyaların işleneceğini seçme konusunda size ince bir kontrol sağlar bash words. tipik olarak "çift tırnak" veya IFS veya find{}

Çoğu / birçok durumda IFS'yi ayarlamanıza ve sıfırlamanıza gerek olmadığını unutmayın; aşağıdaki örneklerde gösterildiği gibi IFS'yi yerel olarak kullanın. Her üçü de boşlukları iyi idare eder. Çünkü Ayrıca, bir "standart" döngü yapısını gerekmez bulmak en \; olduğunu etkili bir şekilde bir döngü; döngü mantığınızı bir bash işlevine koyun (standart bir araç çağırmıyorsanız).

IFS=$'\n' find ~/ -name '*.txt' -exec  function-or-util {} \;  

Ve iki örnek daha

IFS=$'\n' find ~/ -name '*.txt' -exec  printf 'Hello %s\n' {} \;  
IFS=$'\n' find ~/ -name '*.txt' -exec  echo {} \+ |sed 's/home//'  

'bul also allows you to pass multiple filenames as args to you script ..(if it suits your need: use+ instead\; `)


1
Her iki perspektif için de bir miktar geçerlilik vardır. Sadece kendi dosyalarım üzerinde çalışırken, sadece bul ve bunun için endişelenmemeliydim, çünkü dosyalarımın isimlerinde boşluk (veya satır başı!) Yok. Ancak diğer insanların dosyalarıyla çalışmaya başladığınızda, daha sağlam teknikler kullanmanız gerekir.
Scott C Wilson
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.