為什麼要在函式中編寫整個 bash 腳本?
在工作中,我經常編寫 bash 腳本。我的主管建議將整個腳本分解為函式,類似於以下範例:
#!/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
除了“可讀性”之外,還有什麼理由這樣做,我認為可以通過更多的評論和一些行距來同樣完善它?
它是否使腳本更有效地執行(我實際上希望相反,如果有的話),或者它是否使修改程式碼變得更容易超出上述可讀性潛力?或者它真的只是一種風格偏好?
請注意,雖然腳本沒有很好地展示它,但我們實際腳本中函式的“執行順序”往往是非常線性的——
walk_into_bar
取決於i_am_foo
已經完成的東西,並do_baz
作用於由設置的東西walk_into_bar
——所以能夠任意交換執行順序不是我們通常會做的事情。例如,您不會突然想在declare_variables
之後添加walk_into_bar
,那會破壞事情。我將如何編寫上述腳本的一個範例是:
#!/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
在閱讀了Kfir Lavi 的博文“防禦性 Bash 程式”之後,我開始使用這種相同風格的 bash 程式。他給出了很多很好的理由,但我個人認為這些是最重要的:
- 程序變得具有描述性:更容易弄清楚程式碼的特定部分應該做什麼。您看到的不是程式碼牆,而是“哦,
find_log_errors
函式讀取該日誌文件以查找錯誤”。將它與在冗長的腳本中間找到大量使用上帝知道什麼類型的正則表達式的 awk/grep/sed 行進行比較——除非有評論,否則你不知道它在那裡做什麼。set -x
您可以通過包含在和中來調試函式set +x
。一旦你知道其餘的程式碼工作正常,你就可以使用這個技巧只專注於調試那個特定的功能。當然,您可以附上部分腳本,但如果它是一個冗長的部分怎麼辦?這樣做更容易:set -x parse_process_list set +x
- 列印使用
cat <<- EOF . . . EOF
。我已經多次使用它來使我的程式碼更加專業。另外,parse_args()
withgetopts
功能相當方便。同樣,這有助於提高可讀性,而不是將所有內容作為巨大的文本牆塞進腳本中。重複使用這些也很方便。顯然,這對於了解 C、Java 或 Vala 但 bash 經驗有限的人來說更具可讀性。就效率而言,您可以做的事情並不多——bash 本身並不是最有效的語言,而且在速度和效率方面人們更喜歡 perl 和 python。但是,您可以
nice
使用以下功能:nice -10 resource_hungry_function
與在每一行程式碼上呼叫 nice 相比,這減少了大量的輸入,並且當您只希望腳本的一部分以較低優先級執行時可以方便地使用。
在我看來,在後台執行函式也有助於在後台執行大量語句。
我使用這種風格的一些例子:
可讀性是一回事。但是模組化不僅僅是這個。(半模組化可能更適合功能。)
在函式中,您可以將一些變數保留在本地,從而提高可靠性,減少事情搞砸的機會。
功能的另一個優點是可重用性。一旦對函式進行編碼,就可以在腳本中多次應用它。您還可以將其移植到另一個腳本。
您現在的程式碼可能是線性的,但將來您可能會進入多執行緒領域,或者Bash 世界中的*多處理領域。*一旦你學會了在函式中做事,你就會為進入並行階段做好準備。
還要補充一點。正如 Etsitpab Nioliv 在下面的評論中指出的那樣,很容易從功能中重定向為一個連貫的實體。但函式重定向還有一個方面。即,可以沿著函式定義設置重定向。例如。:
f () { echo something; } > log
現在函式呼叫不需要顯式重定向。
$ f
這可以避免多次重複,從而再次提高可靠性並有助於保持秩序。
也可以看看