将每个bash输出都导向文件

3

有没有一种方法可以将每个输出都导入到文件中,就像监视终端以记录打印的所有内容一样?

此外,当我使用tee时,它似乎会停止实际程序的输出。 我该怎么改变这个? 我希望保持正常的输出,并只使用其副本保存在文件中。

仅作为示例,我有这个ruby脚本:

100.times do |i|
  puts i
end

运行$ruby script.rb | tee output.txt命令可以很好地保存输出,但是它只会在整个过程运行完后才被打印出来。


2
它只在整个过程运行后才打印的原因是tee在内部进行了缓冲。如果您只是正常运行它,标准输出流将在每行之后刷新,但是tee具有内部缓冲区,并且仅在该缓冲区足够大时才刷新到stdout。如果您改成运行100000.times do |i|,您会发现它会在运行完成之前打印。供您参考。 - scott_fakename
2
man script 可以帮助你。祝你好运。 - shellter
“脚本”似乎是解决方案。我会研究一下。谢谢 :) - Fábio Queluci
1个回答

3

使用Bash进行操作

如果您正在使用bash(根据您的标签,您确实在使用),您可以运行script -a -c "ruby your_script.rb" -f your_file.out

例如,如果您编写了以下脚本,并将其保存为test.rb

# test.rb
i = 0
while true do
    i += 1
    puts i
    sleep 1
end

然后运行script -a -c "ruby test.rb" -f test.out,您可以每次刷新文件并查看它每秒递增一个,即使脚本仍在运行。这是因为它正在“刷新”每个输出。

缺点:您只能在bash中使用script命令来执行此操作。在这方面,您会创建一个依赖项;如果没有script,或者它不像我的机器上的script那样工作,则可能根本无法使用此方法。始终要谨慎处理依赖项。

在Ruby中实现

有时候,您可能不会在bash中运行脚本,也可能没有可用的script。那么怎么办?好吧,您只需创建一个打印到文件再打印到stdout的方法,而不考虑操作系统。然后它将每次刷新文件以确保其最新。

# print_to_file.rb
class PrintOutputToFile
  def self.file
    @@file ||= File.open("my_output.log", "w")
  end

  def self.puts! some_data
    file.write "#{some_data}\n"
    file.flush
    puts some_data
  end
end

i = 0
while true do
  i += 1
  PrintOutputToFile.puts! i
  sleep 1
end
缺点:每次运行该脚本都会清除文件。如果您想保存长期日志以覆盖多个运行,则可能需要修改此小脚本。此外,如果没有文件权限,则此脚本将直接崩溃。这不是用于生产的,而仅仅是一个概念验证,供那些有兴趣实现此类系统的人使用。

Ruby本身的另一种方法

您还可以通过将旧的puts方法复制到proc中,然后覆盖核心puts方法以写入日志文件,但最终要调用原始的puts方法来完成。

@@old_puts = method(:puts).to_proc
def puts(output)
  @@file ||= File.open("your_log.txt", "w")
  @@file.write "#{output}\n"
  @@old_puts.call output
  @@file.flush
end
缺点: 这种方法已经超越了“hacky”的范畴,我坦率地说,除非你真的非常了解它,并且绝对必须这样做,否则我不建议实际操作。

嗨,感谢您抽出时间来帮助我。 script可以实现我的需求,但是我似乎无法从.sh脚本内部启动它(以便自动运行)。当我运行以下命令时,一直提示“找不到命令”: #!/bin/bash $(script -f log.txt) - Fábio Queluci
当然可以。我刚刚编辑了帖子以正确着色文本。这解释了如何令您满意地解决问题吗? - Luke
你是否使用了chmod u+x whatever.sh并确保在你的shell脚本中使用了脚本格式script -a -c "ruby whatever.rb" -f log.txt - Luke
1
我提出了第三种解决这个问题的方法。希望它能够达到你的目的。 - Luke
1
嗨,卢克。你的第一种方法正是我所需要的,但我做了一些改进,也许会对你有用:我将调用script的命令放在了.bashrc文件中。这样做本身会创建一个无限循环,因为script的工作方式是如此,所以你需要确保只调用它一次。使用$SHLVL == 1的简单if语句就可以了。现在我可以自动记录发生在Shell上的所有事情。非常感谢你的帮助。 - Fábio Queluci
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接