Tại sao viết toàn bộ tập lệnh bash trong các hàm?


59

Trong công việc, tôi viết kịch bản bash thường xuyên. Người giám sát của tôi đã đề nghị rằng toàn bộ tập lệnh được chia thành các chức năng, tương tự như ví dụ sau:

#!/bin/bash

# Configure variables
declare_variables() {
    noun=geese
    count=three
}

# Announce something
i_am_foo() {
    echo "I am foo"
    sleep 0.5
    echo "hear me roar!"
}

# Tell a joke
walk_into_bar() {
    echo "So these ${count} ${noun} walk into a bar..."
}

# Emulate a pendulum clock for a bit
do_baz() {
    for i in {1..6}; do
        expr $i % 2 >/dev/null && echo "tick" || echo "tock"
        sleep 1
    done
}

# Establish run order
main() {
    declare_variables
    i_am_foo
    walk_into_bar
    do_baz
}

main

Có bất kỳ lý do để làm điều này ngoài "khả năng đọc", mà tôi nghĩ có thể được thiết lập tốt như nhau với một vài bình luận và khoảng cách dòng?

Liệu nó làm cho kịch bản chạy hiệu quả hơn (tôi thực sự mong đợi điều ngược lại, nếu có), hoặc nó giúp việc sửa đổi mã dễ dàng hơn ngoài tiềm năng dễ đọc đã nói ở trên? Hay nó thực sự chỉ là một sở thích phong cách?

Xin lưu ý rằng mặc dù tập lệnh không thể hiện tốt, "thứ tự chạy" của các hàm trong tập lệnh thực tế của chúng tôi có xu hướng rất tuyến tính - walk_into_barphụ thuộc vào công cụ i_am_foođã thực hiện và do_bazhoạt động trên công cụ được thiết lập bởi walk_into_bar- vì vậy có thể tự ý trao đổi thứ tự chạy không phải là thứ chúng ta thường làm. Ví dụ, bạn sẽ không đột nhiên muốn đặt declare_variablessau walk_into_bar, điều đó sẽ phá vỡ mọi thứ.

Một ví dụ về cách tôi sẽ viết kịch bản trên sẽ là:

#!/bin/bash

# Configure variables
noun=geese
count=three

# Announce something
echo "I am foo"
sleep 0.5
echo "hear me roar!"

# Tell a joke
echo "So these ${count} ${noun} walk into a bar..."

# Emulate a pendulum clock for a bit
for i in {1..6}; do
    expr $i % 2 >/dev/null && echo "tick" || echo "tock"
    sleep 1
done

30
Tôi thích ông chủ của bạn. Trong kịch bản của tôi, tôi cũng đặt main()ở trên cùng và thêm main "$@"ở dưới cùng để gọi nó. Điều đó cho phép bạn thấy logic kịch bản cấp cao đầu tiên khi bạn mở nó.
John Kugelman

22
Tôi không đồng ý với quan điểm rằng khả năng đọc có thể "được thiết lập tốt như nhau với một vài bình luận và khoảng cách dòng." Ngoại trừ có thể là hư cấu, tôi sẽ không muốn đối phó với một cuốn sách không có mục lục và tên mô tả cho mỗi chương và phần. Trong các ngôn ngữ lập trình, đó là loại dễ đọc mà các chức năng có thể cung cấp và không thể nhận xét.
Rhymoid

6
Lưu ý rằng các biến được khai báo trong các hàm nên được khai báo local- điều này cung cấp phạm vi biến cực kỳ quan trọng trong bất kỳ tập lệnh không tầm thường nào.
nhện Boris

8
Tôi không đồng ý với sếp của bạn. Nếu bạn phải chia tập lệnh của mình thành các hàm, có lẽ bạn không nên viết tập lệnh shell ở vị trí đầu tiên. Viết một chương trình thay thế.
el.pescado

6
Các hàm dành cho các quá trình được lặp lại, trong tập lệnh hoặc trong nhiều tập lệnh. Họ cũng cho phép các phương pháp thống nhất được đưa vào vị trí. Ví dụ: sử dụng một hàm để ghi vào syslog. Miễn là mọi người sử dụng cùng một chức năng, các mục nhật ký hệ thống của bạn nhất quán hơn. Các hàm sử dụng một lần như ví dụ của bạn không cần phức tạp hóa tập lệnh. Trong một số trường hợp, họ giới thiệu các vấn đề (phạm vi biến).
Xalious

Câu trả lời:


39

Tôi đã bắt đầu sử dụng cùng một kiểu lập trình bash sau khi đọc bài đăng trên blog của Kfir Lavi "Lập trình Bash phòng thủ" . Anh ta đưa ra khá nhiều lý do chính đáng, nhưng cá nhân tôi thấy đây là những lý do quan trọng nhất:

  • các thủ tục trở nên mô tả: sẽ dễ dàng hơn nhiều để tìm ra một phần cụ thể của mã được thực hiện. Thay vì tường mã, bạn thấy "Ồ, find_log_errorshàm đọc tệp nhật ký đó có lỗi". So sánh nó với việc tìm toàn bộ rất nhiều dòng awk / grep / sed sử dụng thần biết loại regex nào ở giữa một đoạn script dài - bạn không biết nó đang làm gì ở đó trừ khi có ý kiến.

  • bạn có thể gỡ lỗi các hàm bằng cách đặt vào set -xset +x. Khi bạn biết phần còn lại của mã hoạt động ổn, bạn có thể sử dụng thủ thuật này để tập trung vào gỡ lỗi chỉ chức năng cụ thể đó. Chắc chắn, bạn có thể kèm theo các phần của tập lệnh, nhưng nếu đó là một phần dài thì sao? Làm điều gì đó dễ dàng hơn như thế này:

     set -x
     parse_process_list
     set +x
    
  • sử dụng in ấn với cat <<- EOF . . . EOF. Tôi đã sử dụng nó khá nhiều lần để làm cho mã của tôi chuyên nghiệp hơn nhiều. Ngoài ra, parse_args()với getoptschức năng khá tiện lợi. Một lần nữa, điều này giúp với khả năng đọc, thay vì đẩy mọi thứ vào kịch bản như bức tường văn bản khổng lồ. Nó cũng thuận tiện để sử dụng lại.

Và rõ ràng, điều này dễ đọc hơn nhiều đối với người biết C hoặc Java hoặc Vala, nhưng có kinh nghiệm bash hạn chế. Về hiệu quả, không có nhiều thứ bạn có thể làm - bash tự nó không phải là ngôn ngữ hiệu quả nhất và mọi người thích perl và python khi nói về tốc độ và hiệu quả. Tuy nhiên, bạn có thể nicemột chức năng:

nice -10 resource_hungry_function

So với cách gọi đẹp trên từng dòng mã, điều này làm giảm toàn bộ việc gõ VÀ có thể được sử dụng thuận tiện khi bạn chỉ muốn một phần của tập lệnh chạy với mức độ ưu tiên thấp hơn.

Chạy các chức năng trong nền, theo ý kiến ​​của tôi, cũng giúp ích khi bạn muốn có toàn bộ các câu lệnh để chạy trong nền.

Một số ví dụ tôi đã sử dụng phong cách này:


3
Tôi không chắc chắn bạn nên thực hiện bất kỳ đề nghị từ bài viết đó rất nghiêm túc. Cấp, nó có một vài ý tưởng tốt, nhưng rõ ràng không có ai sử dụng để viết kịch bản. Không phải là một đơn biến trong bất kỳ ví dụ được trích dẫn (!) Và nó cho thấy sử dụng CHỮ HOA tên biến mà thường là một ý tưởng rất xấu vì chúng có thể xung đột với env hiện vars. Quan điểm của bạn trong câu trả lời này có ý nghĩa, nhưng bài viết được liên kết dường như được viết bởi một người chỉ quen với các ngôn ngữ khác và đang cố gắng ép phong cách của họ lên bash.
terdon

1
@terdon Mình quay lại bài báo và đọc lại. Nơi duy nhất mà tác giả đề cập đến việc đặt tên biến chữ hoa là trong "Biến toàn cầu bất biến". Nếu bạn coi các biến toàn cục là các biến phải nằm trong môi trường của hàm, thì việc biến chúng thành vốn. Về mặt lưu ý, hướng dẫn sử dụng của bash không quy ước trạng thái cho trường hợp biến. Ngay cả ở đây câu trả lời được chấp nhận nói "thông thường" và "tiêu chuẩn" duy nhất là của Google, không đại diện cho toàn bộ ngành CNTT.
Sergiy Kolodyazhnyy

@terdon trên một lưu ý khác, tôi đồng ý 100% rằng trích dẫn biến nên được đề cập trong bài viết, và nó cũng đã được chỉ ra trong các bình luận trên blog. Ngoài ra, tôi sẽ không phán xét ai đó sử dụng phong cách mã hóa này, bất kể họ có sử dụng ngôn ngữ khác hay không. Toàn bộ câu hỏi và câu trả lời này cho thấy rõ ràng có những ưu điểm đối với nó và mức độ của người mà họ sử dụng ngôn ngữ khác có lẽ không liên quan ở đây.
Sergiy Kolodyazhnyy

1
@terdon tốt, bài viết đã được đăng như một phần của tài liệu "nguồn". Tôi có thể đã đăng tất cả mọi thứ theo ý kiến ​​của riêng tôi, nhưng tôi chỉ cần công nhận rằng một số thứ tôi học được từ bài báo, và tất cả những điều này đến từ nghiên cứu theo thời gian. Trang linkin của tác giả cho thấy họ có kinh nghiệm tốt với Linux và CNTT nói chung, vì vậy tôi đoán bài báo không thực sự cho thấy điều đó, nhưng tôi tin tưởng vào trải nghiệm của bạn khi nói về Linux và kịch bản shell, vì vậy bạn có thể đúng .
Sergiy Kolodyazhnyy

1
Đó là một câu trả lời tuyệt vời nhưng tôi cũng muốn thêm phạm vi biến đó trong Bash thật thú vị. Vì lý do đó, tôi thích khai báo các biến của mình bên trong các hàm bằng cách sử dụng localvà gọi mọi thứ thông qua main()hàm. Điều này làm cho mọi thứ trở nên dễ quản lý hơn và bạn có thể tránh được tình huống lộn xộn.
Housni

65

Khả năng đọc là một chuyện. Nhưng có nhiều thứ để mô đun hóa hơn là chỉ này. ( Bán mô đun hóa có thể đúng hơn cho các chức năng.)

Trong các chức năng, bạn có thể giữ một số biến cục bộ, điều này làm tăng độ tin cậy , giảm khả năng mọi thứ bị rối tung.

Một pro khác của chức năng là tái sử dụng . Khi một chức năng được mã hóa, nó có thể được áp dụng nhiều lần trong tập lệnh. Bạn cũng có thể chuyển nó sang tập lệnh khác.

Mã của bạn bây giờ có thể là tuyến tính, nhưng trong tương lai bạn có thể tham gia vào lĩnh vực đa luồng hoặc đa xử lý trong thế giới Bash. Một khi bạn học cách làm mọi thứ trong các chức năng, bạn sẽ được trang bị tốt cho bước vào song song.

Thêm một điểm để thêm. Như Etsitpab Nioliv thông báo trong bình luận bên dưới, thật dễ dàng để chuyển hướng từ các chức năng như một thực thể kết hợp. Nhưng có thêm một khía cạnh của chuyển hướng với các chức năng. Cụ thể, các chuyển hướng có thể được đặt dọc theo định nghĩa hàm. Ví dụ.:

f () { echo something; } > log

Bây giờ không có chuyển hướng rõ ràng là cần thiết bởi các cuộc gọi chức năng.

$ f

Điều này có thể tiết kiệm nhiều lần lặp lại, một lần nữa làm tăng độ tin cậy và giúp giữ mọi thứ theo thứ tự.

Xem thêm


55
Câu trả lời rất tốt mặc dù nó sẽ tốt hơn nhiều nếu nó được chia thành các chức năng.
Pierre Arlaud

1
Có thể thêm các chức năng đó cho phép bạn nhập tập lệnh đó vào tập lệnh khác (bằng cách sử dụng sourcehoặc . scriptname.shvà sử dụng các chức năng đó như - nếu chúng có trong tập lệnh mới của bạn.
SnakeDoc

Điều đó đã được bao phủ trong một câu trả lời khác.
Tomasz

1
Tôi trân trọng điều đó. Nhưng tôi muốn để người khác cũng quan trọng.
Tomasz

7
Tôi đã phải đối mặt với một trường hợp ngày hôm nay khi tôi phải chuyển hướng một số đầu ra của tập lệnh sang một tập tin (để gửi nó qua email) thay vì lặp lại. Tôi chỉ đơn giản là phải thực hiện myFactor >> myFile để chuyển hướng đầu ra của các chức năng mong muốn. Khá tiện lợi. Có thể có liên quan.
Etsitpab Nioliv

39

Trong bình luận của tôi, tôi đã đề cập đến ba lợi thế của chức năng:

  1. Họ dễ dàng hơn để kiểm tra và xác minh tính chính xác.

  2. Các chức năng có thể dễ dàng được sử dụng lại (có nguồn gốc) trong các tập lệnh trong tương lai

  3. Sếp của bạn thích chúng.

Và, đừng bao giờ đánh giá thấp tầm quan trọng của số 3.

Tôi muốn giải quyết một vấn đề nữa:

... Vì vậy, việc có thể tự ý trao đổi thứ tự chạy không phải là điều chúng ta thường làm. Ví dụ, bạn sẽ không đột nhiên muốn đặt declare_variablessau walk_into_bar, điều đó sẽ phá vỡ mọi thứ.

Để có được lợi ích của việc phá mã thành các hàm, người ta nên cố gắng làm cho các hàm độc lập nhất có thể. Nếu walk_into_baryêu cầu một biến không được sử dụng ở nơi khác, thì biến đó sẽ được xác định và tạo thành cục bộ walk_into_bar. Quá trình tách mã thành các hàm và giảm thiểu các phụ thuộc lẫn nhau của chúng sẽ giúp mã rõ ràng và đơn giản hơn.

Lý tưởng nhất, các chức năng nên dễ dàng để kiểm tra cá nhân. Nếu, vì các tương tác, chúng không dễ kiểm tra, thì đó là một dấu hiệu cho thấy chúng có thể được hưởng lợi từ việc tái cấu trúc.


Tôi cho rằng đôi khi hợp lý để mô hình hóa và thực thi các phụ thuộc đó, so với tái cấu trúc để tránh chúng (vì nếu có đủ chúng, và chúng đủ lông, điều đó có thể dẫn đến trường hợp mọi thứ không còn được mô đun hóa chức năng nào cả). Một trường hợp sử dụng rất phức tạp một khi đã truyền cảm hứng cho một khung để làm việc đó .
Charles Duffy

4
Những gì cần được chia thành các chức năng nên được, nhưng ví dụ đưa nó đi quá xa. Tôi nghĩ rằng cái duy nhất thực sự làm tôi khó chịu là hàm khai báo biến. Các biến toàn cục, đặc biệt là các biến tĩnh, cần được xác định toàn cầu trong phần nhận xét dành riêng cho mục đích đó. Các biến động nên cục bộ cho các hàm sử dụng và sửa đổi chúng.
Xalious

@Xalious Tôi đã thấy một thực tiễn trong đó các biến toàn cục được Khởi tạo trong một thủ tục, như một bước trung gian và nhanh chóng trước khi phát triển một thủ tục đọc giá trị của chúng từ một tệp bên ngoài ... Tôi đồng ý rằng nó nên sạch hơn để phân tách định nghĩa và Khởi tạo nhưng hiếm khi bạn phải cúi xuống để trải qua lợi thế số 3;-)
Hastur

13

Bạn chia mã thành các hàm với cùng lý do bạn sẽ làm điều đó cho C / C ++, python, perl, ruby ​​hoặc bất kỳ mã ngôn ngữ lập trình nào. Lý do sâu xa hơn là sự trừu tượng - bạn gói gọn các nhiệm vụ cấp thấp hơn thành các nguyên hàm (hàm) cấp cao hơn để bạn không cần bận tâm về cách mọi thứ được thực hiện. Đồng thời, mã trở nên dễ đọc hơn (và có thể duy trì) và logic chương trình trở nên rõ ràng hơn.

Tuy nhiên, nhìn vào mã của bạn, tôi thấy khá kỳ quặc khi có một hàm để khai báo các biến; Điều này thực sự làm cho tôi tăng một lông mày.


Trả lời IMHO. Bạn có đề nghị khai báo các biến trong mainhàm / phương thức không?
David Tabernero M.

12

Mặc dù tôi hoàn toàn đồng ý với khả năng sử dụng lại , dễ đọc và tinh tế hôn các ông chủ nhưng có một lợi thế khác của các chức năng trong : phạm vi biến . Như LDP cho thấy :

#!/bin/bash
# ex62.sh: Global and local variables inside a function.

func ()
{
  local loc_var=23       # Declared as local variable.
  echo                   # Uses the 'local' builtin.
  echo "\"loc_var\" in function = $loc_var"
  global_var=999         # Not declared as local.
                         # Therefore, defaults to global. 
  echo "\"global_var\" in function = $global_var"
}  

func

# Now, to see if local variable "loc_var" exists outside the function.

echo
echo "\"loc_var\" outside function = $loc_var"
                                      # $loc_var outside function = 
                                      # No, $loc_var not visible globally.
echo "\"global_var\" outside function = $global_var"
                                      # $global_var outside function = 999
                                      # $global_var is visible globally.
echo                      

exit 0
#  In contrast to C, a Bash variable declared inside a function
#+ is local ONLY if declared as such.

Tôi không thấy điều này rất thường xuyên trong các kịch bản shell thế giới thực, nhưng có vẻ như đó là một ý tưởng tốt cho các kịch bản phức tạp hơn. Giảm sự gắn kết giúp tránh các lỗi trong đó bạn đang ghi đè một biến được mong đợi trong một phần khác của mã.

Khả năng sử dụng lại thường có nghĩa là tạo một thư viện chức năng chung và sourceđưa thư viện đó vào tất cả các tập lệnh của bạn. Điều này sẽ không giúp họ chạy nhanh hơn, nhưng nó sẽ giúp bạn viết chúng nhanh hơn.


Rất ít người sử dụng một cách rõ ràng local, nhưng tôi nghĩ rằng hầu hết mọi người viết các kịch bản được chia thành các chức năng vẫn tuân theo nguyên tắc thiết kế. Usign localchỉ làm cho khó khăn hơn để giới thiệu lỗi.
Voo

locallàm cho các biến có sẵn cho hàm và các phần tử con của nó, vì vậy thật tuyệt khi có một biến có thể được truyền từ hàm A, nhưng không có sẵn cho hàm B, có thể muốn có biến có cùng tên nhưng mục đích khác. Vì vậy, điều đó tốt cho việc xác định phạm vi, và như Voo đã nói - ít lỗi hơn
Sergiy Kolodyazhnyy

10

Một lý do hoàn toàn khác với những lý do đã được đưa ra trong các câu trả lời khác: một lý do đôi khi kỹ thuật này được sử dụng, trong đó câu lệnh không định nghĩa hàm duy nhất ở cấp cao nhất là một lời kêu gọi main, để đảm bảo rằng tập lệnh không vô tình làm bất cứ điều gì khó chịu nếu đoạn script bị cắt ngắn. Tập lệnh có thể bị cắt bớt nếu nó được chuyển từ quy trình A sang quy trình B (trình bao) và quy trình A chấm dứt vì bất kỳ lý do gì trước khi viết xong toàn bộ tập lệnh. Điều này đặc biệt có thể xảy ra nếu quá trình A tìm nạp tập lệnh từ tài nguyên từ xa. Mặc dù vì lý do bảo mật không phải là một ý tưởng tốt, đó là một việc đã được thực hiện và một số tập lệnh đã được sửa đổi để lường trước sự cố.


5
Hấp dẫn! Nhưng tôi thấy phiền khi người ta phải chăm sóc những thứ đó trong mỗi chương trình. Mặt khác, chính xác main()mẫu này là thông thường trong Python, nơi người ta sử dụng if __name__ == '__main__': main()ở cuối tệp.
Martin Uting

1
Thành ngữ python có lợi thế là để cho các tập lệnh khác tập lệnh importhiện tại mà không chạy main. Tôi cho rằng một người bảo vệ tương tự có thể được đưa vào một kịch bản bash.
Jake Cobb

@Jake Cobb Vâng. Bây giờ tôi làm điều đó trong tất cả các tập lệnh bash mới. Tôi có một tập lệnh chứa cơ sở hạ tầng cốt lõi của các chức năng được sử dụng bởi tất cả các tập lệnh mới. Kịch bản đó có thể có nguồn gốc hoặc được thực thi. Nếu có nguồn gốc, chức năng chính của nó không được thực thi. Phát hiện nguồn so với thực thi là thông qua thực tế là BASH_SOURCE chứa tên của tập lệnh thực thi. Nếu nó giống với tập lệnh cốt lõi, tập lệnh đang được thực thi. Mặt khác, nó có nguồn gốc.
DocSalvager

7

Một quá trình đòi hỏi một trình tự. Hầu hết các nhiệm vụ là tuần tự. Nó không có ý nghĩa để gây rối với trật tự.

Nhưng điều siêu lớn về lập trình - bao gồm cả kịch bản - đang thử nghiệm. Kiểm tra, thử nghiệm, kiểm tra. Những kịch bản kiểm tra nào hiện tại bạn phải xác nhận tính chính xác của các tập lệnh của bạn?

Sếp của bạn đang cố gắng hướng dẫn bạn từ một đứa trẻ kịch bản để trở thành một lập trình viên. Đây là một hướng tốt để đi vào. Những người đến sau bạn sẽ thích bạn.

NHƯNG. Luôn luôn nhớ rễ định hướng quá trình của bạn. Nếu nó có ý nghĩa để có các hàm được sắp xếp theo thứ tự mà chúng thường được thực thi, thì hãy làm điều đó, ít nhất là lần đầu tiên.

Sau đó, bạn sẽ thấy rằng một số chức năng của bạn đang xử lý đầu vào, đầu ra khác, xử lý khác, mô hình hóa dữ liệu khác và thao tác dữ liệu khác, do đó, có thể thông minh khi nhóm các phương thức tương tự, thậm chí có thể chuyển chúng thành các tệp riêng biệt .

Sau đó, bạn có thể nhận ra rằng bây giờ bạn đã viết thư viện các hàm trợ giúp nhỏ mà bạn sử dụng trong nhiều tập lệnh của mình.


6

Nhận xét và khoảng cách không thể đến bất cứ nơi nào gần khả năng đọc mà các chức năng có thể, như tôi sẽ chứng minh. Không có chức năng, bạn không thể nhìn thấy rừng cây - những vấn đề lớn ẩn giấu giữa nhiều dòng chi tiết. Nói cách khác, mọi người không thể đồng thời tập trung vào các chi tiết đẹp và vào bức tranh lớn. Điều đó có thể không rõ ràng trong một kịch bản ngắn; miễn là nó vẫn còn ngắn, nó có thể đọc được. Tuy nhiên, phần mềm trở nên lớn hơn, không nhỏ hơn và chắc chắn đó là một phần của toàn bộ hệ thống phần mềm của công ty bạn, chắc chắn là lớn hơn rất nhiều, có thể là hàng triệu dòng.

Hãy xem xét nếu tôi đã cho bạn hướng dẫn như thế này:

Place your hands on your desk.
Tense your arm muscles.
Extend your knee and hip joints.
Relax your arms.
Move your arms backwards.
Move your left leg backwards.
Move your right leg backwards.
(continue for 10,000 more lines)

Khi bạn đi được nửa chặng đường, hoặc thậm chí 5%, bạn sẽ quên mất những bước đầu tiên là gì. Bạn không thể phát hiện ra hầu hết các vấn đề, bởi vì bạn không thể nhìn thấy rừng cây. So sánh với các chức năng:

stand_up();
walk_to(break_room);
pour(coffee);
walk_to(office);

Điều đó chắc chắn dễ hiểu hơn nhiều, bất kể bạn có thể đặt bao nhiêu bình luận trong phiên bản tuần tự từng dòng một. Nó cũng làm cho nó xa hơn khả năng bạn sẽ nhận thấy rằng bạn quên làm cho cà phê, và có lẽ quên sit_down () ở cuối. Khi tâm trí của bạn đang nghĩ về các chi tiết của grep và awk regexes, bạn không thể nghĩ về bức tranh lớn - "nếu không có cà phê được làm" thì sao?

Các chức năng chủ yếu cho phép bạn xem bức tranh lớn và chú ý rằng bạn đã quên pha cà phê (hoặc ai đó có thể thích trà hơn). Tại một thời điểm khác, trong một khung tâm trí khác, bạn lo lắng về việc thực hiện chi tiết.

Tất nhiên cũng có những lợi ích khác được thảo luận trong các câu trả lời khác. Một lợi ích khác không được nêu rõ trong các câu trả lời khác là các chức năng cung cấp một đảm bảo quan trọng trong việc ngăn ngừa và sửa lỗi. Nếu bạn phát hiện ra rằng một số biến $ foo trong hàm thích hợp walk_to () đã sai, bạn biết rằng bạn chỉ cần nhìn vào 6 dòng khác của hàm đó để tìm mọi thứ có thể bị ảnh hưởng bởi vấn đề đó và mọi thứ có thể bị ảnh hưởng bởi vấn đề đó và mọi thứ có thể bị ảnh hưởng đã gây ra nó là sai. Không có chức năng (phù hợp), mọi thứ và mọi thứ trong toàn hệ thống có thể là nguyên nhân khiến $ foo không chính xác, và mọi thứ và mọi thứ có thể bị ảnh hưởng bởi $ foo. Do đó, bạn không thể sửa $ foo một cách an toàn mà không kiểm tra lại từng dòng của chương trình. Nếu $ foo là cục bộ của một hàm,


1
Đây không phải là bashcú pháp. Mặc dù đó là một sự xấu hổ; Tôi không nghĩ có một cách để chuyển đầu vào cho các chức năng như thế. (tức là pour();< coffee). Nó trông giống như c++hoặc php(tôi nghĩ).
lên tiếng

2
@ tjt263 không có dấu ngoặc đơn, đó là cú pháp bash: rót cà phê. Với parens, nó là khá nhiều ngôn ngữ khác. :)
Ray Morris

5

Một số sai lầm có liên quan về lập trình:

  • Chương trình của bạn sẽ thay đổi, ngay cả khi sếp của bạn khẳng định đây không phải là trường hợp.
  • Chỉ mã và đầu vào ảnh hưởng đến hành vi của chương trình.
  • Đặt tên rất khó.

Nhận xét bắt đầu như một khoảng trống cho việc không thể diễn đạt ý tưởng của bạn một cách rõ ràng bằng mã *, và trở nên tồi tệ hơn (hoặc đơn giản là sai) với sự thay đổi. Do đó, nếu có thể, hãy diễn đạt các khái niệm, cấu trúc, lý luận, ngữ nghĩa, dòng chảy, xử lý lỗi và bất cứ điều gì khác phù hợp với sự hiểu biết về mã dưới dạng mã.

Điều đó nói rằng, các hàm Bash có một số vấn đề không được tìm thấy trong hầu hết các ngôn ngữ:

  • Không gian tên là khủng khiếp ở Bash. Ví dụ: quên sử dụng localkết quả từ khóa gây ô nhiễm không gian tên toàn cầu.
  • Sử dụng local foo="$(bar)"kết quả trong việc mất mã thoát củabar .
  • Không có tham số được đặt tên, vì vậy bạn phải ghi nhớ những gì "$@"có nghĩa là trong các bối cảnh khác nhau.

* Tôi xin lỗi nếu điều này xúc phạm, nhưng sau khi sử dụng các bình luận trong một số năm và phát triển mà không có chúng ** trong nhiều năm, điều đó khá rõ ràng là vượt trội.

** Sử dụng nhận xét để cấp phép, tài liệu API và những thứ tương tự vẫn cần thiết.


Tôi thiết lập gần như tất cả các biến địa phương bằng cách tuyên bố họ null vào đầu của hàm ... local foo=""Sau đó, thiết lập chúng sử dụng thực hiện lệnh để hành động dựa trên kết quả ... foo="$(bar)" || { echo "bar() failed"; return 1; }. Điều này đưa chúng ta ra khỏi chức năng một cách nhanh chóng khi không thể đặt giá trị bắt buộc. Các dấu ngoặc nhọn là cần thiết để đảm bảo rằng return 1chỉ được thực hiện khi thất bại.
DocSalvager

5

Thời gian là tiền bạc

những câu trả lời hay khác làm sáng tỏ lý do kỹ thuật để viết mô-đun một kịch bản, có khả năng dài, được phát triển trong môi trường làm việc, được phát triển để sử dụng bởi một nhóm người và không chỉ cho mục đích sử dụng của bạn.

Tôi muốn tập trung vào một kỳ vọng: trong môi trường làm việc "thời gian là tiền bạc" . Vì vậy, sự vắng mặt của các lỗi và hiệu suất của mã của bạn được đánh giá cùng với khả năng đọc , khả năng kiểm tra, khả năng bảo trì, khả năng tái cấu trúc, khả năng tái sử dụng ...

Viết bằng "mô-đun" một mã sẽ làm giảm thời gian đọc không chỉ bởi chính người viết , mà ngay cả thời gian được sử dụng bởi người kiểm tra hoặc bởi ông chủ. Hơn nữa, lưu ý rằng thời gian của một ông chủ thường được trả nhiều hơn thời gian của một lập trình viên và rằng ông chủ của bạn sẽ đánh giá chất lượng công việc của bạn.

Hơn nữa, viết bằng "mô-đun" độc lập một mã (thậm chí là tập lệnh bash) sẽ cho phép bạn làm việc "song song" với thành phần khác trong nhóm của bạn rút ngắn thời gian sản xuất chung và sử dụng tối đa chuyên môn của một người, để xem xét hoặc viết lại một phần với không có tác dụng phụ đối với những người khác, để tái chế mã bạn vừa viết "như hiện tại"đối với chương trình / tập lệnh khác, để tạo thư viện (hoặc thư viện đoạn trích), để giảm kích thước tổng thể và khả năng xảy ra lỗi, để gỡ lỗi và kiểm tra kỹ lưỡng từng phần ... và tất nhiên nó sẽ tổ chức trong phần logic chương trình của bạn / script và tăng cường khả năng đọc của nó. Tất cả những thứ đó sẽ tiết kiệm thời gian và tiền bạc. Hạn chế là bạn phải tuân thủ các tiêu chuẩn và nhận xét các chức năng của mình (dù sao bạn cũng phải làm trong môi trường làm việc).

Để tuân thủ một tiêu chuẩn sẽ làm chậm công việc của bạn ngay từ đầu nhưng nó sẽ tăng tốc công việc của tất cả những người khác (và cả của bạn nữa) sau đó. Thật vậy, khi sự hợp tác tăng lên về số lượng người tham gia thì điều này trở thành một nhu cầu không thể tránh khỏi. Vì vậy, ví dụ, ngay cả khi tôi tin rằng các biến toàn cục phải được xác định trên toàn cầu và không phải là hàm, tôi có thể hiểu một tiêu chuẩn làm vô hiệu hóa chúng trong một hàm có tên declare_variables()luôn nằm trong dòng đầu tiên của main()...

Cuối cùng nhưng không kém phần quan trọng, đừng đánh giá thấp khả năng trong các trình soạn thảo mã nguồn hiện đại để hiển thị hoặc ẩn các thói quen riêng biệt có chọn lọc ( Mã gấp ). Điều này sẽ giữ cho mã nhỏ gọn và tập trung người dùng tiết kiệm thời gian một lần nữa.

nhập mô tả hình ảnh ở đây

Ở đây trên bạn có thể thấy nó được mở ra như thế nào chỉ có walk_into_bar()chức năng. Ngay cả những dòng khác dài 1000 dòng, bạn vẫn có thể kiểm soát tất cả mã trong một trang. Lưu ý rằng nó được gấp lại ngay cả phần bạn đi đến khai báo / khởi tạo các biến.


1

Ngoài các lý do được đưa ra trong các câu trả lời khác:

  1. Tâm lý học: Một lập trình viên có năng suất được đo bằng các dòng mã sẽ có động cơ để viết mã dài dòng không cần thiết. Càng nhiều quản lý tập trung vào các dòng mã, lập trình viên càng có nhiều động lực để mở rộng mã của mình với sự phức tạp không cần thiết. Điều này là không mong muốn vì sự phức tạp gia tăng có thể dẫn đến tăng chi phí bảo trì và tăng nỗ lực cần thiết để sửa lỗi.

Đó không phải là câu trả lời quá tệ, như các downvote nói. Lưu ý: agc nói, đây cũng là một khả năng, và đúng vậy. Anh ta không nói đó là khả năng duy nhất và không buộc tội bất kỳ ai, chỉ nêu ra sự thật. Mặc dù tôi nghĩ rằng ngày nay gần như chưa từng nghe thấy một công việc hợp đồng kiểu "dòng mã" trực tiếp -> "$$", nhưng có nghĩa là nó khá phổ biến, vâng, khối lượng mã được sản xuất được tính bởi các nhà lãnh đạo / ông chủ.
dùng259412

0

Một lý do khác thường bị bỏ qua là phân tích cú pháp của bash:

set -eu

echo "this shouldn't run"
{
echo "this shouldn't run either"

Kịch bản này rõ ràng có lỗi cú pháp và bash không nên chạy nó, phải không? Sai lầm.

~ $ bash t1.sh
this shouldn't run
t1.sh: line 7: syntax error: unexpected end of file

Nếu chúng ta bọc mã trong một hàm, điều này sẽ không xảy ra:

set -eu

main() {
  echo "this shouldn't run"
  {
  echo "this shouldn't run either"
}

main
~ $ bash t1.sh
t1.sh: line 10: syntax error: unexpected end of file
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.