Bisakah saya "mengekspor" fungsi dalam bash?


81
source some_file

some_file:

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

Jika saya sumber some_file fungsi "doit" dan variabel TEST tersedia di baris perintah. Tetapi menjalankan skrip ini:

script.sh:

#/bin/sh
echo $TEST
doit test2

Akan mengembalikan nilai TEST, tetapi akan menghasilkan kesalahan tentang fungsi tidak dikenal "doit".

Bisakah saya "mengekspor" fungsinya juga, atau apakah saya harus sumber some_file di script.sh untuk menggunakan fungsi di sana?


2
meringkas jawaban di bawah ini (enzotib benar, dengan asumsi Anda dapat menggunakan bash, seperti yang ditunjukkan pertanyaan): ubah #!/bin/shke #!/bin/bashdan setelah doit() {...} sajaexport -f doit
michael

Sebagai catatan: Solusi ini biasanya akan bekerja saat Anda menggunakan #!/bin/shjuga, tetapi ini adalah praktik yang baik untuk digunakan #!/bin/bashsehingga Anda menghindari masalah saat shell default tidak bash.
Nagel

Jawaban:


120

Di Bash Anda dapat mengekspor definisi fungsi ke sub-shell dengan

export -f function_name

Misalnya, Anda dapat mencoba contoh sederhana ini:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Maka jika Anda menelepon, ./script1Anda akan melihat hasilnya Hello! .


16

"Mengekspor" suatu fungsi menggunakan export -fmenciptakan variabel lingkungan dengan badan fungsi. Pertimbangkan contoh ini:

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

Ini berarti bahwa hanya shell (hanya Bash?) Yang dapat menerima fungsinya. Anda juga bisa mengatur sendiri fungsinya karena Bash hanya menganggap envvars yang dimulai dengan () {fungsi:

$ 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'

Jika Anda perlu "mengekspor" variabel ini melalui SSH, maka Anda benar-benar membutuhkan fungsi sebagai string. Ini dapat dilakukan dengan opsi cetak ( -p) untuk fungsi ( -f) declarebawaan:

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

Ini sangat berguna jika Anda memiliki kode yang lebih kompleks yang perlu dijalankan melalui SSH. Pertimbangkan skrip fiktif berikut:

#!/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 fn2tidak bekerja untuk saya. Di lengkungan linux dengan shmenjadi bash v5.0.7 saya dapatkan sh: fn2: command not found. Di Ubuntu dengan shsedang dash v0.2.3 saya dapatkan sh: 1: fn2: not found. shShell mana yang Anda gunakan?
Socowi

Saya pikir jawaban Anda salah. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'tidak mencetak apa pun untuk saya, jadi jelas ftidak diekspor sebagai string biasa. Saya menguji dengan kedua kombinasi yang disebutkan di atas.
Socowi

Dan bahkan jika fungsi itu diekspor sebagai string, mengapa harus shmengeksekusi string itu sebagai fungsi? Dalam contoh Anda fn2='() { echo Hi;}' sh -c fn2, perintah yang fn2diberikan sh adalah string "fn2". shharus mencari perintah seperti itu di PATH-nya tetapi tidak harus melihat apakah ada variabel $fn2, perluas variabel itu, dan jalankan nilainya sebagai fungsi - terdengar seperti masalah keamanan utama bagi saya. sunting: Saya pikir itu saja! Apakah perilaku Anda yang ditampilkan adalah bug keamanan yang dikenal sebagai ShellShock / Bashdoor ?
Socowi

Dengan Bash 5.0.7 (Arch Linux), tampak bahwa awalan didahului. Ini kemungkinan dilakukan sebagai tanggapan terhadap ShellShock. Contoh: fn(){ echo foo; }; export -f fn; env | grep fookeluaranBASH_FUNC_fn%%=() { echo foo
Lekensteyn

7

Membangun jawaban @ Lekensteyn ...

Jika Anda menggunakannya declare -pfakan menampilkan semua fungsi yang didefinisikan sebelumnya di shell saat ini ke STDOUT.

Pada titik itu Anda dapat mengarahkan kembali STDOUT ke mana pun Anda inginkan dan pada hakekatnya mengatur fungsi-fungsi yang telah ditentukan sebelumnya di mana pun Anda inginkan.

Jawaban berikut akan memasukkan mereka ke dalam variabel. Kemudian kita menggemakan variabel itu ditambah dengan pemanggilan fungsi yang ingin kita jalankan ke shell baru yang muncul sebagai pengguna baru. Kami melakukan ini dengan menggunakan sudodengan saklar -u(alias. user) Dan hanya menjalankan Bash (yang akan menerima STDOUT yang disalurkan sebagai input untuk menjalankan).

Seperti yang kita ketahui bahwa kita akan beralih dari shell Bash ke shell Bash kita tahu bahwa Bash akan menginterpretasikan fungsi-fungsi shell yang didefinisikan sebelumnya dengan benar. Sintaksnya akan baik-baik saja selama kita bergerak di antara satu shell Bash dari versi yang sama ke shell Bash baru dari versi yang sama.

YMMV jika Anda bergerak di antara shell yang berbeda atau antara sistem yang mungkin memiliki versi Bash yang berbeda.

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

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

5

Anda tidak dapat mengekspor fungsi, tidak dengan cara yang Anda gambarkan. Shell hanya akan memuat ~/.bashrcfile pada awal shell interaktif (cari "Doa" di halaman manual bash ).

Yang bisa Anda lakukan adalah membuat "perpustakaan" yang dimuat ketika Anda memulai program:

source "$HOME/lib/somefile"

Dan tempatkan fungsi dan pengaturan non-interaktif Anda di sana.


Jadi saya harus memulai subshell dengan parameter "login" (ke parse ~ / .profile) atau sumber file itu.
Nils

Melihat sedikit lebih dekat, pada cangkang non-interaktif, Anda dapat mengatur BASH_ENVvariabel lingkungan yang some_filesudah Anda miliki, dan itu akan dipanggil. Akan cukup mudah untuk mengetahuinya:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege

2

eval "$(declare -F | sed -e 's/-f /-fx /')"akan mengekspor semua fungsi.

Saya sering melakukan ini sebelum memulai shell interaktif dalam skrip untuk memungkinkan saya melakukan debug dan bekerja dalam konteks skrip saat menggunakan fungsi dan variabelnya.

Contoh:

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

1

Fungsi tidak diekspor ke subproses. Inilah sebabnya mengapa ada file bernama .kshrc atau .bashrc: Untuk mendefinisikan fungsi yang harus tersedia dalam subkulit juga.

Jika menjalankan skrip, skrip. * Shrc biasanya tidak bersumber. Anda harus kode itu secara eksplisit, seperti di . ~/.kshrc.


Jadi ~ root / .bashrc bisa menjadi opsi dalam kasus saya, karena skrip dijalankan sebagai root. Terima kasih atas petunjuk itu.
Nils

jika menggunakan file. * shrc, pastikan mereka tidak memaksakan perilaku interaktif (seperti alias yang bodoh rm=rm -i)
ktf

0

Yah, saya baru mengenal Linux, tetapi Anda dapat mencoba ini. Dalam beberapa file, sebut saja, 'tmp / general' Anda membangun fungsi Anda:

func1(){
   echo "func from general"
}

Dalam skrip shell Anda tambahkan:

. /tmp/general

dan lari:

func1

Anda akan mendapatkan di layar: func from general.


0
declare -x -f NAME

Info lebih lanjut

-f membatasi tindakan atau tampilan untuk fungsi nama dan definisi
-x untuk membuat ekspor NAME
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.