使用Ruby在文件前面添加一行

我想用Ruby添加一行到Ruby的文件顶部:

# initial file contents something else # file contents after prepending "hello" on its own line hello something else 

以下代码只是替换整个文件的内容:

 f = File.new('myfile', 'w') f.write "test string" 

这是一项非常常见的任务:

 original_file = './original_file' new_file = original_file + '.new' 

设置测试:

 File.open(original_file, 'w') do |fo| %w[something else].each { |w| fo.puts w } end 

这是实际的代码:

 File.open(new_file, 'w') do |fo| fo.puts 'hello' File.foreach(original_file) do |li| fo.puts li end end 

将旧文件重命名为安全的文件:

 File.rename(original_file, original_file + '.old') File.rename(new_file, original_file) 

certificate它有效:

 puts `cat #{original_file}` puts '---' puts `cat #{original_file}.old` 

哪个输出:

 hello something else --- something else 

您不想尝试将文件完全加载到内存中。 这将一直有效,直到你得到一个大于你的RAM分配的文件,然后机器爬行,或者更糟糕的是,崩溃。

相反,逐行阅读。 读取单个行仍然非常快,并且可扩展。 您必须在驱动器上有足够的空间来存储原始文件和临时文件。

fwiw这似乎有效:

 #!usr/bin/ruby f = File.open("myfile", "r+") lines = f.readlines f.close lines = ["something\n"] + lines output = File.new("myfile", "w") lines.each { |line| output.write line } output.close 

正如一些人所说,可能不会将它用于较大的文件,但这是一个简单的开始。

 rd = IO.read 'myfile' IO.write 'myfile', "hello\n" + rd 

没有机制可以轻松完成您想要做的事情。

相反,您需要打开文件,删除文件,按旧名称打开新文件进行写入,编写内容,然后从旧文件的内容中写入新文件。 这听起来很复杂,但代码很简单:

 $ cat prepend.rb #!/usr/bin/ruby File.open("passwd", "r") do |orig| File.unlink("passwd") File.open("passwd", "w") do |new| new.write "test string" new.write(orig.read()) end end 

请注意,我使用的机制并不打扰检查错误 – 您应该处理每个File.open()请求和File.unlink()请求的错误 – 它假定文件的全部内容将适合记忆。 一个简短的例子:

 $ rm passwd $ cp /etc/passwd . $ ./prepend.rb $ head passwd test stringroot:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/bin/sh man:x:6:12:man:/var/cache/man:/bin/sh lp:x:7:7:lp:/var/spool/lpd:/bin/sh mail:x:8:8:mail:/var/mail:/bin/sh news:x:9:9:news:/var/spool/news:/bin/sh $ 

如果你想处理可能不完全适合内存的文件,你应该编写一个这样的循环类型(未经测试 – 更好地考虑伪代码):

 while (data=orig.read(4096)) { new.write(data) } 

作为替代方法,您可以写入临时文件,如果写入过程成功, 删除该文件并将临时文件重命名。 无论哪种方法对您最有意义。

你可以试试这个:

 File.copy_stream(myfile,tempfile) f = File.open(myfile,'w') f.write("Hello\n#{File.open(tempfile,'r').read}") f.close File.delete(tempfile) 

我想出了类似的东西,它比我见过的其他解决方案更具描述性和隐秘性:

 def file_prepend(file, str) new_contents = "" File.open(file, 'r') do |fd| contents = fd.read new_contents = str << contents end # Overwrite file but now with prepended string on it File.open(file, 'w') do |fd| fd.write(new_contents) end end 

你可以像这样使用它:

 file_prepend("target_file.txt", "hello world!\n") 

从命令行,您可以执行以下操作:

 ruby -i -pe '$_= "prepended text\n"+$_ if $. == 1' myfile 

或更有效率

 ruby -i -pe 'BEGIN { gets; print "prepended text\n" + $_ }; ' myfile 

遗憾的是,事实certificate, -i (就地)选项并不是真正的就地(但也不是sed s就地选项) – 我的文件将在op之后有一个不同的inode 。

这让我感到难过,因为有效的是,如果我没有足够的磁盘空间来存放两个副本,我就无法过滤(前置)一个巨大的文件。

然而,这并不是很难在原地进行(一般过滤,不仅仅是第一行)。 您需要做的就是:

1)确保你有单独的读写指针(或单独的文件对象用于读写)2)确保你已经缓存了你要重写的未读部分3)如果你的过滤操作,最后截断到文件应该以较短的文件结束

我在https://github.org/pjump/i_rewriter为此编写了包装类。

有了它,你可以做到

 Rewriter.new("myfile") do |line, index| index == 0 ? "prepended text\n" + line : line end 

或者使用可执行文件:

 $ i_rewriter 'index == 0 ? "prepended text\n" + line : line' myfile 

小心使用(如果中断,可能会损坏您的文件)。

纯净,但有效。 最小化与文件相关的操作。

 `#!/bin/ruby inv_file = File.open("./1.txt","r+") { |f| #Read file string by-string until f.eof? #Searching for some if f.readline[/condition here/] #remember position offset = f.pos #Find it? - stop reading break end end #Remember what contains rest of file tail = f.readlines #Again to offset f.pos = offset #insert what necessary f.puts "INSERTED" #reconstruct tail f.write tail.join }`