sự khác biệt giữa chức năng của foo () {} và và foo () {}


96

Tôi có thể định nghĩa các bashhàm bằng cách sử dụng hoặc bỏ qua functiontừ khóa. Có sự khác biệt nào không?

#!/bin/bash

function foo() {
  echo "foo"
}

bar() {
  echo "bar"
}

foo

bar

Cả hai cuộc gọi đến chức năng foobarthành công và tôi không thể thấy bất kỳ sự khác biệt. Vì vậy, tôi tự hỏi nếu nó chỉ để cải thiện khả năng đọc, hoặc có một cái gì đó mà tôi đang thiếu ...

BTW trong các shell khác như dash( /bin/shđược liên kết với nhau dashtrong debian / ubfox) nó không thành công khi sử dụng functiontừ khóa.


8
Cũng có hoặc không có dấu ngoặc đơn : function baz { echo "baz"; }. Xem Bashism trong wiki của GreyCat.
manatwork

Câu trả lời:


42

Không có sự khác biệt AFAIK, ngoài thực tế là phiên bản thứ hai dễ mang theo hơn.


32
Đó là một sự khác biệt LỚN, tính di động ... Tôi thấy quá nhiều câu trả lời đơn giản là KHÔNG hoạt động trên các hệ thống sản xuất (cũ) và cũng có nhiều tùy chọn chỉ hoạt động trên linux. Ít nhất, cảnh báo rằng đây không phải là cách dễ mang theo nhất ... Nó có thể hết sức nguy hiểm (yêu cầu ai đó tar cf - /some/thing | ssh user@desthost "cd destinationdir && tar xf - " mà không cảnh báo họ trước khi kiểm tra kỹ xem phiên bản tar trên Desthost có thoát khỏi "/" có thể dẫn đến thảm họa trong một số trường hợp ...). Ví dụ: nếu sử dụng a function tar { #a safe tar with safety checks ... }shbỏ qua nó, ...
Olivier Dulac

1
@OlivierDulac Tôi sẽ thêm các tập lệnh giả định shnhận ra functiontừ khóa - và nói chung, giả sử các tính năng phổ biến nhưng không đạt tiêu chuẩn mà shell thích kshbashcung cấp - cũng sẽ thường không hoạt động trên các hệ thống sản xuất mới hơn , ngay cả khi chúng hoạt động trên các bản phát hành cũ hơn của HĐH giống nhau. bashvẫn cung cấp shtrên nhiều hệ thống GNU / Linux, nhưng một số bản phân phối phổ biến đã chuyển sang có shmột liên kết tượng trưng đến dash(Debian Almquist SHell) để cải thiện hiệu năng. Điều này bao gồm DebianUbuntu .
Eliah Kagan

2
Không cần giải thích chính xác nó "di động hơn" như thế nào, câu trả lời là không hữu ích.
ivan_pozdeev

Tính di động hiện nay không có gì phải bàn cãi khi ngành công nghiệp đang sử dụng bash hoặc shell tương đương trong> 99,9% của tất cả các môi trường. Nó tập trung vào khả năng đọc và có hai mặt: một số người nói "chức năng" làm cho nó rõ ràng, những người học nó từ các tiêu chuẩn posix hoặc vỏ khác sẽ nói niềng răng là phong cách rõ ràng hơn. Cuối cùng, chọn một người trong nhóm hoặc công ty của bạn và bám sát nó.
Hubert Grzeskowiak

93

Các functiontừ khóa đã được giới thiệu trong ksh . Shell Bourne truyền thống chỉ có foo ()cú pháp và POSIX chỉ chuẩn hóa foo ()cú pháp.

Trong ATT ksh (nhưng không phải pdksh), có một vài khác biệt giữa các hàm được xác định bởi functionvà các hàm được xác định bằng cú pháp Bourne / POSIX. Trong các hàm được xác định bởi function, typesettừ khóa khai báo một biến cục bộ: một khi hàm thoát, giá trị của biến được đặt lại về giá trị của nó trước khi vào hàm. Với cú pháp cổ điển, các biến có phạm vi toàn cầu cho dù bạn có sử dụng typesethay không.

$ ksh -c 'a=global; f () { typeset a=local; }; f; echo $a'
local
$ ksh -c 'a=global; function f { typeset a=local; }; f; echo $a'
global

Một điểm khác biệt trong ksh là các hàm được xác định bằng functiontừ khóa có bối cảnh bẫy riêng. Các bẫy được xác định bên ngoài chức năng bị bỏ qua trong khi thực hiện chức năng và các lỗi nghiêm trọng bên trong chức năng chỉ thoát khỏi chức năng chứ không phải từ toàn bộ tập lệnh. Ngoài ra, $0là tên hàm trong một hàm được xác định bởi functionnhưng tên tập lệnh trong hàm được xác định bằng ().

Pdksh không mô phỏng ATT ksh. Trong pdksh, typesettạo các biến có phạm vi cục bộ bất kể hàm nào và không có bẫy cục bộ (mặc dù việc sử dụng functioncó một số khác biệt nhỏ - xem chi tiết trang man).

Bash và zsh đã giới thiệu functiontừ khóa để tương thích với ksh. Tuy nhiên, trong các shell này function foo { … }foo () { … }hoàn toàn giống nhau, cũng như phần mở rộng bash và zsh function foo () { … }. Các typesettừ khóa luôn tuyên bố các biến địa phương (trừ trường hợp -gđương nhiên), và bẫy không địa phương (bạn có thể bẫy địa phương trong zsh bằng cách thiết lập các local_trapstùy chọn).


8
Cần lưu ý rằng hỗ trợ chức năng đã được thêm vào trình bao Korn trước khi trình bao Bourne đưa ra foo() commandcú pháp của nó và cú pháp Bourne sau đó đã được thêm vào trình bao Korn để tương thích.
Stéphane Chazelas

2
Là chính xác mà function { ... }; f;bỏ qua fsau khi functiontừ khóa?
Ruslan

32
foo() any-command

là cú pháp Bourne hỗ trợ bởi bất kỳ shell Bourne giống như nhưng bash, yashvà các phiên bản gần đây của posh(mà chỉ hỗ trợ các lệnh ghép). (shell Bourne và các triển khai AT & T kshkhông hỗ trợ foo() any-command > redirectionstrừ khi đó any-commandlà lệnh ghép).

foo() any-compound-command

(ví dụ về hợp chất lệnh: { cmd; }, for i do echo "$i"; done, (cmd)... những con người thường được sử dụng nhất { ...; })

là cú pháp POSIX được hỗ trợ bởi bất kỳ shell nào giống Bourne và là cú pháp bạn thường muốn sử dụng.

function foo { ...; }

là cú pháp shell Korn, trước cú pháp Bourne. Chỉ sử dụng cái này nếu viết riêng cho việc triển khai AT & T của vỏ Korn và cần xử lý cụ thể mà nó nhận được ở đó. Cú pháp đó không phải là POSIX, nhưng được hỗ trợ bởi bash, yashzshđể tương thích với Korn shell mặc dù những vỏ (và các pdkshbiến thể dựa trên của vỏ Korn) không đối xử với nó bất kỳ khác với cú pháp chuẩn.

function foo () { ...; }

là cú pháp không có vỏ và không nên được sử dụng . Nó chỉ xảy ra để được hỗ trợ một cách tình cờ bởi bash, yash, zshvà các pdkshbiến thể dựa của Korn shell. Ngẫu nhiên, đó cũng là awkcú pháp chức năng.

Nếu chúng ta tiếp tục đi xuống danh sách bí truyền,

function foo() other-compound-command

(thích function foo() (subshell)hoặc function foo() for i do; ... done) thậm chí còn tồi tệ hơn. Nó được hỗ trợ bởi bash, yashzsh, nhưng không phải ksh, ngay cả các pdkshbiến thể dựa trên.

Trong khi:

function foo() simple command

chỉ được hỗ trợ bởi zsh.


1
Cú pháp bao gồm cả functiontừ khóa và dấu ngoặc đơn được ghi lại bằng Bash. Hướng dẫn của Bash 4.2 và sau đó nói rằng các hàm được khai báo theo cú pháp name () compound-command [ redirections ]hoặc function name [()] compound-command [ redirections ]. Trong Bash 4.1.11 xuống ít nhất là 3.0-beta, đó chỉ là một dòng duy nhất [ function ] name () compound-command [redirection]không bao gồm cú pháp bao gồm functiontừ khóa nhưng không phải là dấu ngoặc đơn nhưng vẫn bao gồm cú pháp bao gồm cả functiontừ khóa và dấu ngoặc đơn.
nisetama

@nise, vấn đề là bashnhận function foo {ra ngoài foo() {khả năng tương thích với trình bao Korn (và luôn luôn có) để nó có thể diễn giải các tập lệnh được viết cho trình bao Korn. Nó cũng hỗ trợ function foo () {, nhưng không có lý do chính đáng để sử dụng cái đó.
Stéphane Chazelas

2
@ StéphaneChazelas Vâng, tôi sẽ nói có một lý do tốt để sử dụng function f() {. Cụ thể, về khả năng đọc, nó sẽ được công nhận là một chức năng bởi bất kỳ ai biết tiếng Anh và bất kỳ ai biết C, so với chỉ một trong những bộ này.
DepressionDaniel

2
Nên là câu trả lời được chấp nhận. Thực sự quan trọngYou should never combine the keyword function with the parentheses () when defining a function.
4wk_

Chào mừng đến năm 2019, nơi chỉ có một tiêu chuẩn thực tế công nghiệp cho các kịch bản tự động hóa linux / unix; trình thông dịch được cài đặt gần như ở mọi nơi (môi trường kỳ lạ sang một bên): bash. Viết mã của bạn với tất cả các tính năng bash, sử dụng shebang và bạn sẽ ổn. Đối số của tính tương thích là void.
Hubert Grzeskowiak

22

Về mặt ngữ nghĩa, hai hình thức này là tương đương trong Bash.

Từ trang người đàn ông:

Các hàm Shell được khai báo như sau:

name () compound-command [redirection]
function name [()] compound-command [redirection]

Điều này định nghĩa một chức năng được đặt tên name. Các chức năng từ dành riêng là tùy chọn. Nếu từ dành riêng cho chức năng được cung cấp, dấu ngoặc đơn là tùy chọn.

EDIT: Tôi chỉ nhận thấy rằng câu hỏi này được gắn thẻ posix. Trong POSIX sh, functiontừ khóa không được sử dụng (mặc dù nó được bảo lưu).


3

Một số người khác đã trả lời đúng vào lúc này, nhưng đây là tóm tắt ngắn gọn của tôi:

Phiên bản thứ hai là di động và có khả năng hoạt động với nhiều vỏ tiêu chuẩn (đặc biệt là POSIX).

Phiên bản đầu tiên sẽ chỉ hoạt động với bash, nhưng bạn có thể bỏ qua dấu ngoặc đơn theo tên hàm với nó.

Mặt khác, chúng đại diện cho các thực thể giống hệt nhau sau khi bash diễn giải chúng.


trên thực tế, phiên bản đầu tiên không có ý nghĩa gì ngoài việc giới hạn nó dưới dạng cú pháp chấp nhận được đối với một số shell. khi bạn bao gồm () các functiontừ khóa vỏ cư xử như thể bạn chỉ cần làm foo(){ ...; }dù sao, ngoại trừ, tất nhiên, đối với một vỏ trong đó nó là cú pháp hợp lệ. và vì vậy bạn nên làm function foo { ...; }nếu bạn phải, hoặc foo(){ ...; }nếu không.
mikeerv
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.