POSIX 和可移植性 |外殼腳本 |grep -s,grep -q
我全力支持 shell 腳本的可移植性。
但我不確定我現在是否做得過火。
在此範例中,我們有一個名為 的函式
confirmation
,它接受第一個參數作為包含問題的字元串,並且所有其他參數都是可能的有效答案:confirmation () { question=$1; shift; correct_answers=$* printf '%b' "$question\\nPlease answer [ $( printf '%s' "$correct_answers" | tr ' ' / ) ] to confirm (Not <Enter>): "; read -r user_answer # this part iterates through the list of correct answers # and compares each as the whole word (actually as the whole line) with the user answer for single_correct_answer in $correct_answers; do printf '%s' "$single_correct_answer" | grep -i -x "$user_answer" > /dev/null 2>&1 && return 0 done return 1 } confirmation 'Do you hate me?' yes yeah kinda
正如你所看到的,核心部分是利用
grep
,所以我查看了手冊頁,發現了這個:-q, --quiet, --silent Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. Also see the -s or --no-messages option. (-q is specified by POSIX .) -s, --no-messages Suppress error messages about nonexistent or unreadable files. Portability note: unlike GNU grep, 7th Edition Unix grep did not conform to POSIX , because it lacked -q and its -s option behaved like GNU grep's -q option. USG -style grep also lacked -q but its -s option behaved like GNU grep. Portable shell scripts should avoid both -q and -s and should redirect standard and error output to /dev/null instead. (-s is specified by POSIX .)
讓我們強調這部分:
可移植性說明:與 GNU 不同
grep
,第 7 版 Unixgrep
不符合POSIX,因為它缺少-q
並且其-s
選項的行為類似於 GNUgrep
的-q
選項。USG 風格grep
也沒有-q
,但它的-s
選項表現得像 GNUgrep
。可移植的 shell 腳本應該避免兩者-q
,-s
並且應該將標準和錯誤輸出重定向到/dev/null
。我是不是已經做得過火了,還是重定向到
/dev/null
唯一的可移植方式?我不關心四十年前作業系統版本的可移植性!
Unix V7 於 70 年代後期發布。那是引入 Bourne shell 的版本。
但是,當時還沒有添加功能支持,
read
沒有-r
,也沒有printf
命令。不區分大小寫grep
與grep -y
. 當然$(...)
不是伯恩。從那時起,類 Unix 系統發生了相當大的發展並出現了分歧。在 90 年代初期,POSIX 確實試圖恢復一些統一。
然而,仍然有一些系統在其預設實現中沒有遵循 POSIX,只是將符合 POSIX 的實現添加為單獨的實用程序。
例如,
/bin/grep
Solaris 更接近 V7grep
而不是 POSIXgrep
。Solaris 上的 POSIXgrep
在/usr/xpg4/bin/grep
(在 Solaris 的最小部署中不可用)。
/bin/grep
在 Solaris 上沒有-q
,-E
,-F
.在 Solaris 上工作時,您通常希望放在
/usr/xpg4/bin
前面$PATH
並使用not(儘管在 Solaris 11/usr/xpg4/bin/sh
中/bin/sh
改變了,其中 /bin/sh 現在是 ksh93,因此平均而言比非常錯誤的基於 ksh88 更符合 POSIX/usr/xpg4/bin/sh
)。對您的程式碼的其他一些可移植性評論:
- 的行為
correct_answers=$*
或read -r
取決於 的目前值$IFS
。($*
將位置參數與 的第一個字元連接$IFS
,read
用於$IFS
將輸入拆分為單詞),因此您需要將其設置為所需的值。通過將位置參數加入標量字元串,這意味著如果其中任何一個包含分隔符,它將無法正常工作,除非您使用 NL 作為分隔符,因為無論如何
read
只會讀取一行,因此答案不能包含換行符。
- 您在
%b
-formatted 參數中包含了可能不打算\x
擴展序列的內容。for single_correct_answer in $correct_answers
使用拆分+全域。我不認為你想要這裡的 glob 部分,並且再次進行連接以稍後再次拆分它有點愚蠢(不可靠)。grep -i -x "$user_answer"
進行正則表達式模式匹配,而不是不區分大小寫的比較。此外,如果答案以 開頭,它將無法正常工作-
,grep
然後將其作為一個選項。printf '%s' text
產生非文本輸出(缺少換行符),因此grep
on 它的行為是未指定的(實際上是不可移植的)。因此,考慮到這些,我會將程式碼更改為:
confirmation() { question=$1; shift printf '%s\nPlease answer [' "$question" sep= for answer do printf %s "$sep$answer" sep=/ done printf '] to confirm (Not <Enter>): ' IFS= read -r user_answer # this part iterates through the list of correct answers # and compares each as the whole word (actually as the whole line) # with the user answer for single_correct_answer do printf '%s\n' "$single_correct_answer" | grep -ixFqe "$user_answer" && return done return 1 }