echo abc|ruby -p 链式调用sub替换为何未得到预期ABc结果
Ruby -p 模式下链式调用sub替换失效原因
要搞懂这个现象,只需要先明确两个核心规则:
- Ruby
-p参数的固定执行逻辑:它会自动把你传入的-e代码包裹在如下循环结构里运行:
while gets # 你写在-e后面的代码会被插入到这里 print $_ end
也就是说不管你写的代码最后返回什么值,-p最终输出的永远是全局变量$_的内容,$_存储的就是每次从标准输入读入的行内容(这个例子里就是echo传入的abc加末尾换行符)。
- sub方法的两种调用行为差异:
- 顶层作用域不指定接收者,直接写
sub(匹配规则, 替换值)时,调用的是Kernel#sub方法:它会对$_做替换,把替换后的新字符串重新赋值给$_,再返回这个新字符串。 - 明确指定字符串作为接收者调用
sub(比如"xxx".sub(...))时,调用的是String#sub方法:它只会返回替换后的新字符串,完全不会修改全局变量$_。
- 顶层作用域不指定接收者,直接写
我们再对应三个场景逐一验证逻辑:
直接执行
'abc'.sub('a','A').sub('b', 'B')
两次sub都是对字符串对象调用的String#sub,第一次把'abc'替换a为A得到"Abc",第二次在"Abc"基础上替换b为B得到"ABc",最后p打印这个最终返回值,结果就是"ABc",符合预期。执行
echo abc | ruby -p -e "sub('a','A');sub('b', 'B')"
分号分隔的两个sub都是无接收者调用,走的都是Kernel#sub。第一个sub执行完,$_被修改为"Abc";第二个sub基于已经更新的$_替换b为B,$_变成"ABc",最后-p打印$_,结果就是ABc,符合预期。执行
echo abc | ruby -p -e "sub('a','A').sub('b', 'B')"- 第一个sub是无接收者调用,走
Kernel#sub:把$_从原始的"abc"替换为"Abc",赋值回$_,同时返回"Abc"这个字符串对象。 - 第二个
.sub('b','B')是对第一个sub返回的"Abc"字符串调用方法,走的是String#sub:它确实把b替换成了B得到"ABc",但这个结果只是整个表达式的临时返回值,既没有赋值回$_,也不会被-p参数捕获输出。 - 最终-p打印的
$_还是停留在第一个sub执行完的状态"Abc",所以输出就是Abc,看起来第二个替换没有生效。
- 第一个sub是无接收者调用,走
如果要让链式调用也得到预期结果,只需要把链式调用的结果重新赋值给$_即可:
echo abc | ruby -p -e "$_ = sub('a','A').sub('b','B')"
上述命令就会正常输出ABc。
内容的提问来源于stack exchange,提问作者puravidaso
相关产品推荐
相关产品推荐

