我正在阅读的书 - 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 几乎相同的正则表达式模式。