Tôi có thể xuất các chức năng xuất khẩu trong bash không?


81
source some_file

some_file:

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

Nếu tôi nguồn some_file thì hàm "doit" và TEST biến có sẵn trên dòng lệnh. Nhưng chạy tập lệnh này:

script.sh:

#/bin/sh
echo $TEST
doit test2

Sẽ trả về giá trị của TEST, nhưng sẽ tạo ra lỗi về hàm "doit" chưa biết.

Tôi cũng có thể "xuất" hàm hay tôi phải lấy nguồn some_file trong script.sh để sử dụng hàm ở đó?


2
tóm tắt câu trả lời dưới đây (enzotib là chính xác, giả sử bạn có thể sử dụng bash, như câu hỏi chỉ ra): thay đổi #!/bin/shthành #!/bin/bashsau doit() {...} chỉexport -f doit
michael

Chỉ dành cho bản ghi: Giải pháp này thường sẽ hoạt động khi bạn sử dụng #!/bin/sh, nhưng đó là cách thực hành tốt để #!/bin/bashbạn tránh các vấn đề khi shell mặc định không bị bash.
Nagel

Câu trả lời:


120

Trong Bash, bạn có thể xuất định nghĩa hàm sang shell phụ với

export -f function_name

Ví dụ, bạn có thể thử ví dụ đơn giản này:

./script1:

    #!/bin/bash

    myfun() {
        echo "Hello!"
    }

    export -f myfun
    ./script2

./script2:

    #!/bin/bash

    myfun

Sau đó, nếu bạn gọi, ./script1bạn sẽ thấy đầu ra Hello! .


16

"Xuất khẩu" một hàm sử dụng export -fsẽ tạo ra một biến môi trường với thân hàm. Xem xét ví dụ này:

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

Điều này có nghĩa là chỉ vỏ (chỉ Bash?) Sẽ có thể chấp nhận hàm. Bạn cũng có thể tự thiết lập chức năng vì Bash chỉ xem xét các envv bắt đầu bằng () {hàm:

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

Nếu bạn cần "xuất" biến này qua SSH, thì bạn thực sự cần hàm dưới dạng chuỗi. Điều này có thể được thực hiện với tùy chọn in ( -p) cho các hàm ( -f) của tích declarehợp:

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

Điều này rất hữu ích nếu bạn có mã phức tạp hơn cần được thực thi qua SSH. Hãy xem xét kịch bản hư cấu sau đây:

#!/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 fn2đã không làm việc cho tôi. Trên arch linux với shbash v5.0.7 tôi đã nhận sh: fn2: command not found. Trên Ubuntu với shdấu gạch ngang v0.2.3 tôi đã nhận sh: 1: fn2: not found. Bạn shđã sử dụng vỏ nào ?
Socowi

Tôi nghĩ rằng câu trả lời của bạn là sai. f(){ echo a;}; export -f f; echo "$f"; sh -c 'printenv f; echo "$f"'không in gì cho tôi, vì vậy rõ ràng fkhông được xuất dưới dạng một chuỗi đơn giản. Tôi đã thử nghiệm với cả hai kết hợp được đề cập ở trên.
Socowi

Và ngay cả khi hàm được xuất dưới dạng chuỗi, tại sao nên shthực thi chuỗi đó dưới dạng hàm? Trong ví dụ của bạn fn2='() { echo Hi;}' sh -c fn2, lệnh fn2được đưa ra theo sh nghĩa đen là chuỗi "fn2". shnên tìm kiếm một lệnh như vậy trong PATH của nó nhưng không nên tìm nếu có một biến $fn2, mở rộng biến đó và thực hiện giá trị của nó như là một hàm - nghe có vẻ như là một vấn đề bảo mật lớn đối với tôi. chỉnh sửa: Tôi nghĩ đó là nó! Hành vi được hiển thị của bạn có phải là lỗi bảo mật được gọi là ShellShock / Bashdoor không?
Socowi

Với Bash 5.0.7 (Arch Linux), có vẻ như một tiền tố được thêm vào trước. Điều này có thể đã được thực hiện để đáp ứng với ShellShock. Ví dụ: fn(){ echo foo; }; export -f fn; env | grep foođầu raBASH_FUNC_fn%%=() { echo foo
Lekensteyn

7

Dựa trên câu trả lời của @ Lekensteyn ...

Nếu bạn sử dụng declare -pfnó sẽ xuất tất cả các hàm được xác định trước đó trong shell hiện tại thành STDOUT.

Tại thời điểm đó, bạn có thể chuyển hướng STDOUT đến bất cứ nơi nào bạn muốn và thực tế sẽ nhồi các chức năng được xác định trước đó bất cứ nơi nào bạn muốn.

Câu trả lời sau đây sẽ nhét chúng vào một biến. Sau đó, chúng tôi lặp lại biến đó cộng với việc gọi hàm mà chúng tôi muốn chạy vào shell mới được sinh ra như một người dùng mới. Chúng tôi thực hiện điều này bằng cách sử dụng sudovới công tắc -u(aka. user) Và chỉ cần chạy Bash (sẽ nhận STDOUT đường ống làm đầu vào để chạy).

Như chúng ta biết rằng chúng ta đang đi từ lớp vỏ Bash sang lớp vỏ Bash, chúng ta biết rằng Bash sẽ diễn giải các hàm được xác định trước một cách chính xác. Cú pháp sẽ ổn miễn là chúng ta đang di chuyển giữa một shell Bash của cùng một phiên bản sang shell Bash mới của cùng một phiên bản.

YMMV nếu bạn đang di chuyển giữa các hệ vỏ khác nhau hoặc giữa các hệ thống có thể có các phiên bản Bash khác nhau.

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

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

5

Bạn không thể xuất các hàm, không theo cách mà bạn đang mô tả. Shell sẽ chỉ tải ~/.bashrctệp khi bắt đầu shell tương tác (tìm kiếm "Invocation" trong trang bash ).

Những gì bạn có thể làm là tạo "thư viện" được tải khi bạn khởi động chương trình:

source "$HOME/lib/somefile"

Và đặt các chức năng và cài đặt không tương tác của bạn ở đó.


Vì vậy, tôi phải bắt đầu subshell với tham số "đăng nhập" (để phân tích cú pháp ~ / .profile) hoặc nguồn tệp đó.
Nils

Nhìn kỹ hơn một chút, trên các shell không tương tác, bạn có thể đặt BASH_ENVbiến môi trường cho some_filebạn đã có, và nó sẽ được gọi. Sẽ thật dễ dàng để tìm ra điều đó:echo echo foobar > /tmp/foobar; BASH_ENV=/tmp/foobar $SHELL -c :
Arcege

2

eval "$(declare -F | sed -e 's/-f /-fx /')"sẽ xuất tất cả các chức năng.

Tôi làm điều này rất nhiều trước khi bắt đầu một trình bao tương tác trong một tập lệnh để cho phép tôi gỡ lỗi và làm việc trong ngữ cảnh tập lệnh trong khi sử dụng các hàm và biến của nó.

Thí dụ:

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

1

Các chức năng không được xuất sang các quy trình con. Đây là lý do tại sao có các tệp có tên .kshrc hoặc .bashrc: Để xác định các hàm mà shoiuld cũng có sẵn trong các lớp con.

Nếu chạy tập lệnh, tập lệnh. * Shrc thường không có nguồn gốc. Bạn sẽ phải mã đó rõ ràng, như trong . ~/.kshrc.


Vì vậy, ~ root / .bashrc có thể là một tùy chọn trong trường hợp của tôi, vì các tập lệnh được chạy dưới dạng root. Cảm ơn vì gợi ý đó.
Nils

nếu sử dụng các tệp. * shrc, hãy chắc chắn rằng chúng không ép buộc hành vi tương tác (như bí danh ngu ngốc rm=rm -i)
ktf

0

Chà, tôi mới dùng Linux, nhưng bạn có thể thử cái này. Trong một số tệp, hãy gọi nó là 'tmp / general' mà bạn xây dựng chức năng của mình:

func1(){
   echo "func from general"
}

Trong tập lệnh shell của bạn thêm:

. /tmp/general

và chạy:

func1

Bạn sẽ nhận được trên màn hình : func from general.


0
declare -x -f NAME

Thêm thông tin

-f hạn chế hành động hoặc hiển thị tên hàm và định nghĩa
-x để xuất TÊN
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.