Prepend a single line to file with Ruby

12,458

Solution 1

This is a pretty common task:

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

Set up the test:

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

This is the actual code:

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

Rename the old file to something safe:

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

Show that it works:

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

Which outputs:

hello
something
else
---
something
else

You don't want to try to load the file completely into memory. That'll work until you get a file that is bigger than your RAM allocation, and the machine goes to a crawl, or worse, crashes.

Instead, read it line by line. Reading individual lines is still extremely fast, and is scalable. You'll have to have enough room on your drive to store the original and the temporary file.

Solution 2

As some have said, probably don’t use this for larger files, but it is a simple start.

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

Solution 3

I have came up with something like this, it is a little bit more descriptive and less cryptic than other solutions I've seen:

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

And you can use it like this:

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

Solution 4

fwiw this seems to work:

#!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

Solution 5

No mechanism exists to do what you want to do easily.

Instead, you will need to open the file, delete the file, open a new file by the old name for writing, write your content, and then write the new file from the old file's contents. That's pretty convoluted sounding but is straightforward in code:

$ 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

Note that the mechanism I've used doesn't bother checking errors -- you should probably handle errors on each File.open() request and the File.unlink() request -- and it assumes that the entire contents of the file will fit in memory. A short example:

$ 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
$ 

If you want to handle files that might not fit entirely in memory, you should code a loop sort of like this (untested -- better consider it pseudo-code):

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

As an alternative, you could write to a temporary file, and if the writing process succeeds, then delete the file and rename the temporary file in place. Whichever approach makes the most sense to you.

Share:
12,458

Related videos on Youtube

MrDatabase
Author by

MrDatabase

Updated on June 04, 2022

Comments

  • MrDatabase
    MrDatabase almost 2 years

    I'd like to add a single line to the top a of file with Ruby like this:

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

    The following code just replaces the contents of the entire file:

    f = File.new('myfile', 'w')
    f.write "test string"
    
  • PJP
    PJP over 12 years
    Be careful slurping the file. It is not a scalable solution to read an entire file into memory.
  • steve
    steve over 10 years
    I like this for simple tinkering, personally.
  • Samuel Smith
    Samuel Smith about 9 years
    Best solution for the simple case.
  • sjsc
    sjsc over 7 years
    Great answer! Can reuse it throughout an app. Best solution IMO. Thanks!