grep 正則表達式用引號括起來和元字符加反斜槓轉義的測試html
實驗在 grep 命令中的表達式:不加引號,加單引號,加雙引號的區別,以及部分元字符前加與不加 `\’ 進行轉義的區別。實驗環境爲「實驗樓( http://www.shiyanlou.com/ )上的 CentOS 6 ,GNU grep 2.6.3。linux
一、測試不把 grep 的表達式加上引號:正則表達式
[root@d9a69d7b11ac test]#mkdir test; cd test; touch hello
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep hell*"
+ ls ./hello
+ grep hello
./hello
[root@d9a69d7b11ac test]# touch hello2
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep hell*"
+ ls ./hello ./hello2
+ grep hello hello2
[root@d9a69d7b11ac test]# echo "hello3" > hello2
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep hell*"
+ ls ./hello ./hello2
+ grep hello hello2
hello3
[root@d9a69d7b11ac test]#
shell
可見,若是 grep 的表達式不加引號,碰到通配符等就會由於 bash 的擴展功能,而先把表達式進行擴展,擴展的結果再送入表達式進行grep 的命令執行。express
二、bash
下面測試把 grep 的表達式加上單引號括起來的狀況:ide
[root@d9a69d7b11ac test]# rm hello2
rm: remove regular empty file `hello2'? y
[root@d9a69d7b11ac test]# ls
hello
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep 'hell*'"
+ grep 'hell*'
+ ls ./hello
./hello
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep 'hell*'"
+ ls ./hello
+ grep 'hell*'
./hello
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep 'hell*'"
+ grep 'hell*'
+ ls ./hello
./hello
[root@d9a69d7b11ac test]# touch hello2
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep 'hell*'"
+ ls ./hello ./hello2
+ grep 'hell*'
./hello
./hello2
[root@d9a69d7b11ac test]# echo "hello3" > hello2
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep 'hell*'"
+ ls ./hello ./hello2
+ grep 'hell*'
./hello
./hello2
[root@d9a69d7b11ac test]#
測試
可見,grep 表達式用單引號括起來就避免了 bash 的預先擴展。lua
可是,要注意,bash -x 的展開功能的展開順序不是固定的,如上,有時先展開 ls ./*,有時先展開 grep ‘hell*’(由於是加的單引號,因此展開後保持不變。)。可是,展開後的執行是先執行 ls ./* ,結果再傳給 grep ‘hell*’ 執行的。spa
三、
下面測試把 grep 的表達式加雙引號括起來的狀況:
[root@d9a69d7b11ac test]# rm hello2
rm: remove regular empty file `hello2'? y
[root@d9a69d7b11ac test]# ls
hello
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep "hell*""
+ ls ./hello
+ grep hello
./hello
[root@d9a69d7b11ac test]# bash -x -c 'ls ./* | grep "hell*"'
+ ls ./hello
+ grep 'hell*'
./hello
[root@d9a69d7b11ac test]# touch hello2
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep "hell*""
+ ls ./hello ./hello2
+ grep hello hello2
[root@d9a69d7b11ac test]# bash -x -c 'ls ./* | grep "hell*"'
+ ls ./hello ./hello2
+ grep 'hell*'
./hello
./hello2
[root@d9a69d7b11ac test]# ls ./* | grep "hell*"
./hello
./hello2
[root@d9a69d7b11ac test]# echo "hello3" > hello2
[root@d9a69d7b11ac test]# bash -x -c "ls ./* | grep "hell*""
+ grep hello hello2
+ ls ./hello ./hello2
hello3
[root@d9a69d7b11ac test]# bash -x -c 'ls ./* | grep "hell*"'
+ ls ./hello ./hello2
+ grep 'hell*'
./hello
./hello2
[root@d9a69d7b11ac test]# ls ./* | grep "hell*"
./hello
./hello2
[root@d9a69d7b11ac test]# bash -x -c 'a="he"; ls ./* | grep "${a}ll*"'
+ a=he
+ ls ./hello ./hello2
+ grep 'hell*'
./hello
./hello2
[root@d9a69d7b11ac test]# bash -x -c 'a="he"; ls ./* | grep '${a}ll*''
+ a=he
+ ls ./hello ./hello2
+ grep 'll*'
./hello
./hello2
./hello2
[root@d9a69d7b11ac test]# a=he; ls ./* | grep '${a}ll*'
[root@d9a69d7b11ac test]# a=he; ls ./* | grep "${a}ll*"
./hello
./hello2
[root@d9a69d7b11ac test]# bash -x -c 'a="he"; ls ./* | grep ${a}ll*'
+ a=he
+ ls ./hello ./hello2
+ grep hello hello2
hello3
[root@d9a69d7b11ac test]# a="he"; ls ./* | grep ${a}ll*
hello3
[root@d9a69d7b11ac test]#
能夠看出,grep 的表達式加上雙引號,能夠避免一部分 bash 擴展功能,如 ls ./* | grep 「hell*」 中 grep 表達式中的 *;可是不能避免變量擴展,如能夠擴展:a=」he」; ls ./* | grep 「${a}ll*」。
另外,在測試 bash -x -c ‘a=」he」; ls ./* | grep ‘${a}ll*」 和 a=he; ls ./* | grep ‘${a}ll*’ 時,咱們看見了不同的結果,具體緣由不明,有多是 bash -c 的功能對’${a}ll*’做了一些修改?
另外,雖然暫時看來在 grep 中的表達式用雙引號括起來彷佛能夠利用它擴展變量的功能把 grep 的正則表達式弄成變量,可是,這個功能有沒有其它的反作用呢?目前沒有查找到相關的文檔依據,因此保險的作法是:在 grep 的正則表達式中只用單引號括起來。這種作法也是 grep 的 info 文檔中的例子所採用的。
四、
實驗一下 grep 使用基本正則表達式時,部分元字符必須加上轉義 \ 的狀況:
在 info grep 的菜單:* Regluar Expressions:: ->Basic vs Extended:: 中寫道:
3.6 Basic vs Extended Regular Expressions=========================================
In basic regular expressions the meta-characters `?’, `+’, `{’, `|’,
`(’, and `)’ lose their special meaning; instead use the backslashed
versions `\?’, `\+’, `\{’, `\|’, `\(’, and `\)’.
因此,必須在 grep 的正則表達式中使用 \ 以使這些字符的做用生效。以下(繼續使用上面的測試環境):
[root@e16578371323 test]# ls
hello hello2
[root@e16578371323 test]# ls ./* | grep 'hell?'
[root@e16578371323 test]# ls ./* | grep 'hell\?'
./hello
./hello2
[root@e16578371323 test]# ls ./* | grep 'hel{2}'
[root@e16578371323 test]# ls ./* | grep 'hel\{2\}'
./hello
./hello2
[root@e16578371323 test]#
五、
結論:在使用 grep 時,正則表達式必定要用單引號括起來,不然可能由於 shell 執行環境的預先展開功能致使錯誤;在基本正則表達式(grep 默認爲基本正則表達式)中的元字符 `?’, `+’, `{’, `|’,`(’, `)’ 前面必定要加上 `\’ 進行轉義。另外,注意區分通配符`?’, `*’, `[]‘ 與正則表達式中相應字符的含義和用法。
歡迎交流探討,如有錯漏,敬請批評與斧正。謝謝。
本文來自:Linux教程網