chức năng gọi được khai báo dưới đây


15

Có thể gọi một hàm được khai báo bên dưới trong bash không?

Thí dụ

if [ "$input" = "yes" ]; then
    YES_FUNCTION
elif [ "$input" = "no" ]; then
    NO_FUNCTION
else
    exit 0;
fi

YES_FUNCTION()
{
  .....
  .....
}

NO_FUNCTION()
{
  .....
  .....
}

Câu trả lời:


36

Giống như những người khác đã nói, bạn không thể làm điều đó.

Nhưng nếu bạn muốn sắp xếp mã thành một tệp sao cho chương trình chính nằm ở đầu tệp và các chức năng khác được xác định bên dưới, bạn có thể thực hiện bằng cách có một mainchức năng riêng .

Ví dụ

#!/bin/sh

main() {
    if [ "$1" = yes ]; then
        do_task_this
    else
        do_task_that
    fi
}

do_task_this() {
    ...
} 
do_task_that() {
    ...
} 

main "$@"; exit

Khi chúng tôi gọi mainở cuối tập tin, tất cả các chức năng đã được xác định. Hoàn toàn chuyển "$@"đến mainđược yêu cầu để làm cho các đối số dòng lệnh của tập lệnh hiển thị trong hàm.

Rõ ràng exittrên cùng một dòng với lệnh gọi tới main là không bắt buộc, nhưng có thể được sử dụng để ngăn tập lệnh đang chạy bị rối nếu tập tin tập lệnh bị sửa đổi. Không có nó, shell sẽ cố gắng tiếp tục đọc các lệnh từ tệp script sau khi maintrả về. (xem Cách đọc toàn bộ tập lệnh shell trước khi thực thi nó? )


@ikkachu nghĩ nó nên hoạt động .. để tôi kiểm tra.
msp9011

7
Với các tập lệnh Bash, tôi thường sử dụng [[ ${BASH_SOURCE[0]} = "$0" ]] && Main "$@"để gọi hàm chính để tôi có thể lấy nó trong tập lệnh khác mà không Mainbị thực thi. Sau đó, tôi có thể sử dụng lại các chức năng hoặc viết các bài kiểm tra để kiểm tra chúng.
BlackJack

11
main "$@"; exit(với exittrên cùng một dòng main) cũng hữu ích như một biện pháp bảo vệ chống lại tập tin bị sửa đổi trong khi nó được giải thích.
Stéphane Chazelas

2
@JoL, những gì đã đọc không được đọc lại và shell sẽ cần đọc và phân tích toàn bộ văn bản của một vòng lặp trước khi bắt đầu chạy nó, nhưng sau khi vòng lặp trả về, nó sẽ tiếp tục đọc từ phần còn lại của tệp tại vị trí hiện tại (và nếu tập tin đã được sửa đổi, nó sẽ làm hỏng mọi thứ). Nếu mọi thứ đều ở trong hàm, shell cần đọc mọi thứ trước khi bắt đầu làm bất cứ điều gì (ngoại trừ xác định các hàm đó), nếu chúng ta đặt dòng exittrên cùng một dòng vì mainchúng ta đảm bảo shell sẽ không đọc lại bất cứ thứ gì từ tệp sau khi maintrả về.
Stéphane Chazelas

1
@MontyHarder, không quan trọng nếu bạn sử dụng main; exit, main; exit $?hoặc main <EOF>, trong mọi trường hợp, mã thoát của mainđược sử dụng làm mã thoát của tập lệnh. Các exitsẽ chỉ để ngăn chặn điều bị sai lầm nếu ai đó chỉnh sửa kịch bản trong khi nó đang chạy.
ilkkachu

13

Không, các chức năng phải tồn tại trong môi trường shell tại thời điểm gọi chúng.

"Hướng dẫn phong cách Shell" của Google có cách khắc phục:

Một hàm được gọi mainlà cần thiết cho các tập lệnh đủ dài để chứa ít nhất một hàm khác.

Ở phần cuối của tập lệnh, sau tất cả các hàm, như câu lệnh duy nhất không có trong một hàm, bạn sẽ có

main "$@"

Điều này sẽ gọi mainhàm với bất kỳ tham số nào mà tập lệnh được đưa ra. Các mainchức năng có thể được bố trí ở phía trên cùng của kịch bản (hướng dẫn phong cách nói để đặt nó ở phía dưới, nhưng sau đó một lần nữa, nó nói nhiều điều).

Khi shell nhận được maincuộc gọi, tất cả các hàm trong tập lệnh đã được phân tích cú pháp và do đó có thể được gọi từ bên trong mainhàm.


9

Không, các chức năng phải được khai báo trước khi chúng được sử dụng. Các kịch bản Shell được đọc từng dòng và hành động theo từng dòng; vì vậy một hàm không tồn tại cho đến khi khai báo của nó được thực thi.


bạn đúng rồi. vấn đề là tôi có hơn 30 chức năng trong một tập lệnh. nó khá khó khăn khi chúng ta đọc mã. Trong Cnó thoải mái.
msp9011

3
Bạn có thể đặt các khai báo hàm của bạn trong một tệp khác và nguồn nó ( . yourfile).
Stephen Kitt

Vâng, tôi đã thử điều đó, nhưng yêu cầu là phải có một kịch bản duy nhất.
msp9011

@SivaPrasath Chính xác thì vấn đề là gì? Chỉ cần xác định tất cả các hàm, thậm chí có thể đặt mã chính vào một hàm và sau đó dòng cuối cùng hiển thị hàm nào được gọi và chứa phần chính của tập lệnh.
BlackJack

@SivaPrasath Trong C, bạn không có ifcâu lệnh trần bên ngoài hàm. Chức năng không cần phải được định nghĩa khi bạn tuyên bố các ifchức năng -containing, chỉ khi bạn gọi nó.
chepner

4

Shell không có khái niệm về declaringchức năng. Vì vậy, bạn không thể có một tuyên bố chuyển tiếp.

Kết quả là, bạn cần phải thực hiện chức năng đọc bởi shell trước khi nó có thể được gọi.


4
Về mặt kỹ thuật, một số shell (ksh, zsh) có tính năng tự động tải chức năng có thể được xem như một hình thức khai báo (trong đó autoload fkhai báo hàm, nhưng phần thân của nó chỉ được tải khi gọi lần đầu tiên). Điều đó không áp dụng cho OP bashmặc dù.
Stéphane Chazelas
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.