我正在閱讀的書 - Learning the Bash Shell by O'Reilly 指定了一些程式碼如下:
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
howmany=$1
shift
....
....
etc
這使用
grep
搜尋實用程式來測試是否$1
符合適當的模式。為此,我們^-[0-9][0-9]*$
向 grep 提供正規表示式,該表達式被解釋為「一個初始破折號後面跟著一個數字,可以選擇後面跟著一個或多個數字」。如果找到匹配,grep
則將返回匹配,並且測試將為 true,否則grep
將不返回任何內容,並且處理將傳遞給elif
測試。請注意,我們已將正規表示式括在單引號中,以阻止 shell 解釋 $ 和 *,並將它們不加修改地傳遞給 grep。
那麼,為什麼正規表示式不會'^-[0-9]'
像單引號那樣失去其含義,通常單引號內的所有內容都會失去其含義。
感謝您的幫助。
答案1
雖然其他人已經回答了您的具體問題,但我會補充一點
if [ -n "$(echo $1 | grep '^-[0-9][0-9]*$')" ]; then
檢查字串是否與正規表示式相符的錯誤方法有以下幾個原因:
- 不能用於
echo
任意數據 - 像上面將參數擴充保留為不含引號的
$1
是 split+glob 運算子。 grep
不將正規表示式與其完整輸入相匹配,而是在其輸入的每一行上進行匹配。例如,它會傳回 truefoo\n-0\nbar
。- 正規表示式可以匹配零長度,因此在一般情況下檢查是否
grep
產生一些輸出是錯誤的(請注意,命令替換會刪除尾隨換行符)。最好使用並依賴, 而不是 的grep -q
退出狀態,並且還可以避免命令替換。grep
[
- 請注意,該
grep
命令可以簡化為grep -xE '-[0-9]+'
bash
,ksh93
並且zsh
有一個專門的運算子用於(擴展)正則表達式匹配。為了在所有三個(以及 bash-3.1)中可移植且可靠地使用它,語法是:
re='^-[0-9]+$'
if [[ $1 =~ $re ]]; then
echo matches
fi
yash
並且zsh
還支援:
if [ "$1" '=~' '^-[0-9]+$' ]; then
echo matches
fi
進行字串(基本)正規表示式符合的標準指令是expr
:
if expr " $1" : ' -[0-9]\{1,\}$' > /dev/null; then
echo matches
fi
請注意,^
(但不是$
)隱含在 中expr
。我們也使用該前導空格字元來避免$1
恰巧是expr
運算符的值出現問題。
另請注意,如果正規表示式包含\(...\)
,它將影響 的行為expr
。
總而言之,最好使用awk
另一種標準/可移植的方法來實現這一點(注意awk
使用擴展的正規表示式):
if STRING=$1 RE='^-[0-9]+$' awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'; then
...
或使用一個函數:
re_match() {
STRING=$1 RE=$2 awk '
BEGIN{exit(ENVIRON["STRING"] !~ ENVIRON["RE"])}'
}
if re_match "$1" '^-[0-9]+$'
在這種情況下,您還可以使用標準case
構造來實現它:
case $1 in
("" | *[!0-9-]* | [!-]* | - | ?*-*) ;;
(*) echo match;;
esac
要使用grep
,您可以將其與--null
選項(如果受支持,因為這不是標準選項)一起使用,以告訴它處理 NUL 分隔記錄而不是換行分隔記錄。由於在大多數 shell 中,$1
不能包含 NUL,因此應該是安全的:
if printf %s "$1" | grep --null -xq '-[0-9]+$'; then
echo match
fi
答案2
答案3
單引號可以防止通配(讓bash
像這樣解釋通配符*
)和透過使用變數擴展$
。基本上你是說bash
「從字面上獲取這些字元並將它們傳遞給grep
」。當grep
看到它們時,它是為了理解正規表示式而建構的,所以然後正規表示式在內部解釋grep
。
較短版本:單引號參數提供了一種在參數傳遞給命令之前逃避 shell 處理的方法。
答案4
它確實失去了意義。grep
使用與 bash 幾乎相同的正規表示式模式。