Find -exec çağrısında kullanıcı tanımlı bir işlev yürütme


25

Solaris 10'dayım ve aşağıdakileri ksh (88), bash (3.00) ve zsh (4.2.1) ile test ettim.

Aşağıdaki kod hiçbir sonuç vermedi:

function foo {
    echo "Hello World"
}

find somedir -exec foo \;

Bulma, birkaç dosyayla eşleşiyor ( -exec ...ile değiştirilerek gösterildiği gibi -print) ve işlev dış findaramadan çağrıldığında mükemmel çalışıyor .

İşte man findsayfanın söyledikleri -exec:

 -exec komutu Eğer çalıştırılan komut bir dönerse doğru
                     çıkış durumu olarak sıfır değeri. Sonu
                     komut kaçan bir kişi tarafından kesilmek zorundadır
                     noktalı virgül (;). Bir komut argümanı {}
                     geçerli yol adıyla değiştirildi. Eğer
                     -exec'in son argümanı {} ve sizsiniz.
                     noktalı virgül (;) yerine + belirtin,
                     komut, birkaç kez çağrılır
                     {}, yol adları grubuyla değiştirildi. Eğer
                     komutun herhangi bir çağrısı bir
                     çıkış durumu olarak sıfır olmayan değeri bulun
                     sıfır olmayan bir çıkış durumu döndürür.

Muhtemelen böyle bir şey yapmaktan kurtulabilirdim:

for f in $(find somedir); do
    foo
done

Ancak alan ayırıcı konularla uğraşmaktan korkuyorum.

Bir çağrıdan bir kabuk işlevini çağırmak mümkün mü (aynı komut dosyasında tanımlanmış, kapsam belirleme sorunlarıyla uğraşmayalım) find ... -exec ...?

Ben ikisi ile denedim /usr/bin/findve /bin/findaynı sonucu var.


ilan ettikten sonra fonksiyonu dışa aktarmayı denediniz mi? export -f foo
h3rrmiller 12:12

2
İşlevi harici bir komut dosyası yapmanız ve eklemeniz gerekir PATH. Alternatif olarak, sh -c '...'işlevi kullanın ve her ikisi de ...bit içindeki işlevi AND çalıştırın . İşlevler ve komut dosyaları arasındaki farkları anlamanıza yardımcı olabilir .
jw013

Yanıtlar:


27

A functionbir kabuğa yereldir, bu yüzden find -execbir kabuğun doğurması ve kullanmadan önce bu kabuğunda tanımlanmış bir işleve sahip olmanız gerekir. Gibi bir şey:

find ... -exec ksh -c '
  function foo {
    echo blan: "$@"
  }
  foo "$@"' ksh {} +

bashbirinin çevre yoluyla işlevleri dışa aktarmasına izin verir export -f, böylece (bash'te) yapabilirsiniz:

foo() { ...; }
export -f foo
find ... -exec bash -c 'foo "$@"' bash {} +

ksh88sahiptir typeset -fxihracat fonksiyonu (değil çevre yoluyla) için, ama bu sadece o-patlamayla az tarafından yürütülen komut dosyaları tarafından kullanılabilir kshile çok değil, ksh -c.

Başka bir seçenek de yapmaktır:

find ... -exec ksh -c "
  $(typeset -f foo)"'
  foo "$@"' ksh {} +

Diğer bir deyişle, işlev typeset -ftanımını foosatır içi komut dosyasında bırakmak için kullanın . Eğer Not fookullandığı diğer fonksiyonlar, ayrıca bunları da dökümü gerekir.


2
Niçin iki olay olduğunu kshya bashda -execemir altında olduğunu açıklayabilir misiniz ? İlkini anlıyorum ama ikinci durumu değil.
daniel kullmann 15:12

6
@danielkullmann olarak bash -c 'some-code' a b c, $0bir a, $1bir b... yani istersen $@olmak a, b, cdaha önce bir şey eklemek gerekir. Çünkü $0hata iletilerini görüntülerken de kullanılır, bu bağlam içinde mantıklı kabuk, ya da bir şey adını kullanmak iyi bir fikirdir.
Stéphane Chazelas 15:12

@ StéphaneChazelas, böyle güzel cevap için teşekkür ederim.
Kullanıcı9102d82

5

Bu her zaman uygulanabilir değildir, ancak olduğunda basit bir çözümdür. globstarSeçeneği ayarlayın ( set -o globstarksh93'de, shopt -s globstarbash ≥4'te; zsh'de varsayılan olarak açıktır). Ardından **/, geçerli dizini ve alt dizinlerini tekrar tekrar eşleştirmek için kullanın .

Örneğin, yerine find . -name '*.txt' -exec somecommand {} \;koşabilirsiniz

for x in **/*.txt; do somecommand "$x"; done

Bunun yerine find . -type d -exec somecommand {} \;koşabilirsiniz

for d in **/*/; do somecommand "$d"; done

Bunun yerine find . -newer somefile -exec somecommand {} \;koşabilirsiniz

for x in **/*; do
  [[ $x -nt somefile ]] || continue
  somecommand "$x"
done

Ne zaman **/işi sizin için değil (kabuk buna sahip veya bir gerektiğinden olmadığından findbir kabuk analog yok seçeneği), işlevi tanımlayın find -execargüman .


globstarKullandığım ksh ( ksh88) sürümünde bir seçenek bulamıyorum .
rahmu

@rahmu Gerçekten, ksh93'de yeni. Solaris 10'un bir yerinde ksh93 yok mu?
Gilles 'SO- kötülükten vazgeç'

Bu blog yazısına göre ksh93, hem Bourne Shell'in hem de ksh88'in yerine Solaris 11'de tanıtıldı ...
rahmu

@rahmu. Solaris bir süredir ksh93'e sahipti dtksh(bazı X11 uzantılarına sahip ksh93), ancak eski bir versiyon ve muhtemelen CDE'nin isteğe bağlı olduğu isteğe bağlı bir paketin parçası.
Stéphane Chazelas

Özyinelemeli globbing'in nokta dosyalarını finddışladığı ve dotdir'lere düşmediğinden ve dosya listesini sıraladığından (her ikisi de globbing niteleyicileri aracılığıyla zsh'de adres olabilen) farklı olduğuna dikkat edilmelidir. Ayrıca, **/*aksine ./**/*, dosya isimleri ile başlayabilir -.
Stéphane Chazelas

4

Bir sınırlayıcı olarak \ 0 kullanın ve şu ismin içindeki dosya isimlerini doğmuş bir komuttan şu şekilde okuyun:

foo() {
  printf "Hello {%s}\n" "$1"
}

while read -d '' filename; do
  foo "${filename}" </dev/null
done < <(find . -maxdepth 2 -type f -print0)

Burada neler oluyor:

  • read -d '' Bir sonraki \ 0 bayta kadar okur, böylece dosya isimlerindeki garip karakterler hakkında endişelenmenize gerek kalmaz.
  • Benzer şekilde, -print0oluşturulan her dosya adını \ n yerine sonlandırmak için \ 0 kullanır.
  • cmd2 < <(cmd1)aynı cmd1 | cmd2olduğu CMD2 bir altkabuk ana kabuğun çalışır ve haricinde.
  • foo çağrısı /dev/null, borudan yanlışlıkla okunmamasını sağlamak için yönlendirilir .
  • $filename Kabuk, boşluk içeren bir dosya adını bölmeye çalışmadığı için alıntı yapılır.

Şimdi, read -dve <(...)zsh, bash ve ksh 93u'dayız, ancak daha önceki ksh sürümlerinden emin değilim.


4

Önceden tanımlanmış bir kabuk işlevi kullanmak için betiğinizden doğmuş bir alt işlem istiyorsanız, dışa aktarmanız gerekir. export -f <function>

Not: export -fbash özgüdür

çünkü sadece bir kabuk kabuk fonksiyonlarını çalıştırabilir :

find / -exec /bin/bash -c 'function "$1"' bash {} \;

EDIT: aslında betiğiniz buna benzemelidir:

#!/bin/bash
function foo() { do something; }
export -f foo
find somedir -exec /bin/bash -c 'foo "$0"' {} \;

1
export -fbu bir sözdizimi hatasıdır kshve işlev tanımını ekrana yazdırır zsh. Tümünde ksh, zshve bashbu sorunu çözmez.
rahmu

1
find / -exec /bin/bash -c 'function' \;
h3rrmiller

Şimdi çalışıyor, ama sadece ile bash. Teşekkür ederim!
rahmu

4
Asla {}kabuk koduna gömme ! Bu, dosya adının kabuk kodu olarak yorumlandığı anlamına gelir, bu yüzden çok tehlikelidir
Stéphane Chazelas
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.