使用Ruby的Open3 / Process.spawn()

时间:2018-07-25 21:12:24

标签: ruby ansi-escape

我正在使用sass-lint NPM软件包从Rake任务中对.scss文件进行样式检查,因此:

  sass_lint_cmd = "sass-lint --config #{ui_library_path}/scss/.sass-lint.yml '#{ui_library_path}/scss/*.scss' -v -q --max-warnings=0"
  output, status = Open3.capture2e(sass_lint_cmd)
  raise IOError, output unless status == 0

这基本上是可行的,只要发生任何林特警告或错误,Rake任务就会中止并将sass-lint输出(包括错误)转储到控制台。

但是,直接运行时,sass-lint会产生漂亮的彩色输出。由capture2e捕获时,颜色会丢失。

我认为问题是sass-lint(或节点)检测到它不在TTY中运行,因此输出纯文本。是否可以将某些Process.spawn()选项传递给Open3.capture2e()或其他方法,使我认为它正在TTY中运行?

(注意:我确实看过Trick an application into thinking its stdout is a terminal, not a pipe,但是macOS随附的BSD版本script似乎不支持--return-c选项,并且我在macOS上运行。)


更新:我按照Piccolo's answer尝试了script -q /dev/nullPTY.spawn(),但是没有运气。

script -q /dev/null …可从命令行运行,但不能在Open3.capture2e()中运行(它运行,但会产生单色输出和虚假的Bundler::GemNotFound堆栈跟踪)。

对于PTY.spawn(),将上面的代码替换为以下代码:

r, _w, pid = PTY.spawn(scss_lint_command)
_, proc_status = Process.wait2(pid)
output, status = [r, proc_status.exitstatus]
(warn(output); raise) unless status == 0

子进程似乎从未完成;如果我ps在另一个终端中,则显示为处于可中断的睡眠状态。杀死子进程并不能释放父进程。

块形式也是如此。

output, status = nil
PTY.spawn(scss_lint_command) do |r, _w, pid|
  _, proc_status = Process.wait2(pid)
  output, status = [r, proc_status.exitstatus]
end
(warn(output); raise) unless status == 0

1 个答案:

答案 0 :(得分:1)

您是否考虑过使用Ruby出色的pty库而不是Open3

每个the thread you linked的伪终端似乎都模拟了实际的TTY,因此脚本除非知道$TERM之类的脚本,否则不会知道它不在终端中,但是这也可以相对容易被欺骗。

根据this flowchart,使用pty而不是Open3的缺点是STDERR不会获得自己的流。


或者,根据this answer,也从您链接的线程来看,script -q /dev/null $COMMAND在Mac OS X上似乎可以解决问题。

在Mac上,ls -Gls的输出着色,作为简短测试,我将ls -G传递给cat如下:

script -q /dev/null ls -G | cat

它以彩色显示,而只需运行

ls -G | cat

没有。

此方法在irb中也有效,再次使用ls -G

$ touch regular_file
$ touch executable_file
$ mkdir directory
$ chmod +x executable_file
$ irb
2.4.1 :001 > require 'Open3'
 => true
2.4.1 :002 > output, status = Open3.capture2e("ls -G")
 => ["directory\nexecutable_file\nregular_file\n", #<Process::Status: pid 39299 exit 0>]
2.4.1 :003 > output, status = Open3.capture2e("script -q /dev/null ls -G")
 => ["^D\b\b\e[1m\e[36mdirectory\e[39;49m\e[0m       \e[31mexecutable_file\e[39;49m\e[0m regular_file\r\n", #<Process::Status: pid 39301 exit 0>]
2.4.1 :004 >