Ruby 重定向stdout时,将/dev/stdout作为文件写入失败,错误为Errno::EACCES

Ruby 重定向stdout时,将/dev/stdout作为文件写入失败,错误为Errno::EACCES,ruby,io,stdout,Ruby,Io,Stdout,我有一个我正在使用的遗留gem,给定一个文件名,知道如何写入文件,但不知道如何写入标准输出或任何其他形式的IO。为了使它更灵活,我试图通过传递/dev/stdout作为文件名来欺骗它写入stdout。如果我在终端中运行调用gem的代码,并且不重定向输出,这是可行的。但是,如果我随后尝试将输出重定向到其他文件或设备,它将失败,权限被拒绝@rb_sysopen-/dev/stdout(Errno::EACCES) 在对gem的源代码进行了一些实验和挖掘之后,我想出了以下方法来重现这个问题。给定这个R

我有一个我正在使用的遗留gem,给定一个文件名,知道如何写入文件,但不知道如何写入标准输出或任何其他形式的IO。为了使它更灵活,我试图通过传递
/dev/stdout
作为文件名来欺骗它写入stdout。如果我在终端中运行调用gem的代码,并且不重定向输出,这是可行的。但是,如果我随后尝试将输出重定向到其他文件或设备,它将失败,权限被拒绝@rb_sysopen-/dev/stdout(Errno::EACCES)

在对gem的源代码进行了一些实验和挖掘之后,我想出了以下方法来重现这个问题。给定这个Ruby脚本
stdout.rb

#/usr/bin/env ruby
open('/dev/stdout',w+')do | f|
f、 放置(‘你好,世界’)
结束
(在本例中,我打开文件
w+
,因为这就是遗留gem的功能,但它的失败方式与
a+
相同)

我可以在终端中运行脚本,它可以工作:

$。/stdout.rb
你好,世界
但是,如果我将其重定向到一个文件,将其传输到另一个命令(例如
grep
),或者尝试使用类似
echo$(./stdout.rb)
的内容捕获输出,则会失败:

$。/stdout.rb>/tmp/hello.txt
回溯(最近一次呼叫最后一次):
2:from./stdout.rb:3:in`'
1:from./stdout.rb:3:in'open'
./stdout.rb:3:in'initialize':权限被拒绝@rb\u sysopen-/dev/stdout(Errno::EACCES)
在调用
File.open()
时,它似乎失败了。但是,重定向/捕获案例中的
/dev/stdout
权限与它刚刚写入终端时有什么不同

(FWIW,这是在macOS Catalina 10.15.5,Darwin 19.5.0上。)



更新:如果我将模式从
w+
更改为
w
,它会工作。我从中了解到“+”表示“更新”,即读和写。我不清楚为什么遗留gem认为它需要这样做,但更重要的是,为什么这在TTY中是可能的,而在重定向时是不可能的?

我认为答案是Ruby
IO::new

当原始IO的打开模式为只读时,该模式不能更改为可写。同样,开放模式不能从只写更改为可读。 尝试进行此类更改时,会根据平台在不同位置引发错误

w
打开
IO
对象意味着:

“w”仅写,截断现有文件 设置为零长度或创建新文件以进行写入

w+
打开
IO
对象意味着:

“w+”读写,将现有文件截断为零长度 或者创建一个新文件进行读写

问题似乎是您无法使用
w+
打开
stdout
,因为权限是针对
只写
。 问题是为什么只有在使用输出重定向时才会出现错误

答案可能在我引用的前一句话中:

尝试进行此类更改时,会根据平台在不同位置引发错误

我建议在“实验”中使用
StringIO
对象,而不是
/dev/stdout

评论后添加

我现在意识到你需要传递一个字符串

在本例中,我将尝试使用'/dev/tty',我认为它可以工作,因为它还没有在只有写权限的情况下打开


问题是,然后您应该在GUI中使用适当的
tty
而不是模拟终端来查看结果,或者您应该找到一种方法将
tty
输出传输到另一个
IO
。更重要的是,只有Unix像操作系统

我认为答案是Ruby
IO::new

当原始IO的打开模式为只读时,该模式不能更改为可写。同样,开放模式不能从只写更改为可读。 尝试进行此类更改时,会根据平台在不同位置引发错误

w
打开
IO
对象意味着:

“w”仅写,截断现有文件 设置为零长度或创建新文件以进行写入

w+
打开
IO
对象意味着:

“w+”读写,将现有文件截断为零长度 或者创建一个新文件进行读写

问题似乎是您无法使用
w+
打开
stdout
,因为权限是针对
只写
。 问题是为什么只有在使用输出重定向时才会出现错误

答案可能在我引用的前一句话中:

尝试进行此类更改时,会根据平台在不同位置引发错误

我建议在“实验”中使用
StringIO
对象,而不是
/dev/stdout

评论后添加

我现在意识到你需要传递一个字符串

在本例中,我将尝试使用'/dev/tty',我认为它可以工作,因为它还没有在只有写权限的情况下打开


问题是,然后您应该在GUI中使用适当的
tty
而不是模拟终端来查看结果,或者您应该找到一种方法将
tty
输出传输到另一个
IO
。更重要的是,如果API使用IO对象,那么我将使用StringIO。我可能没有办法,只能使用monkey patch方法调用
File.open()
@David Moles好的,我现在意识到你需要以某种方式传递字符串,我根据你的评论更新了我的答案。相信我,如果API采用IO对象,我会使用StringIO。我可能没有办法,只能使用monkey patch调用的方法