如何让Ruby脚本实现类似Foreman的流式输出前置标识功能?
解决方案
串行版本脚本修改
要实现自动为输出添加当前处理项的标识前缀,核心是临时捕获标准输出(stdout),处理后再重新输出。修改后的脚本如下:
$stdin.each_line do |it| it.strip! # 保存原始标准输出对象 original_stdout = $stdout # 创建StringIO对象用于捕获输出 captured_output = StringIO.new $stdout = captured_output begin # 执行用户传入的代码块 eval(ARGV.first) ensure # 恢复原始标准输出,避免影响后续操作 $stdout = original_stdout # 遍历捕获的每一行输出,添加前缀后打印 captured_output.string.each_line do |line| puts "[#{it}] #{line.chomp}" end end end
并行版本脚本修改
基于Parallel库的版本,只需在每个并行处理块内复用相同的捕获逻辑(保证线程内的stdout隔离):
require 'parallel' Parallel.each($stdin.each_line.to_a) do |it| it.strip! original_stdout = $stdout captured_output = StringIO.new $stdout = captured_output begin eval(ARGV.first) ensure $stdout = original_stdout captured_output.string.each_line do |line| puts "[#{it}] #{line.chomp}" end end end
工作原理
- 捕获输出:通过将
$stdout临时替换为StringIO对象,拦截用户代码块(包括system命令、puts等)产生的所有输出。 - 处理输出:遍历捕获到的每一行内容,为其添加
[当前处理项]前缀后再打印。 - 安全恢复:使用
ensure块保证无论代码执行成功与否,都会恢复原始的$stdout,避免破坏后续输出逻辑。
使用示例
执行命令时无需手动添加puts it,直接传入业务逻辑即可:
dir | each "Dir.chdir(it) { system('bundle') }"
输出效果自动变为:
[code] Bundle complete! 41 Gemfile dependencies, 165 gems now installed. [code] Use `bundle info [gemname]` to see where a bundled gem is installed. [code-ruby] Bundle complete! 6 Gemfile dependencies, 44 gems now installed. [code-ruby] Use `bundle info [gemname]` to see where a bundled gem is installed.
内容的提问来源于stack exchange,提问作者Dorian
相关产品推荐
相关产品推荐

