Fonksiyonları bash olarak “verebilirim”?


81
source some_file

some_file:

doit ()
{
  echo doit $1
}
export TEST=true

Some_file kaynağını kullanırsam "doit" işlevi ve TEST değişkeni komut satırında bulunur. Ancak bu komut dosyasını çalıştırıyorum:

script.sh:

#/bin/sh
echo $TEST
doit test2

TEST değerini döndürecek, ancak bilinmeyen "doit" işlevi hakkında bir hata üretecektir.

İşlevi de "dışa aktarabilir miyim" veya işlevi kullanmak için script.sh dosyasındaki some_file kaynağını sağlamam gerekir mi?


2
değişikliği: Aşağıdaki cevapları (soru işaret olarak enzotib, sen bash kullanabilirsiniz varsayarak, doğrudur) özetleme #!/bin/shiçin #!/bin/bashve sonra doit() {...} sadeceexport -f doit
michael

Sadece kayıt için: Bu çözüm genellikle siz de kullandığınızda çalışır #!/bin/sh, ancak #!/bin/bashvarsayılan kabuk bash olmadığı zaman sorunlardan kaçınmak için kullanmak iyi bir uygulamadır .
Nagel

Yanıtlar:


120

Bash'de fonksiyon tanımlarını sub-shell'e dışa aktarabilirsiniz.

export -f function_name

Örneğin, bu basit örneği deneyebilirsiniz:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

O zaman ararsanız ./script1çıkış Merhaba göreceksiniz ! .


16

Bir işlevi kullanarak "dışa aktarma" export -fişlev gövdesiyle bir ortam değişkeni yaratır. Bu örneği düşünün:

$ fn(){ echo \'\"\ \ \$; }
$ export -f fn
$ sh -c printenv\ fn
() {  echo \'\"\ \ \$
}

Bu, yalnızca kabuğun (sadece Bash?) İşlevi kabul edebileceği anlamına gelir. Ayrıca, işlevi yalnızca Bash, işlev olarak başlayan envvarları kabul ettiği için kendiniz de ayarlayabilirsiniz () {:

$ fn2='() { echo Hi;}' sh -c fn2
Hi
$ fn3='() {' sh -c :
sh: fn3: line 1: syntax error: unexpected end of file
sh: error importing function definition for `fn3'

Bu değişkeni SSH'ye "dışa aktarmanız" gerekiyorsa, gerçekten işleve bir dize olarak ihtiyacınız vardır. Bu , yerleşik -pfonksiyonların ( -f) baskı seçeneği ( ) ile yapılabilir declare:

$ declare -pf fn
fn () 
{ 
    echo \'\"\ \ \$
}

SSH üzerinden yürütülmesi gereken daha karmaşık bir kodunuz varsa, bu çok kullanışlıdır. Aşağıdaki hayali senaryoyu inceleyin:

#!/bin/bash
remote_main() {
   local dest="$HOME/destination"

   tar xzv -C "$dest"
   chgrp -R www-data "$dest"
   # Ensure that newly written files have the 'www-data' group too
   find "$dest" -type d -exec chmod g+s {} \;
}
tar cz files/ | ssh user@host "$(declare -pf remote_main); remote_main"

fn2='() { echo Hi;}' sh -c fn2benim için işe yaramadı. shV.5.0.7 bash ile linux kemer üzerinde aldım sh: fn2: command not found. Ubuntu'da shçizgi v0.2.3 olmakla birlikte aldım sh: 1: fn2: not found. Hangi shkabuğu kullandın?
Socowi,

Bence cevabın yanlış. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'benim için hiçbir şey basmaz, bu nedenle açıkça fdüz bir dize olarak verilmez. Yukarıda belirtilen her iki kombinasyonla da test ettim.
Socowi

İşlev bir dize olarak dışa aktarılsa bile, neden shbu dize bir işlev olarak çalıştırmalı? Örnekte fn2='() { echo Hi;}' sh -c fn2komut fn2verilen sh anlamıyla dizedir "fn2". shPATH'sinde böyle bir komut aramalı fakat değişken olup olmadığına bakmamalı $fn2, bu değişkeni genişletmeli ve değerini bir işlev olarak yerine getirmeli - bana büyük bir güvenlik sorunu gibi geliyor. düzenleme: Sanırım bu kadar! Gösterdiğiniz davranış, ShellShock / Bashdoor olarak bilinen güvenlik hatası mıydı ?
Socowi,

Bash 5.0.7 (Arch Linux) ile bir ön ekin hazır olduğu anlaşılıyor. Bu muhtemelen ShellShock'a cevap olarak yapıldı. Örnek: fn(){ echo foo; }; export -f fn; env | grep foooutputsBASH_FUNC_fn%%=() { echo foo
Lekensteyn

7

@ Lekensteyn'in cevabı üzerine inşa ...

Eğer kullanırsanız declare -pfo çıkış STDOUT geçerli kabuğunda daha önce tanımlanmış fonksiyonlar olacak.

Bu noktada STDOUT'u istediğiniz yere yönlendirebilir ve önceden tanımlanmış olan fonksiyonları istediğiniz yerde kullanabilirsiniz.

Aşağıdaki cevap onları bir değişkene doldurur. Sonra bu değişkeni ve yeni bir kullanıcı olarak ortaya çıkan yeni kabuğa çalıştırmak istediğimiz işlevin çağrışımını ekleriz. Biz kullanarak yapabilirsiniz sudoile -u(aka. user) Anahtarı ve sade (çalıştırmak için girdi olarak borulu STDOUT alacak olan) Bash çalışan.

Bir Bash kabuğundan bir Bash kabuğuna gittiğimizi bildiğimiz için, Bash'in önceden tanımlanmış işlevleri tanımlayabildiğini biliyoruz. Aynı sürümdeki bir Bash kabuğuna, aynı sürümdeki yeni bir Bash kabuğuna geçtiğimiz sürece sözdizimi iyi olmalıdır.

YMMV, farklı mermiler arasında veya Bash'in farklı sürümlerine sahip sistemler arasında geçiş yapıyorsanız.

#!/bin/bash
foo() {
  echo "hello from `whoami`"
}

FUNCTIONS=`declare -pf`; echo "$FUNCTIONS ; foo" | sudo -u otheruser bash
# $./test.sh
# hello from otheruser

5

Tanımladığınız şekilde değil, işlevleri veremezsiniz. Kabuk, ~/.bashrcdosyayı yalnızca etkileşimli bir kabuğun başlangıcına yükler ( bash kılavuz sayfasında "Invocation" ifadesini arayın ).

Yapabileceğiniz şey, programı başlattığınızda yüklenen "kütüphane" oluşturmaktır:

source "$HOME/lib/somefile"

İnteraktif olmayan işlevlerinizi ve ayarlarınızı da buraya yerleştirin.


Bu yüzden alt kabuğu "login" parametresiyle başlatmam (~ / .profile dosyasını) veya bu dosyayı kaynaklamam gerekiyor.
Nils,

Biraz daha yakından bakıldığında, etkileşimli olmayan kabuklarda, zaten sahip olduğunuz BASH_ENVortam değişkenini ayarlayabilir some_fileve bunun adı verilir. Bunu bulmak yeterince kolay olurdu:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege

2

eval "$(declare -F | sed -e 's/-f /-fx /')"tüm fonksiyonları dışa aktarır.

İşlev ve değişkenlerini kullanırken betiği bağlamında hata ayıklamama ve çalışmamı sağlamak için bir komut dosyasında etkileşimli bir kabuk başlatmadan önce bunu çok yaparım.

Örnek:

eval "$(declare -F | sed -e 's/-f /-fx /')"
export SOME IMPORTANT VARIABLES AND PASSWORDS
bash -i

1

İşlevler alt işlemlere verilmez. Bu nedenle .kshrc veya .bashrc adlı dosyalar var: Alt kabuklarda da kullanılabilen işlevleri tanımlamak için.

Bir komut dosyası çalıştırılıyorsa,. * Shrc komut dosyaları normal olarak kaynaklanmaz. Bunu olduğu gibi açıkça kodlamanız gerekir . ~/.kshrc.


Böylece ~ root / .bashrc benim durumumda bir seçenek olabilir, çünkü scriptler root olarak çalıştırılır. Bu ipucu için teşekkürler.
Nils,

. * SHRC dosyalarını kullanarak, onlar (aptal takma gibi interaktif davranış zorlamak yok emin olun rm=rm -i)
KTF

0

Ben Linux'ta yeniyim ama bunu deneyebilirsiniz. Bazı dosyalarda, 'tmp / general' olarak adlandıralım.

func1(){
   echo "func from general"
}

Kabuk betiğinize ekleyin:

. /tmp/general

ve koş:

func1

Ekranda alırsınız: func from general.


0
declare -x -f NAME

Daha fazla bilgi

-f İşlemi veya görüntülemeyi işlev adlarını ve tanımları kısıtlamak
-x, NAME'lerin dışa aktarılmasını sağlamak için
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.