在 ruby 中,如何重命名文本文件,保持相同的句柄并删除旧文件

In ruby, how to rename a text file, keep same handle and delete the old file

我正在使用常量 (FLOG) 作为句柄来写入我的日志。在给定的时间点,我必须使用一个临时日志,然后将该内容附加到常规日志,所有这些都具有相同的句柄,通过一系列方法使用。
我的测试程序如下。关闭与临时日志关联的句柄 'FLOG' 后,当我将 FLOG 重新分配给新日志时,这会以某种方式重新打开临时日志,但我无法删除它。 有没有办法确保旧的临时文件保持关闭状态(这样我就可以删除它)

# Pre-existing log:
final_log = "final_#{Time.now.strftime("%Y%m%d")}.txt"

#Writing something in it
File.open(final_log, "w+") { |file| file.write("This is the final log: #{final_log}\n") }

# temp log:
temp_log = "temp_#{Time.now.strftime("%Y%m%d")}.txt"
FLOG = File.new(temp_log, "w+")

# write some stuff in temp_log
FLOG.puts "Writing in temp_log named #{temp_log}"

# closing handle for temp_log
FLOG.close  

# avoid constant reuse warning:
Object.send(:remove_const,'FLOG') if Object.const_defined?('FLOG')

# need to append temp_log content to final_log with handle FLOG 
FLOG = File.open(final_log, "a+") 

# appending old temp log to new log 
File.open(temp_log, "r").readlines.each do |line|
    puts "appending...  #{line}"
    FLOG.puts "appending...  #{line}"               
end

# closing handle    
FLOG.close

# this tells me that 'temp_log' is somehow re-opened:
ObjectSpace.each_object(File) { |f| puts("3: #{temp_log} is open") if f.path == temp_log && !f.closed? }

File.delete(temp_log)       # Cant do that:
# test_file2.rb:35:in `delete': Permission denied - temp_20150324.txt (Errno::EACCES)

如果您要使用临时文件,请使用 tempfile

require 'tempfile'

# Pre-existing log:
final_log = "final_#{Time.now.strftime("%Y%m%d")}.txt"

#Writing something in it
File.open(final_log, "w+") { |file| file.write("This is the final log: #{final_log}\n") }

# give the tempfile a meaningful prefix
temp_log = Tempfile.new('foobar')
begin
  $flog = temp_log

  # write some stuff in temp_log
  $flog.puts "Writing in temp_log named #{temp_log.path}"

  # need to append temp_log content to final_log with handle $flog
  $flog = File.open(final_log, "a+")

  # reopen temp_log for reading, append to new log
  temp_log.open.readlines.each do |line|
    puts "appending...  #{line}"
      $flog.puts "appending...  #{line}"
  end

  # closing handle
  $flog.close
ensure
  # delete temp_log
  temp_log.unlink
end

虽然全局变量通常很糟糕,但破解常量以便您可以像全局变量一样使用它更糟糕。

temp_log 仍然打开,因为您没有关闭它。如果你做了类似的事情:

temp_log_lines = File.open(temp_log, 'r') { |f| f.readlines }

然后到 temp_log 的 I/O 流将在块的末尾关闭。但是,执行 File.open(temp_log, "r").readlines 会获取 File.open 返回的 IO 对象并在其上调用 readlines,然后您在随附的块中调用 each。由于该块是您对 each 而不是 File.open 的调用的一部分,因此流不会在它结束时关闭,并在程序的其余部分保持打开状态。

至于为什么不能在程序末尾删除temp_log,不知道底层文件系统是怎么回事就不好说了。 Ruby 和基础 (POSIX) OS 都不会抱怨如果你删除了一个你已经打开流但没有关闭的文件;该文件将被取消 linked 但流将持续存在并且仍然具有文件的内容等等。您得到的错误是说该程序的 Ruby 进程的所有者无权删除该程序创建的文件。这很奇怪,但很难仅从这段代码中诊断出来。考虑程序运行的目录,它的权限是什么,等等。

更一般地说,Ruby 中有些东西可以让您的生活更轻松。

如果您想要一个临时文件,可以使用 Tempfile class,它可以为您做很多工作。

对 Ruby 中的文件执行 I/O 的惯用方法是将块传递到 File.open。该块被传递给在块末尾自动关闭的文件的 I/O 流,因此您无需手动执行此操作。这是一个例子:

flog = File.new(temp_log, 'w+') do |f|
  f.puts "Writing in temp_log named #{temp_log}"
end

FLOG 在您的代码中不是真正的常量。 A constant is only a constant if its value never changes throughout the life of the program it's declared in. Ruby 是一种非常宽松的语言,因此它允许您重新分配它们,但如果您这样做会发出警告。如果你这样做,更严格的语言会抛出错误。 FLOG只是一个普通的变量,应该写成flog。常量的一个很好的用途是你的程序需要能够引用的外部值,而不是每次你需要引用 pi 的近似值时写 3.141592653589793,你可以声明 PI = 3.141592653589793 并在之后使用 PI。在 Ruby 中,这已经在 Math 模块中为您完成——Math::PI returns 这个。用户设置是常量经常出现的另一个地方——它们是在程序运行之前确定的,有助于确定程序的作用,并且在执行期间不应修改,因此有时将它们存储在常量中是有意义的。

您将提供的程序描述为测试程序。 Ruby 拥有非常棒的测试库,您可以利用它比编写这样的脚本更好。 Minitest 是 Ruby 标准库的一部分,也是我在 Ruby 中最喜欢的测试框架,但很多人也喜欢 RSpec。 (我想 link 那些框架的文档,但我没有足够的声誉 — 抱歉。你必须 Google。)

不过,如果您像这样强制性地编写代码,将很难使用这些框架。 Ruby 是一种非常面向对象的语言,在使用它时,您会从以面向对象的风格构建代码中获益良多。如果您不熟悉 OO 设计,一些对我来说非常有用的书是 Sandi Metz Ruby 中的实用面向对象设计,重构:改进现有代码的设计,作者:Martin Fowler 等人,Growing Object-Oriented Software, Guided by Tests,作者:Steve Freeman 和 Nat Pryce。 (这里缺少 links 也是一样。)