1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        如何寫出安全的、基本功能完善的Bash腳本

        共 6022字,需瀏覽 13分鐘

         ·

        2020-12-26 13:12


        每個人或多或少總會碰到要使用并且自己完成編寫一個最基礎(chǔ)的Bash腳本的情況。真實情況是,沒有人會說“哇哦,我喜歡寫這些腳本”。所以這也是為什么很少有人在寫的時候?qū)W⒃谶@些腳本上。

        我本身也不是一個Bash腳本專家,但是我會在本文中跟你展示一個最基礎(chǔ)最簡單的安全腳本模板,會讓你寫的Bash腳本更加安全實用,你掌握了之后肯定會受益匪淺。

        為什么要寫B(tài)ash腳本

        其實關(guān)于Bash腳本最好的解釋如下:

        The opposite of "it's like riding a bike" is "it's like programming in bash".

        A phrase which means that no matter how many times you do something, you will have to re-learn it every single time.

        — Jake Wharton (@JakeWharton)

        December 2, 2020

        意思就是,跟騎自行車相反,無論做了多少次,每次都感覺像重新學(xué)一樣。

        但是Bash腳本語言和其他一些廣受歡迎的語言,例如JavaScript一樣,他們不會輕易突然消失,雖然Bash腳本語言不太可能成為業(yè)界的主流語言,但實際他就在我們周圍,無處不在。

        Bash就像繼承了shell的衣缽一樣,在每臺linux上都可以看到他的身影,這可是大多數(shù)后端程序運行的環(huán)境,因此當你需要編寫服務(wù)器的應(yīng)用程序啟動、CI/CD步驟或集成測試用的腳本,Bash就在那里等著你。

        將幾個命令粘在一起,將輸出從一個傳遞到另一個,然后只啟動一些可執(zhí)行文件,Bash是眾多方案中最簡單的一個。雖然用其他語言編寫更大、更復(fù)雜的腳本更有效果,但你不能指望Python、Ruby、fish或其他任何你認為最好的程序,可以在任何地方編譯使用。所以在將其添加到某個prod server、Docker image或CI環(huán)境之前,往往會讓人三思而后行。

        當然啦,Bash還遠遠不夠完美兩個字。他的語法對初學(xué)者就像一個噩夢。錯誤處理也很困難。到處都是我們必須處理掉的陷阱。

        Bash script template(Bash腳本模板)

        廢話不多說,獻上我的模板


        #!/usr/bin/env?bash

        set?-Eeuo?pipefail
        trap?cleanup?SIGINT?SIGTERM?ERR?EXIT

        script_dir=$(cd?"$(dirname?"${BASH_SOURCE[0]}")"?&>/dev/null?&&?pwd?-P)

        usage()?{
        ??cat?<Usage:?$(basename?"${BASH_SOURCE[0]}")?[-h]?[-v]?[-f]?-p?param_value?arg1?[arg2...]

        Script?description?here.

        Available?options:

        -h,?--help??????Print?this?help?and?exit
        -v,?--verbose???Print?script?debug?info
        -f,?--flag??????Some?flag?description
        -p,?--param?????Some?param?description
        EOF
        ??exit
        }

        cleanup()?{
        ??trap?-?SIGINT?SIGTERM?ERR?EXIT
        ??#?script?cleanup?here
        }

        setup_colors()?{
        ??if?[[?-t?2?]]?&&?[[?-z?"${NO_COLOR-}"?]]?&&?[[?"${TERM-}"?!=?"dumb"?]];?then
        ????NOFORMAT='\033[0m'?RED='\033[0;31m'?GREEN='\033[0;32m'?ORANGE='\033[0;33m'?BLUE='\033[0;34m'?PURPLE='\033[0;35m'?CYAN='\033[0;36m'?YELLOW='\033[1;33m'
        ??else
        ????NOFORMAT=''?RED=''?GREEN=''?ORANGE=''?BLUE=''?PURPLE=''?CYAN=''?YELLOW=''
        ??fi
        }

        msg()?{
        ??echo?>&2?-e?"${1-}"
        }

        die()?{
        ??local?msg=$1
        ??local?code=${2-1}?#?default?exit?status?1
        ??msg?"$msg"
        ??exit?"$code"
        }

        parse_params()?{
        ??#?default?values?of?variables?set?from?params
        ??flag=0
        ??param=''

        ??while?:;?do
        ????case?"${1-}"?in
        ????-h?|?--help)?usage?;;
        ????-v?|?--verbose)?set?-x?;;
        ????--no-color)?NO_COLOR=1?;;
        ????-f?|?--flag)?flag=1?;;?#?example?flag
        ????-p?|?--param)?#?example?named?parameter
        ??????param="${2-}"
        ??????shift
        ??????;;
        ????-?*)?die?"Unknown?option:?$1"?;;
        ????*)?break?;;
        ????esac
        ????shift
        ??done

        ??args=("$@")

        ??#?check?required?params?and?arguments
        ??[[?-z?"${param-}"?]]?&&?die?"Missing?required?parameter:?param"
        ??[[?${#args[@]}?-eq?0?]]?&&?die?"Missing?script?arguments"

        ??return?0
        }

        parse_params?"$@"
        setup_colors

        #?script?logic?here

        msg?"${RED}Read?parameters:${NOFORMAT}"
        msg?"-?flag:?${flag}"
        msg?"-?param:?${param}"
        msg?"-?arguments:?${args[*]-}"

        Choose Bash

        #!/usr/bin/env?bash

        腳本為了獲得最佳兼容性,它引用/usr/bin/env,而不是直接引用/bin/bash。

        Fail fast

        set?-Eeuo?pipefail

        set命令可以更改腳本執(zhí)行選項。例如,通常Bash不關(guān)心某個命令是否失敗,返回非零退出狀態(tài)代碼。它只是快速地跳到下一個?,F(xiàn)在考慮一下這個小腳本:

        #!/usr/bin/env?bash
        cp?important_file?./backups/
        rm?important_file

        如果備份目錄不存在,會發(fā)生什么情況?確切地說,你將在控制臺中收到一條錯誤消息,但是在你能夠做出反應(yīng)之前,該文件已經(jīng)被第二個命令刪除。

        Get the location

        script_dir=$(cd?"$(dirname?"${BASH_SOURCE[0]}")"?&>/dev/null?&&?pwd?-P)

        這行代碼盡其所能定義腳本的位置目錄,然后我們對其進行cd配置。為什么?

        通常,我們的腳本在相對于腳本位置的路徑上運行,復(fù)制文件并執(zhí)行命令,假設(shè)腳本目錄也是一個工作目錄。是的,只要我們從它的目錄執(zhí)行腳本。

        但是,假設(shè)我們的CI配置執(zhí)行腳本如下所示呢:

        /opt/ci/project/script.sh

        那么我們的腳本不是在項目目錄中操作的,而是在CI工具的一些完全不同的工作目錄中操作的。我們可以通過在執(zhí)行腳本之前轉(zhuǎn)到目錄來修復(fù)它:

        cd?/opt/ci/project?&&?./script.sh

        但從腳本的角度解決這個問題要好得多。因此,如果腳本從同一目錄中讀取某個文件或執(zhí)行另一個程序,請按如下方式調(diào)用:

        cat?"$script_dir/my_file"

        同時,腳本不會更改工作目錄的位置。如果腳本是從其他目錄執(zhí)行的,并且用戶提供了指向某個文件的相對路徑,我們?nèi)匀豢梢宰x取它。

        Try to clean up

        trap?cleanup?SIGINT?SIGTERM?ERR?EXIT

        cleanup()?{
        ??trap?-?SIGINT?SIGTERM?ERR?EXIT
        ??#?script?cleanup?here
        }

        在腳本結(jié)束時,將執(zhí)行cleanup()函數(shù)。你可以在這里嘗試刪除腳本創(chuàng)建的所有臨時文件。

        請記住,cleanup()不僅可以在最后調(diào)用,在任何時候都可以。

        Display helpful help

        usage()?{
        ??cat?<Usage:?$(basename?"${BASH_SOURCE[0]}")?[-h]?[-v]?[-f]?-p?param_value?arg1?[arg2...]

        Script?description?here.

        ...
        EOF
        ??exit
        }

        盡量讓usage()函數(shù)相對靠近腳本的頂部,有兩種作用:

        • 要為不知道所有選項并且不想查看整個腳本來發(fā)現(xiàn)這些選項的人顯示幫助。

        • 當有人修改腳本時,保存一個最小的文檔(因為兩周后,你甚至不記得當初是怎么寫的)。

        我不主張在這里記錄每個函數(shù)。但是一個簡短、漂亮的腳本使用這些消息是必需的。

        Print nice messages

        setup_colors()?{
        ??if?[[?-t?2?]]?&&?[[?-z?"${NO_COLOR-}"?]]?&&?[[?"${TERM-}"?!=?"dumb"?]];?then
        ????NOFORMAT='\033[0m'?RED='\033[0;31m'?GREEN='\033[0;32m'?ORANGE='\033[0;33m'?BLUE='\033[0;34m'?PURPLE='\033[0;35m'?CYAN='\033[0;36m'?YELLOW='\033[1;33m'
        ??else
        ????NOFORMAT=''?RED=''?GREEN=''?ORANGE=''?BLUE=''?PURPLE=''?CYAN=''?YELLOW=''
        ??fi
        }

        msg()?{
        ??echo?>&2?-e?"${1-}"
        }

        首先,如果你還不想在文本中使用顏色,那么先刪除setup_colors()函數(shù)。我保留它是因為我知道如果我不必每次都用谷歌編碼的話,我會更頻繁地使用顏色。

        其次,這些顏色只用于msg()函數(shù),而不是echo命令。

        msg()函數(shù)用于打印不是腳本輸出的所有內(nèi)容。這包括所有日志和消息,而不僅僅是錯誤。引用?12 Factor CLI Apps的文章說法:

        In short: stdout is for output, stderr is for messaging.

        — Jeff Dickey, who knows a little about building CLI apps

        stdout用于輸出,stderr用于消息傳遞。

        這就是為什么在大多數(shù)情況下你不應(yīng)該為stdout使用顏色。

        用msg()打印的消息被發(fā)送到stderr流并支持特殊的序列,比如顏色。如果stderr輸出不是交互式終端,或者傳遞了一個標準參數(shù),那么顏色將被禁用。用法如下:

        msg?"This?is?a?${RED}very?important${NOFORMAT}?message,?but?not?a?script?output?value!"

        要檢查stderr是不是交互式終端時的行為,請在腳本中添加類似于上面的一行。然后執(zhí)行它,將stderr重定向到stdout并通過管道將其發(fā)送到cat。管道操作使輸出不再直接發(fā)送到終端,而是發(fā)送到下一個命令,因此顏色會被禁用。

        $?./test.sh?2>&1?|?cat
        This?is?a?very?important?message,?but?not?a?script?output?value!

        Parse any parameters

        parse_params()?{
        ??#?default?values?of?variables?set?from?params
        ??flag=0
        ??param=''

        ??while?:;?do
        ????case?"${1-}"?in
        ????-h?|?--help)?usage?;;
        ????-v?|?--verbose)?set?-x?;;
        ????--no-color)?NO_COLOR=1?;;
        ????-f?|?--flag)?flag=1?;;?#?example?flag
        ????-p?|?--param)?#?example?named?parameter
        ??????param="${2-}"
        ??????shift
        ??????;;
        ????-?*)?die?"Unknown?option:?$1"?;;
        ????*)?break?;;
        ????esac
        ????shift
        ??done

        ??args=("$@")

        ??#?check?required?params?and?arguments
        ??[[?-z?"${param-}"?]]?&&?die?"Missing?required?parameter:?param"
        ??[[?${#args[@]}?-eq?0?]]?&&?die?"Missing?script?arguments"

        ??return?0
        }

        如果在腳本中參數(shù)化有意義的話,我就通常就會去做,即使整個腳本只在一個地方使用。它使復(fù)制和重用它變得更容易,而這通常是早晚發(fā)生的。而且,即使某些東西需要硬編碼,通常在比Bash腳本更高的級別上有更好的位置。

        CLI參數(shù)有三種主要類型:標志、命名參數(shù)和位置參數(shù)。parse_params()函數(shù)支持所有這些參數(shù)。

        這里沒有處理的唯一一個公共參數(shù)模式是連接多個單字母標志。為了能夠傳遞兩個標志作為-ab,而不是-a-b,需要一些額外的代碼。

        while循環(huán)是一種手動解析參數(shù)的方法。在其他語言中,您應(yīng)該使用一個內(nèi)置的解析器或可用的庫,但是,好吧,這是Bash。

        模板中有一個示例標志(-f)和命名參數(shù)(-p)。只需更改或復(fù)制它們以添加其他參數(shù)。之后不要忘記更新usage()。

        這里最重要的一點是,當您使用第一個google結(jié)果進行Bash參數(shù)解析時,通常會丟失一個未知選項的錯誤。腳本收到未知選項的事實意味著用戶希望它執(zhí)行腳本無法完成的操作。所以用戶的期望和腳本行為可能會有很大的不同。最好是在壞事發(fā)生之前完全阻止處決。

        在Bash中解析參數(shù)有兩種選擇。是一個接一個的。有人贊成和反對使用它們。我發(fā)現(xiàn)這些工具不是最好的,因為默認情況下,macOS上的getopt行為完全不同,getopts不支持長參數(shù)(比如--help)。

        Using the template

        復(fù)制粘貼它,就像你在網(wǎng)上找到的大多數(shù)代碼一樣。

        復(fù)制后,只需更改4件事:

        • 包含腳本說明的usage()文本

        • cleanup()內(nèi)容

        • parse_params()中的參數(shù)–保留--help和--no color,但替換示例:-f和-p

        • 實際的腳本邏輯

        Portability

        我在MacOS上測試了這個模板(使用默認的bash3.2)和幾個Docker映像:Debian、Ubuntu、CentOS、amazonlinux、Fedora。它的確起作用了。

        顯然,它不能在缺少Bash的環(huán)境中工作,比如alpinellinux。

        Further reading

        在用Bash或其他更好的語言創(chuàng)建CLI腳本時,有一些通用規(guī)則。這些資源將指導(dǎo)您如何使小型腳本和大型CLI應(yīng)用程序可靠,參考如下:

        • Command Line Interface Guidelines(https://clig.dev/)
        • 12 Factor CLI Apps(https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46)
        • Command line arguments anatomy explained with examples(https://betterdev.blog/command-line-arguments-anatomy-explained/)

        Closing notes

        我不會是第一個也不是最后一個創(chuàng)建Bash腳本模板的人。這個項目是一個很好的選擇,雖然對我的日常需求來說有點太大了。畢竟,我盡量使Bash腳本盡可能?。ǘ液苌偈褂茫?/p>

        編寫B(tài)ash腳本時,請使用支持ShellCheck linter的IDE,如JetBrains IDEs。它會阻止你做一堆適得其反的事情。

        本文翻譯自:https://betterdev.blog/minimal-safe-bash-script-template/

        巨星隕落!2007年圖靈獎得主Edmund Clarke因感染新冠離世...

        2020-12-25

        微信突然更新,新增了這些功能...

        2020-12-25

        Eclipse 官宣,干掉 VS Code !

        2020-12-25

        2020年Spring Cloud最后一個大版本發(fā)布!

        2020-12-24

        支持Dubbo接口文檔生成的工具!

        2020-12-24

        36 張圖梳理 Intellij IDEA 常用設(shè)置

        2020-12-23



        掃一掃,關(guān)注我

        知曉前沿科技,領(lǐng)略技術(shù)魅力

        DD自研的滬牌代拍業(yè)務(wù)

        深度交流


        瀏覽 30
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            www.淫荡 | 一级片毛 | 国产美女免费视频 | 91精品国产综合久久久久久漫画 | 国产91精品看黄网站在线观看 | 人妻人人摸 | 美女被 到喷水www69XX | 性生活一级故事片 | 日逼操 | 国产又爽又黄免费视频 |