2015-01-06 20 views
21

Thông thường, chức năng bash được định nghĩa sử dụng dấu ngoặc nhọn để kèm theo cơ thể:chức năng bash: bao quanh cơ thể trong niềng răng vs ngoặc

foo() 
{ 
    ... 
} 

Khi làm việc trên một kịch bản shell hiện nay việc sử dụng các chức năng, tôi đã chạy vào các vấn đề với các biến có cùng tên trong hàm được gọi là trong hàm gọi, cụ thể là các biến đó giống nhau. Sau đó tôi đã phát hiện ra rằng điều này có thể được ngăn chặn bằng cách xác định các biến cục bộ bên trong hàm như là địa phương: local var=xyz.

Sau đó, tại một số điểm, tôi đã phát hiện ra một sợi (Defining bash function body using parenthesis instead of braces) trong đó nó được giải thích rằng đó chỉ là hợp lệ để xác định một chức năng sử dụng dấu ngoặc đơn như thế này:

foo() 
(
    ... 
) 

Hiệu quả của việc này là rằng thân hàm được thực hiện trong một vỏ con, có lợi ích là hàm có phạm vi biến riêng của nó, cho phép tôi định nghĩa chúng mà không có cục bộ. Kể từ khi có một phạm vi chức năng địa phương dường như có ý nghĩa nhiều hơn nữa và để được an toàn hơn nhiều so với tất cả các biến là toàn cầu, tôi ngay lập tức tự hỏi mình:

  • Tại sao niềng răng được sử dụng bởi mặc định kèm theo cơ quan chức năng thay vì dấu ngoặc đơn ?

Tuy nhiên, tôi nhanh chóng cũng phát hiện ra một nhược điểm lớn để thực hiện các chức năng trong một subshell, cụ thể là thoát khỏi kịch bản từ bên trong một hàm không hoạt động nữa, thay vào đó buộc tôi phải làm việc với tư cách trở lại dọc theo toàn bộ cây gọi (trong trường hợp hàm lồng nhau). Điều này dẫn tôi đến câu hỏi tiếp theo này:

  • Có nhược điểm lớn khác (*) để sử dụng dấu ngoặc đơn thay vì niềng răng (mà có thể giải thích tại sao niềng răng dường như được ưa thích)?

(*) Tôi biết (từ các cuộc thảo luận ngoại lệ liên quan đến tôi stumbled khi theo thời gian) mà một số người cho rằng sử dụng một cách rõ ràng tình trạng lỗi là tốt hơn nhiều so với việc có thể để thoát khỏi bất cứ nơi nào, nhưng tôi thích cái sau.

Dường như cả hai kiểu đều có ưu điểm và nhược điểm của chúng. Vì vậy, tôi hy vọng một số bạn sử dụng bash nhiều kinh nghiệm có thể cho tôi một số hướng dẫn chung:

  • Khi tôi phải sử dụng dấu ngoặc nhọn để kèm theo cơ quan chức năng, và khi nào là nó khuyến khích chuyển sang ngoặc?

EDIT: Take-aways từ câu trả lời

Cám ơn câu trả lời của bạn, người đứng đầu của tôi bây giờ là một chút rõ ràng liên quan đến này. Vì vậy, những gì tôi lấy đi từ những câu trả lời là:

  • Stick với dấu ngoặc nhọn thông thường, nếu chỉ để không nhầm lẫn tiềm năng khác mà người dùng/nhà phát triển của kịch bản (và thậm chí sử dụng niềng răng nếu toàn bộ cơ thể được bọc trong ngoặc đơn).

  • Điểm bất lợi duy nhất của dấu ngoặc nhọn là bất kỳ biến nào trong phạm vi gốc đều có thể thay đổi, mặc dù trong một số trường hợp, đây có thể là một lợi thế. Điều này có thể dễ dàng bị phá vỡ bằng cách khai báo các biến là local.

  • Sử dụng dấu ngoặc đơn, có thể có một số hiệu ứng không mong muốn nghiêm trọng, chẳng hạn như thoát ra, dẫn đến các sự cố khi giết tập lệnh và cách ly phạm vi biến.

+0

Chỉnh sửa của bạn rất tốt! – fedorqui

Trả lời

12

Tại sao niềng răng được sử dụng bởi mặc định kèm theo các chức năng cơ thể thay vì dấu ngoặc đơn?

Phần thân của hàm có thể là bất kỳ lệnh ghép nào. Điều này thường là { list; }, nhưng ba hình thức khác của lệnh ghép được cho phép về mặt kỹ thuật: (list), ((expression))[[ expression ]].

C và ngôn ngữ trong họ C như C++, Java, C# và JavaScript đều sử dụng dấu ngoặc nhọn để phân định các thân hàm.Niềng răng xoăn là cú pháp tự nhiên nhất cho các lập trình viên quen thuộc với các ngôn ngữ đó.

Có những nhược điểm lớn khác (*) để sử dụng dấu ngoặc đơn thay vì dấu ngoặc nhọn (có thể giải thích tại sao dấu ngoặc nhọn được ưa thích hơn)?

Có. Có rất nhiều điều bạn không thể thực hiện từ một hệ vỏ con, bao gồm:

  • Thay đổi các biến toàn cục. Các thay đổi của biến sẽ không lan truyền đến shell cha.
  • Thoát tập lệnh. Câu lệnh exit sẽ chỉ thoát khỏi trình bao phụ.

Bắt đầu trình bao phủ phụ cũng có thể là một lần truy cập hiệu suất nghiêm trọng. Bạn đang khởi chạy một quy trình mới mỗi khi bạn gọi hàm.

Bạn cũng có thể có hành vi lạ nếu tập lệnh của bạn bị giết. Các tín hiệu cha mẹ và con sẽ nhận được sẽ thay đổi. Đó là một hiệu ứng tinh tế nhưng nếu bạn có các trình xử lý trap hoặc bạn kill tập lệnh của bạn, những phần đó không hoạt động theo cách bạn muốn.

Khi nào tôi sử dụng dấu ngoặc nhọn để đính kèm thân chức năng và khi nào nên chuyển sang dấu ngoặc đơn?

Tôi khuyên bạn nên luôn sử dụng dấu ngoặc nhọn. Nếu bạn muốn một sub-shell rõ ràng, sau đó thêm một tập hợp các dấu ngoặc đơn bên trong các dấu ngoặc nhọn. Chỉ sử dụng dấu ngoặc đơn là cú pháp rất bất thường và sẽ gây nhầm lẫn cho nhiều người đọc kịch bản của bạn.

foo() { 
    (
     subshell commands; 
    ) 
} 
+0

Mặc dù trước tiên tôi đã xem xét @ kojiro's, tôi sẽ chấp nhận câu trả lời của bạn vì bạn đã đưa ra một cách rõ ràng trả lời cho cả ba câu hỏi, cộng với bạn đã nêu ra hai điểm rất tốt với hành vi của người thừa kế nếu kịch bản bị giết (sẽ không nghĩ về điều đó) và việc sử dụng dấu ngoặc đơn không có dấu ngoặc sẽ là tổng hợp quá bất thường. – flotzilla

+0

Nhưng tôi vẫn thấy khả năng thay đổi các biến toàn cục ngoài hộp nhưng phải khai báo các biến cục bộ một cách rõ ràng chẳng hạn như nhiều nguy hiểm hơn một dấu cộng. Tôi rất muốn các biến được địa phương theo mặc định, nhưng có thể khai báo chúng như là toàn cầu để họ vẫn có thể được truy cập. Nhưng tôi sẽ đi vào thói quen luôn tuyên bố họ với 'địa phương' để giải quyết vấn đề này. – flotzilla

+1

@VaticanViolator Tôi đồng ý, sẽ đẹp hơn nếu mặc định là địa phương. Đó chỉ là một trong những điều mà bạn học cách chấp nhận và giải quyết. Ví dụ, tôi đặt tên biến cục bộ trong các biến thường và toàn cục bằng chữ hoa. Nó làm cho nó rõ ràng đó là gì và làm giảm nguy cơ vô tình chà đạp trên globals nếu tôi quên một tuyên bố 'địa phương'. –

4

Điều đó thực sự quan trọng. Vì các hàm bash không trả về các giá trị và các biến mà chúng sử dụng là từ phạm vi toàn cục (nghĩa là chúng có thể truy cập các biến từ "bên ngoài" phạm vi của nó), cách thông thường để xử lý đầu ra của hàm là lưu trữ giá trị trong một biến và sau đó gọi nó.

Khi bạn xác định hàm có (), bạn nói đúng: nó sẽ tạo ra hệ vỏ con. Sub-shell đó sẽ chứa các giá trị giống như bản gốc đã có, nhưng sẽ không thể sửa đổi chúng. Vì vậy, bạn đang mất nguồn tài nguyên của việc thay đổi các biến phạm vi toàn cầu.

Xem ví dụ:

$ cat a.sh 
#!/bin/bash 

func_braces() { #function with curly braces 
echo "in $FUNCNAME. the value of v=$v" 
v=4 
} 

func_parentheses() (
echo "in $FUNCNAME. the value of v=$v" 
v=8 
) 


v=1 
echo "v=$v. Let's start" 
func_braces 
echo "Value after func_braces is: v=$v" 
func_parentheses 
echo "Value after func_parentheses is: v=$v" 

Hãy thực hiện nó:

$ ./a.sh 
v=1. Let's start 
in func_braces. the value of v=1 
Value after func_braces is: v=4 
in func_parentheses. the value of v=4 
Value after func_parentheses is: v=4 # the value did not change in the main shell 
+1

các chức năng kỹ thuật có thể trả về một giá trị mã thoát thông qua 'return' sẽ được trả lại cho cha mẹ có thể truy cập qua chuỗi hoặc' $? ' – Catskul

4

tôi có xu hướng sử dụng một subshell khi tôi muốn thay đổi thư mục, nhưng luôn luôn từ thư mục gốc tương tự, và không thể làm phiền sử dụng pushd/popd hoặc tự quản lý các thư mục.

for d in */; do 
    (cd "$d" && dosomething) 
done 

này sẽ làm việc cũng từ một cơ quan chức năng, nhưng ngay cả khi bạn xác định chức năng với dấu ngoặc nhọn, nó vẫn còn có thể sử dụng nó từ một subshell.

doit() { 
    cd "$1" && dosomething 
} 
for d in */; do 
    (doit "$d") 
done 

Tất nhiên, bạn vẫn có thể duy trì biến phạm vi bên trong một hàm xoăn-cú đúp xác định sử dụng kê khai hoặc địa phương:

myfun() { 
    local x=123 
} 

Vì vậy, tôi sẽ nói, xác định rõ ràng chức năng của bạn như một subshell chỉ khi không là một vỏ bọc phụ là bất lợi cho hành vi đúng đắn của hàm đó.

Thông tin bên lề: Là một lưu ý phụ, hãy xem xét rằng bash thực tế luôn luôn xử lý hàm này dưới dạng lệnh ghép nối dấu ngoặc nhọn. Nó chỉ thỉnh thoảng có dấu ngoặc đơn trong đó:

$ f() (echo hi) 
$ type f 
f is a function 
f() 
{ 
    (echo hi) 
} 
Các vấn đề liên quan