Có ai biết về bất kỳ tài nguyên nào nói về các phương pháp hay nhất hoặc các mẫu thiết kế cho các kịch bản shell (sh, bash, v.v.) không?Các mẫu thiết kế hoặc thực hành tốt nhất cho các kịch bản shell
Trả lời
Tôi đã viết các kịch bản shell khá phức tạp và đề xuất đầu tiên của tôi là "không". Lý do là khá dễ gây ra một sai lầm nhỏ làm cản trở kịch bản của bạn, hoặc thậm chí làm cho nó nguy hiểm.
Điều đó nói rằng, tôi không có các tài nguyên khác để vượt qua bạn nhưng trải nghiệm cá nhân của tôi. Đây là những gì tôi thường làm, đó là quá mức cần thiết, nhưng có xu hướng vững chắc, mặc dù rất tiết.
Invocation
làm cho kịch bản của bạn chấp nhận tùy chọn dài và ngắn. hãy cẩn thận vì có hai lệnh để phân tích cú pháp các tùy chọn, getopt và getopts. Sử dụng getopt khi bạn gặp ít rắc rối hơn.
CommandLineOptions__config_file=""
CommandLineOptions__debug_level=""
getopt_results=`getopt -s bash -o c:d:: --long config_file:,debug_level:: -- "[email protected]"`
if test $? != 0
then
echo "unrecognized option"
exit 1
fi
eval set -- "$getopt_results"
while true
do
case "$1" in
--config_file)
CommandLineOptions__config_file="$2";
shift 2;
;;
--debug_level)
CommandLineOptions__debug_level="$2";
shift 2;
;;
--)
shift
break
;;
*)
echo "$0: unparseable option $1"
EXCEPTION=$Main__ParameterException
EXCEPTION_MSG="unparseable option $1"
exit 1
;;
esac
done
if test "x$CommandLineOptions__config_file" == "x"
then
echo "$0: missing config_file parameter"
EXCEPTION=$Main__ParameterException
EXCEPTION_MSG="missing config_file parameter"
exit 1
fi
Một điểm quan trọng khác là chương trình phải luôn trả về 0 nếu hoàn thành thành công, khác 0 nếu có sự cố.
Chức năng gọi
Bạn có thể gọi chức năng trong bash, chỉ cần nhớ để xác định chúng trước khi cuộc gọi. Các hàm giống như các tập lệnh, chúng chỉ có thể trả về các giá trị số. Điều này có nghĩa là bạn phải phát minh ra một chiến lược khác nhau để trả về các giá trị chuỗi. Chiến lược của tôi là sử dụng một biến gọi là RESULT để lưu trữ kết quả và trả về 0 nếu hàm được hoàn thành một cách rõ ràng. Ngoài ra, bạn có thể tăng ngoại lệ nếu bạn trả về một giá trị khác 0 và sau đó đặt hai biến ngoại lệ (ví dụ: EXCEPTION và EXCEPTION_MSG), loại đầu tiên chứa loại ngoại lệ và thứ hai là thông báo có thể đọc được của con người.
Khi bạn gọi một hàm, các tham số của hàm được gán cho các vars đặc biệt $ 0, $ 1 vv. Tôi khuyên bạn nên đặt chúng vào các tên có ý nghĩa hơn. khai báo các biến bên trong hàm như địa phương:
function foo {
local bar="$0"
}
Lỗi tình huống dễ bị
Trong bash, trừ khi bạn khai báo bằng cách khác, một biến unset được sử dụng như một chuỗi rỗng. Điều này rất nguy hiểm trong trường hợp lỗi đánh máy, vì biến bị đánh sai sẽ không được báo cáo và biến này sẽ được đánh giá là trống. sử dụng
set -o nounset
để ngăn điều này xảy ra. Hãy cẩn thận, bởi vì nếu bạn làm điều này, chương trình sẽ hủy bỏ mỗi khi bạn đánh giá một biến không xác định.Vì lý do này, cách duy nhất để kiểm tra xem một biến không được định nghĩa như sau:
if test "x${foo:-notset}" == "xnotset"
then
echo "foo not set"
fi
Bạn có thể khai báo các biến như readonly:
readonly readonly_var="foo"
Mô-đun hóa
Bạn có thể đạt được mô đun "python like" nếu bạn sử dụng mã sau:
set -o nounset
function getScriptAbsoluteDir {
# @description used to get the script path
# @param $1 the script $0 parameter
local script_invoke_path="$1"
local cwd=`pwd`
# absolute path ? if so, the first character is a/
if test "x${script_invoke_path:0:1}" = 'x/'
then
RESULT=`dirname "$script_invoke_path"`
else
RESULT=`dirname "$cwd/$script_invoke_path"`
fi
}
script_invoke_path="$0"
script_name=`basename "$0"`
getScriptAbsoluteDir "$script_invoke_path"
script_absolute_dir=$RESULT
function import() {
# @description importer routine to get external functionality.
# @description the first location searched is the script directory.
# @description if not found, search the module in the paths contained in $SHELL_LIBRARY_PATH environment variable
# @param $1 the .shinc file to import, without .shinc extension
module=$1
if test "x$module" == "x"
then
echo "$script_name : Unable to import unspecified module. Dying."
exit 1
fi
if test "x${script_absolute_dir:-notset}" == "xnotset"
then
echo "$script_name : Undefined script absolute dir. Did you remove getScriptAbsoluteDir? Dying."
exit 1
fi
if test "x$script_absolute_dir" == "x"
then
echo "$script_name : empty script path. Dying."
exit 1
fi
if test -e "$script_absolute_dir/$module.shinc"
then
# import from script directory
. "$script_absolute_dir/$module.shinc"
elif test "x${SHELL_LIBRARY_PATH:-notset}" != "xnotset"
then
# import from the shell script library path
# save the separator and use the ':' instead
local saved_IFS="$IFS"
IFS=':'
for path in $SHELL_LIBRARY_PATH
do
if test -e "$path/$module.shinc"
then
. "$path/$module.shinc"
return
fi
done
# restore the standard separator
IFS="$saved_IFS"
fi
echo "$script_name : Unable to find module $module."
exit 1
}
sau đó bạn có thể import các file có phần mở rộng .shinc với cú pháp sau
nhập khẩu "AModule/ModuleFile"
nào sẽ được tìm kiếm trong SHELL_LIBRARY_PATH. Khi bạn luôn nhập vào không gian tên chung, hãy nhớ tiền tố tất cả các hàm và biến của bạn với một tiền tố thích hợp, nếu không bạn có nguy cơ bị xung đột tên. Tôi sử dụng dấu gạch dưới kép như dấu chấm python.
Ngoài ra, đặt này là điều đầu tiên trong module của bạn
# avoid double inclusion
if test "${BashInclude__imported+defined}" == "defined"
then
return 0
fi
BashInclude__imported=1
Object lập trình hướng
Trong bash, bạn không thể làm lập trình hướng đối tượng, trừ khi bạn xây dựng một hệ thống khá phức tạp của phân bổ đối tượng (tôi đã nghĩ về điều đó. Nó khả thi, nhưng điên rồ). Trong thực tế, bạn có thể thực hiện "Lập trình theo định hướng Singleton": bạn có một cá thể của mỗi đối tượng và chỉ một đối tượng.
Điều tôi làm là: tôi định nghĩa một đối tượng thành một mô-đun (xem mục nhập mô-đun hóa). Sau đó, tôi xác định vars trống (tương tự như các biến thành viên) một hàm init (constructor) và hàm thành viên, giống như trong mã ví dụ này
# avoid double inclusion
if test "${Table__imported+defined}" == "defined"
then
return 0
fi
Table__imported=1
readonly Table__NoException=""
readonly Table__ParameterException="Table__ParameterException"
readonly Table__MySqlException="Table__MySqlException"
readonly Table__NotInitializedException="Table__NotInitializedException"
readonly Table__AlreadyInitializedException="Table__AlreadyInitializedException"
# an example for module enum constants, used in the mysql table, in this case
readonly Table__GENDER_MALE="GENDER_MALE"
readonly Table__GENDER_FEMALE="GENDER_FEMALE"
# private: prefixed with p_ (a bash variable cannot start with _)
p_Table__mysql_exec="" # will contain the executed mysql command
p_Table__initialized=0
function Table__init {
# @description init the module with the database parameters
# @param $1 the mysql config file
# @exception Table__NoException, Table__ParameterException
EXCEPTION=""
EXCEPTION_MSG=""
EXCEPTION_FUNC=""
RESULT=""
if test $p_Table__initialized -ne 0
then
EXCEPTION=$Table__AlreadyInitializedException
EXCEPTION_MSG="module already initialized"
EXCEPTION_FUNC="$FUNCNAME"
return 1
fi
local config_file="$1"
# yes, I am aware that I could put default parameters and other niceties, but I am lazy today
if test "x$config_file" = "x"; then
EXCEPTION=$Table__ParameterException
EXCEPTION_MSG="missing parameter config file"
EXCEPTION_FUNC="$FUNCNAME"
return 1
fi
p_Table__mysql_exec="mysql --defaults-file=$config_file --silent --skip-column-names -e "
# mark the module as initialized
p_Table__initialized=1
EXCEPTION=$Table__NoException
EXCEPTION_MSG=""
EXCEPTION_FUNC=""
return 0
}
function Table__getName() {
# @description gets the name of the person
# @param $1 the row identifier
# @result the name
EXCEPTION=""
EXCEPTION_MSG=""
EXCEPTION_FUNC=""
RESULT=""
if test $p_Table__initialized -eq 0
then
EXCEPTION=$Table__NotInitializedException
EXCEPTION_MSG="module not initialized"
EXCEPTION_FUNC="$FUNCNAME"
return 1
fi
id=$1
if test "x$id" = "x"; then
EXCEPTION=$Table__ParameterException
EXCEPTION_MSG="missing parameter identifier"
EXCEPTION_FUNC="$FUNCNAME"
return 1
fi
local name=`$p_Table__mysql_exec "SELECT name FROM table WHERE id = '$id'"`
if test $? != 0 ; then
EXCEPTION=$Table__MySqlException
EXCEPTION_MSG="unable to perform select"
EXCEPTION_FUNC="$FUNCNAME"
return 1
fi
RESULT=$name
EXCEPTION=$Table__NoException
EXCEPTION_MSG=""
EXCEPTION_FUNC=""
return 0
}
Bẫy và xử lý tín hiệu
Tôi thấy điều này rất hữu ích để bắt và xử lý ngoại lệ.
function Main__interruptHandler() {
# @description signal handler for SIGINT
echo "SIGINT caught"
exit
}
function Main__terminationHandler() {
# @description signal handler for SIGTERM
echo "SIGTERM caught"
exit
}
function Main__exitHandler() {
# @description signal handler for end of the program (clean or unclean).
# probably redundant call, we already call the cleanup in main.
exit
}
trap Main__interruptHandler INT
trap Main__terminationHandler TERM
trap Main__exitHandler EXIT
function Main__main() {
# body
}
# catch signals and exit
trap exit INT TERM EXIT
Main__main "[email protected]"
gợi ý và lời khuyên
Nếu có gì không làm việc cho một số lý do, hãy cố gắng sắp xếp lại các mã. Đặt hàng là quan trọng và không phải lúc nào cũng trực quan.
thậm chí không xem xét làm việc với tcsh. nó không hỗ trợ chức năng, và nó nói chung là khủng khiếp.
Hy vọng điều đó sẽ hữu ích, mặc dù xin lưu ý. Nếu bạn phải sử dụng những thứ tôi đã viết ở đây, điều đó có nghĩa là vấn đề của bạn quá phức tạp để giải quyết bằng shell. sử dụng ngôn ngữ khác. Tôi đã phải sử dụng nó do yếu tố con người và di sản.
Wow, và tôi nghĩ rằng tôi sẽ cho overkill trong bash ... Tôi có xu hướng sử dụng các chức năng bị cô lập và subshells lạm dụng (do đó tôi bị ảnh hưởng khi tốc độ là trong bất kỳ cách nào có liên quan). Không có biến toàn cục nào, không trong và ngoài (để giữ gìn sự tĩnh lặng). Tất cả trở lại thông qua đầu ra hoặc đầu ra tệp. set -u/set -e (quá xấu thiết lập -e trở thành vô ích ngay sau khi đầu tiên nếu, và hầu hết các mã của tôi thường là ở đó). Đối số hàm được thực hiện với [local something = "$ 1"; shift] (cho phép sắp xếp lại dễ dàng khi tái cấu trúc). Sau một 3000 dòng bash script tôi có xu hướng viết kịch bản nhỏ nhất trong thời trang này ... – Eugene
ur một nhà khoa học điên – Prospero
sửa chữa nhỏ cho mô-đun hóa: 1 bạn cần trở lại sau . "$ script_absolute_dir/$ module.shinc" để tránh bị thiếu cảnh báo. 2 bạn phải đặt IFS = "$ saved_IFS" trước khi bạn quay trở lại tìm mô-đun trong $ SHELL_LIBRARY_PATH – Duff
Dễ dàng: sử dụng python thay vì tập lệnh shell. Bạn có thể tăng gấp 100 lần khả năng đọc, mà không phải làm phức tạp bất cứ thứ gì bạn không cần, và bảo toàn khả năng phát triển các phần của tập lệnh thành các hàm, đối tượng, đối tượng liên tục (zodb), các đối tượng phân tán (pyro) gần bất kỳ mã bổ sung nào.
bạn mâu thuẫn với chính mình bằng cách nói "không cần phải phức tạp" và liệt kê các phức tạp khác nhau mà bạn cho là thêm giá trị, trong khi hầu hết các trường hợp đều bị lạm dụng vào quái vật xấu xí thay vì được sử dụng để đơn giản hóa các vấn đề và triển khai. – Evgeny
điều này ngụ ý một nhược điểm lớn, kịch bản của bạn sẽ không được di chuyển trên các hệ thống mà python không có mặt – astropanic
Tôi nhận ra điều này đã được trả lời trong '08 (bây giờ là hai ngày trước '12); tuy nhiên, đối với những người nhìn vào những năm sau này, tôi sẽ cảnh báo bất cứ ai chống lại các ngôn ngữ như Python hay Ruby vì nó có nhiều khả năng hơn và nếu không, đó là lệnh (hoặc vài lần nhấp chuột) không được cài đặt . Nếu bạn cần thêm tính di động, hãy nghĩ đến việc viết chương trình của bạn bằng Java vì bạn sẽ khó có thể tìm thấy một máy không có JVM. –
Hoặc quote cũ tương tự như những gì Joao nói: ". Sử dụng perl Bạn sẽ muốn biết bash nhưng không sử dụng nó"
Đáng buồn là tôi đã quên người đã nói điều đó.
Và có những ngày này tôi muốn giới thiệu python over perl.
sử dụng set -e để bạn không cày về phía trước sau khi lỗi. Hãy thử làm cho nó sh tương thích mà không dựa vào bash nếu bạn muốn nó chạy trên không-linux.
Hãy xem Advanced Bash-Scripting Guide để có nhiều sự khôn ngoan về kịch bản lệnh shell - không chỉ Bash.
Đừng nghe mọi người nói với bạn để xem các ngôn ngữ khác, được cho là phức tạp hơn. Nếu shell scripting đáp ứng nhu cầu của bạn, hãy sử dụng nó. Bạn muốn có chức năng, chứ không phải fanciness. Các ngôn ngữ mới cung cấp các kỹ năng mới có giá trị cho bản lý lịch của bạn, nhưng điều đó không giúp ích gì nếu bạn có công việc cần được thực hiện và bạn đã biết trình bao.
Như đã nêu, không có nhiều "phương pháp hay nhất" hoặc "mẫu thiết kế" cho kịch bản lệnh shell. Các cách sử dụng khác nhau có các nguyên tắc và xu hướng khác nhau - giống như bất kỳ ngôn ngữ lập trình nào khác.
Lưu ý rằng đối với các tập lệnh có độ phức tạp nhỏ, đó không phải là cách thực hành tốt nhất. Mã hóa không phải là về việc nhận được một cái gì đó để làm việc. Đó là về xây dựng nó một cách nhanh chóng, dễ dàng, và nó là đáng tin cậy, tái sử dụng, và dễ đọc và duy trì (đặc biệt là cho những người khác). Các kịch bản lệnh shell không mở rộng tốt ở mọi cấp độ. Các ngôn ngữ mạnh mẽ hơn đơn giản hơn nhiều đối với các dự án có bất kỳ logic nào. – drifter
Có một phiên tuyệt vời tại OSCON năm nay (2008) trên chỉ chủ đề này: http://assets.en.oreilly.com/1/event/12/Shell%20Scripting%20Craftsmanship%20Presentation%201.pdf
Biết khi nào nên sử dụng nó. Đối với các lệnh dán nhanh và bẩn cùng nhau thì không sao. Nếu bạn cần thực hiện nhiều hơn một vài quyết định không tầm thường, các vòng lặp, bất cứ điều gì, hãy sử dụng Python, Perl và modularize.
Vấn đề lớn nhất với vỏ thường là kết quả cuối cùng trông giống như một quả bóng bùn lớn, 4000 dòng bash và đang phát triển ... và bạn không thể loại bỏ nó vì bây giờ toàn bộ dự án của bạn phụ thuộc vào nó. Tất nhiên, nó bắt đầu tại 40 dòng của bash đẹp.
Để tìm một số "thực hành tốt nhất", hãy tìm cách Linux distro (ví dụ Debian) viết của họ init-script (thường được tìm thấy trong /etc/init.d)
Hầu hết trong số đó là không có "bash-ISMS" và có sự tách biệt tốt các cài đặt cấu hình, thư viện và định dạng nguồn.
Kiểu cá nhân của tôi là viết một bản mô tả chính xác định một số biến mặc định và sau đó cố gắng tải ("nguồn") tệp cấu hình có thể chứa các giá trị mới.
Tôi cố gắng tránh các chức năng vì chúng có xu hướng làm cho tập lệnh phức tạp hơn. (Perl được tạo ra cho mục đích đó.)
Để đảm bảo tập lệnh là di động, hãy kiểm tra không chỉ bằng #!/Bin/sh, mà còn sử dụng #!/Bin/ash, #!/Bin/dash, v.v. .Bạn sẽ phát hiện mã cụ thể Bash sớm đủ.
tập lệnh shell là ngôn ngữ được thiết kế để thao tác các tệp và quy trình. Trong khi nó tuyệt vời cho điều đó, nó không phải là một ngôn ngữ mục đích chung, vì vậy luôn luôn cố gắng để keo logic từ tiện ích hiện có hơn là tái tạo logic mới trong kịch bản shell.
Khác với nguyên tắc chung đó tôi đã thu thập một số common shell script mistakes.
- 1. Thực tiễn BDD tốt nhất để thiết kế kịch bản dưa chuột cho các biểu mẫu
- 2. Mẫu thiết kế tốt nhất cho kịch bản sau
- 3. PHP mẫu thiết kế thực hành tốt nhất
- 4. Trang web tốt nhất cho các mẫu thiết kế?
- 5. Hiệu quả D: thực hành tốt nhất và các mẫu thiết kế
- 6. Thực hành tốt nhất cho mẫu DAO?
- 7. Mẫu thiết kế máy khách dịch vụ Web (thực hành tốt nhất)
- 8. Thực hành các mẫu tạo kiểu tốt nhất
- 9. mẫu thiết kế tốt nhất cho "undo" tính năng
- 10. Thực hành tốt nhất khi thiết kế các dịch vụ web SOA WCF là gì?
- 11. Thiết kế mẫu tốt cho nhiều lịch sử thực thể?
- 12. Thực hành tốt nhất thừa kế: * args, ** kwargs hoặc chỉ định rõ ràng các tham số
- 13. Thực hành tốt nhất cho thiết kế mô hình trong Ruby on Rails
- 14. Thực hành tốt nhất cho các sản phẩm tự động
- 15. Các mẫu thiết kế cho thiết kế đồng thời/tác nhân dựa trên thiết kế
- 16. Thiết kế API - thực hành tốt nhất và cách hỗ trợ nhiều phiên bản
- 17. Ngoài các mẫu thiết kế?
- 18. Cách tốt nhất để thiết kế kịch bản cơ sở dữ liệu này là gì?
- 19. Trình thiết kế GUI tốt nhất cho nhật thực?
- 20. Thực hành tốt nhất Permalink cho các trang ajax
- 21. Nguyên tắc thiết kế, thực tiễn tốt nhất và mẫu thiết kế cho C (hoặc lập trình thủ tục nói chung)?
- 22. Các mẫu thiết kế JBoss Seam?
- 23. Vaadin: Các mẫu thiết kế
- 24. Thực hành tốt nhất: Viết kịch bản Cross-Site hợp pháp
- 25. Thiết kế cơ sở dữ liệu Thực tiễn tốt nhất
- 26. Thực hành tốt nhất cho các hằng số Python & Django
- 27. Thực hành các chỉ số thiết kế Android?
- 28. mảng kịch bản Shell
- 29. Thực hành tốt nhất cho CSS rõ ràng hoặc tràn
- 30. ngôn ngữ kịch bản tốt nhất cho Unity3D
Tôi vừa viết một bài viết nhỏ về [mẫu mẫu trong BASH] (http://quickshiftin.com/blog/2014/01/template-method-pattern-bash/) tối qua. Xem những gì bạn nghĩ. – quickshiftin