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?
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?
Yanıtlar:
İ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
$i
Daha sonra boşlukları yorumlamaktan kaçınmak için, kullanırken alıntı yapmayı da unutmayın . Ayrıca $IFS
kullandıktan sonra tekrar ayarlamayı unutmayın , çünkü bunu yapmamak daha sonra şaşırtıcı hatalara neden olacaktır.
Buna bir uyarı daha eklenir: while
Kullandığınız kabuğa bağlı olarak , döngü içinde bir alt kabukta olanlar olabilir, bu nedenle değişken ayarları devam etmeyebilir. for
Ama fiyata, uygulamak bile döngü versiyonu önler $IFS
boşluklarla önlemek konulara çözüm, daha sonra sorun varsa geçecektir find
dö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.
Kullanın find -print0
ve xargs -0
kendi küçük C programınıza yazın veya kendi küçük C programınıza yazın. Bu -print0
ve bunun -0
iç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.
"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 IFS
sonra 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 -exec
ile find
komuta veya kullanımına -print0
içine ve boru bunu xargs -0
. İlk durumda find
kaçan dosya adı ile ilgilenir. In -print0
durumda, find
boş ayırıcı ile çıkış yazdırır ve sonra xargs
bu 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 for
döngü yerine geçmez .
find -print0
ilexargs -0
find -print0
Kombine ile kullanmak xargs -0
yasal 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 echo
komut için bağımsız değişken olarak iletir . Çünkü -n 1
seçeneği belirttik , xargs
her seferinde yalnızca bir argüman iletecek echo
. Bu seçeneği atlamış xargs
olsaydık, mümkün olduğunca çok şey geçmiş olurdu echo
. ( echo short input | xargs --show-limits
Komut satırında kaç bayta izin verildiğini görebilirsiniz.)
xargs
Tam olarak ne yapar ?Etkilerini xargs
girdilerine - 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
Ben bash
bashers 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, find
hangi 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
\; `)
find -print0
ve varxargs -0
.