我正在使用Ruby。我想弄清楚为什么捆绑工具rake release
会在git push
步骤上挂起,同时也会discussed inconclusively here。
我已将其缩小到这行代码:
`git push 2>&1`
我可以通过在IRB中运行相同的代码行来重现该问题。
底层git push
确实执行了什么是神秘的,但由于某种原因,Ruby从未收到返回状态。它只是无限期地等待子进程。
检查进程列表显示子进程有Z+
(僵尸?)状态:
UID PID PPID C STIME TTY TIME CMD USER PGID SESS JOBC STAT TT
501 23397 3757 0 1:44PM ttys001 0:00.54 irb mbrictson 23397 0 1 S+ s001
501 26035 23397 0 2:06PM ttys001 0:00.00 (sh) mbrictson 23397 0 1 Z+ s001
显然,git push
只能在我的shell中找到。只是当它通过Ruby使用它挂起的反引号来调用时。
此外,这很好用:
system("git push 2>&1") # => true
这个(即没有输出重定向)也能正常工作!
`git push` # => "Everything up-to-date"
部分问题显然是ControlMaster auto
~/.ssh/config
。执行git push
时,会导致在后台生成新的控件连接过程。或许%x(git push 2>&1)
等待此后台进程退出?如果我在SSH配置中禁用ControlMaster,这实际上解决了这个问题。
但是,这让我很困扰。我宁愿不必禁用ControlMaster只是为了让Ruby的反引号操作符满意。
任何人都可以解释:
%x()
会挂起但system()
不挂?2>&1
会有所作为?这是在Mac OS X Yosemite上使用Ruby 2.2.0。
答案 0 :(得分:3)
想出来:
为什么%x()挂起但系统()不挂?
%x()等待完全读取子进程的输出; system()不关心输出。
According to this bug report,OpenSSH中的ControlPersist
设置会导致stderr在主连接的生命周期内保持打开状态。在我的SSH配置中,我有ControlPersist 5m
,果然,%x()在最终完成之前会持续5分钟。
这不会影响system()
,因为系统不会等待输出。
为什么删除2>& 1会有所作为?
如上所述,SSH主连接使stderr保持打开状态。它显然关闭了stdout。由于stdout已关闭,%x(git push)
立即结束,因为没有什么可以在stdout上等待。当2>&1
添加到命令时,这会导致stderr重定向到stdout。由于stderr被主连接保持打开,这反过来导致stdout保持打开状态。 %x
等待stdout并挂起。
不幸的是,OpenSSH的这种行为没有显示出被改变的迹象,因此除了禁用ControlPersist
之外,没有令人满意的解决方案。