我从 bash shell 运行了以下命令:
echo 'hello world' | ruby -ne 'puts $_ if /hello/'
一开始以为是打错了,没想到竟然输出了hello world
我想输入:
echo 'hello world' | ruby -ne 'puts $_ if /hello/ === $_'
谁能给出解释或指向文档,说明为什么我们得到与 $_
的这种隐式比较?
我还要注意:
echo 'hello world' | ruby -ne 'puts $_ if /test/'
不会输出任何东西。
最佳答案
Ruby 解析器在条件句中有一个正则表达式文字 的特殊情况。通常(即不使用 e
、n
或 p
命令行选项)此代码:
if /foo/
puts "TRUE!"
end
产生:
$ ruby regex-in-conditional1.rb
regex-in-conditional1.rb:1: warning: regex literal in condition
首先将与正则表达式匹配的内容分配给 $_
,如下所示:
$_ = 'foo'
if /foo/
puts "TRUE!"
end
产生:
$ ruby regex-in-conditional2.rb
regex-in-conditional2.rb:2: warning: regex literal in condition
TRUE!
这是 Ruby 条件规则的一个(记录不详的)异常(exception),其中任何不是 false
或 nil
的东西都被评估为 truthy。
这仅适用于正则表达式文字,以下行为符合您对条件的预期:
regex = /foo/
if regex
puts "TRUE!"
end
输出:
$ ruby regex-in-conditional3.rb
TRUE!
这是在解析器中处理的。在 MRI 代码中搜索警告文本会生成单个 match in parse.y
。 :
case NODE_DREGX:
case NODE_DREGX_ONCE:
warning_unless_e_option(parser, node, "regex literal in condition");
return NEW_MATCH2(node, NEW_GVAR(rb_intern("$_")));
我不了解 Bison,所以我无法准确解释这里发生了什么,但是您可以推断出一些线索。 warning_unless_e_option
如果设置了 -e
选项,函数只是抑制警告,因为在普通代码中不鼓励使用此功能,但在命令行的表达式中可能很有用(这解释了为什么你看不到警告在你的代码中)。下一行似乎是在构建一个解析子树,它是正则表达式和 $_
全局变量之间的正则表达式匹配,其中包含“[t]he last input line of string by gets or readline”。 ”。然后这些节点将被编译成通常的正则表达式方法调用。
这显示了正在发生的事情,我将以 Kernel#gets
documentation 中的引述作为结束。这可以解释为什么这是一个如此晦涩的功能
The style of programming using $_ as an implicit parameter is gradually losing favor in the Ruby community.
关于Ruby 命令行隐式条件检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30631728/