Bash: bir işlevi parametre olarak ilet


91

Bash'de bir işlevi parametre olarak iletmem gerekiyor. Örneğin, aşağıdaki kod:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  eval $1
  echo "after"
}

around x

Çıktısı olmalıdır:

before
Hello world
after

evalBu bağlamda doğru olmadığını biliyorum ama bu sadece bir örnek :)

Herhangi bir fikir?

Yanıtlar:


126

İşlev adının veya argümanlarının değerlendirilmesini geciktirmek gibi süslü bir şeye ihtiyacınız yoksa şunlara ihtiyacınız yoktur eval:

function x()      { echo "Hello world";          }
function around() { echo before; $1; echo after; }

around x

ne istersen yapar. Fonksiyonu ve argümanlarını şu şekilde bile iletebilirsiniz:

function x()      { echo "x(): Passed $1 and $2";  }
function around() { echo before; "$@"; echo after; }

around x 1st 2nd

baskılar

before
x(): Passed 1st and 2nd
after

2
Başka bir y () fonksiyonum varsa, x 1. 2. y 1. 2. 2'yi yapabilir miyim? X ve y'nin yaklaşık argüman olduğunu, 1. ve 2.'nin ise x ve y'nin argümanları olduğunu nasıl biliyor?
techguy2000

Ve fonksiyon kelimesini atlayabilirsiniz.
jasonleonhard

Ancak, o zaman ad aralıklı olmayacaklar. Örneğin, yöntemlerin içinde yöntemleriniz varsa ve functionkelimeyi tutarsanız, en üst düzey yöntemi çalıştırana kadar bu dahili yöntemlere erişemezsiniz.
jasonleonhard

29

Kimsenin soruyu tam olarak cevapladığını sanmıyorum. Dizeleri sırayla yankılayıp yankılamayacağını sormadı. Daha ziyade, sorunun yazarı, işlev işaretçisi davranışını simüle edip edemeyeceğini bilmek ister.

Yaptığıma çok benzeyen birkaç cevap var ve bunu başka bir örnekle genişletmek istiyorum.

Yazardan:

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  ($1)                   <------ Only change
  echo "after"
}

around x

Bunu genişletmek için x echo "Merhaba dünya: $ 1" fonksiyonuna sahip olacağız ve fonksiyonun gerçekten ne zaman çalıştığını göstereceğiz. "X" işlevinin adı olan bir dizge geçeceğiz:

function x() {
  echo "Hello world:$1"
}

function around() {
  echo "before"
  ($1 HERE)                   <------ Only change
  echo "after"
}

around x

Bunu açıklamak için, "x" dizisi "önce" yankılanan () etrafında işleve geçirilir, x işlevini çağırır ($ 1 değişkeni aracılığıyla, ilk parametre etrafa iletilir) "HERE" bağımsız değişkenini iletir ve sonunda yankılanır .

Diğer bir yana, bu, değişkenleri işlev adları olarak kullanma yöntemidir. Değişkenler aslında işlevin adı olan dizeyi tutar ve ($ değişken arg1 arg2 ...) bağımsız değişkenleri geçiren işlevi çağırır. Aşağıya bakınız:

function x(){
    echo $3 $1 $2      <== just rearrange the order of passed params
}

Z="x"        # or just Z=x

($Z  10 20 30)

verir: 30 10 20, burada Z değişkeninde depolanan "x" adlı işlevi çalıştırdık ve 10 20 ve 30 parametrelerini geçtik.

Fonksiyonlara değişken isimleri atayarak fonksiyonlara referans verdiğimiz yerin üstünde, böylece fonksiyon adını gerçekten bilmek yerine değişkeni kullanabiliriz (bu, program akışını genelleştirmek için c'deki çok klasik bir fonksiyon işaretçisi durumunda yapabileceğinize benzer, ancak ön - komut satırı argümanlarına göre yapacağınız işlev çağrılarını seçmek).

Bash'de bunlar işlev işaretçileri değil, daha sonra kullanacağınız işlevlerin adlarına atıfta bulunan değişkenlerdir.


Bu cevap harika. Tüm örneklerin bash senaryolarını yaptım ve çalıştırdım. Ayrıca çok yardımcı olan "yalnızca değişim" yaratma biçiminizi de gerçekten seviyorum. Son paragrafta ikinci paragrafta yanlış yazım var: "Yukarıda"
JMI MADISON

Yazım hatası düzeltildi. Teşekkürler @J MADISON
uDude

7
neden onu sarıyorsun bu ()bir alt kabuk başlatmaz?
horseyguy

17

kullanmaya gerek yok eval

function x() {
  echo "Hello world"
}

function around() {
  echo "before"
  var=$($1)
  echo "after $var"
}

around x

5

Dizelerden başka bir işleve hiçbir şey aktaramazsınız. Süreç ikameleri bir çeşit sahte olabilir. Bash, bir komut tamamlanana kadar FIFO'yu açık tutma eğilimindedir.

İşte hızlı bir aptalca

foldl() {
    echo $(($(</dev/stdin)$2))
} < <(tr '\n' "$1" <$3)

# Sum 20 random ints from 0-999
foldl + 0 <(while ((n=RANDOM%999,x++<20)); do echo $n; done)

Fonksiyonlar dışa aktarılabilir, ancak bu ilk göründüğü kadar ilginç değildir. Bunun, hata ayıklama işlevlerini betiklere veya betikleri çalıştıran diğer programlara erişilebilir kılmak için yararlı buluyorum.

(
    id() {
        "$@"
    }

    export -f id
    exec bash -c 'echowrap() { echo "$1"; }; id echowrap hi'
)

id hala yalnızca bir işlevin adı (ortamdaki bir serileştirmeden otomatik olarak alınan) ve değiştirgeleri olan bir dizge alır.

Pumbaa80'in başka bir cevaba yaptığı yorum da iyidir ( eval $(declare -F "$1")), ancak her zaman global olduklarından işlevler için değil, esas olarak diziler için kullanışlıdır. Bunu bir işlev içinde çalıştıracak olsaydın, tek yapacağı onu yeniden tanımlamaktır, yani hiçbir etkisi olmaz. Geçerli kapsamda ne olduğuna bağlı olarak kapanışlar veya kısmi işlevler veya "işlev örnekleri" oluşturmak için kullanılamaz. En iyi ihtimalle bu, başka bir yerde yeniden tanımlanan bir dizede bir işlev tanımını saklamak için kullanılabilir - ancak bu işlevler, tabii ki evalkullanılmadıkça yalnızca kodlanabilir.

Temelde Bash bu şekilde kullanılamaz.


2

Daha iyi bir yaklaşım, işlevlerinizde yerel değişkenler kullanmaktır. Sorun o zaman sonucu arayan kişiye nasıl ulaşırsınız. Bir mekanizma, komut ikamesini kullanmaktır:

function myfunc()
{
    local  myresult='some value'
    echo "$myresult"
}

result=$(myfunc)   # or result=`myfunc`
echo $result

Burada sonuç standart çıktıya çıkarılır ve arayan, bir değişkendeki değeri yakalamak için komut ikamesini kullanır. Değişken daha sonra gerektiği gibi kullanılabilir.


1

Aşağıdaki satırlarda bir şeye sahip olmalısınız:

function around()
{
  echo 'before';
  echo `$1`;
  echo 'after';
}

Daha sonra arayabilirsin around x


-1

eval muhtemelen bunu başarmanın tek yoludur. Tek gerçek dezavantajı, bunun güvenlik yönüdür, çünkü kötü niyetli hiçbir şeyin geçmemesini sağlamanız gerekir ve yalnızca çağrılmak istediğiniz işlevler çağrılır (';' gibi kötü karakterlere sahip olup olmadığının kontrol edilmesiyle birlikte) içinde de).

Dolayısıyla, kodu çağıran sizseniz, bunu yapmanın tek yolu muhtemelen eval. Alt komutları ($ () ve ``) içeren muhtemelen işe yarayacak başka değerlendirme biçimleri olduğunu, ancak daha güvenli olmadığını ve daha pahalı olduğunu unutmayın.


eval bunu yapmanın tek yoludur.
Wes

1
Bunu kolayca kontrol edebilirsiniz eval $1kullanarak bir işlev çağırırif declare -F "$1" >/dev/null; then eval $1; fi
user123444555621

2
... ya da daha iyisi:eval $(declare -F "$1")
user123444555621
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.