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.