4

Updated description to be clearer.

Say I have a file and it has these lines in it.

one
two
three
five

How do I add a line that says "four" after the line that says "three" so my file now looks like this?

one 
two
three
four
five
Chris F
  • 14,337
  • 30
  • 94
  • 192
  • 1
    So you want to read a file on the computer and add a line after a particular number of lines? What is the context? What are the rules determining when you add the line? – Sasha Jan 23 '15 at 22:09
  • 1
    If the file is small, you can load the whole thing into memory and rewrite the whole file after inserting your new line. Can you guarantee the file size? – ptd Jan 23 '15 at 22:21
  • http://stackoverflow.com/questions/2139058/how-to-insert-a-string-into-a-textfile or http://stackoverflow.com/questions/3320220/insert-rows-on-specific-line-in-a-file – Casper Jan 23 '15 at 22:22
  • You have several options, but they all involve reading the entire existing file and writing the entire new file. One way is to read the file into memory, as a string or an array, insert the line and then write the object to file. Another way is to read the file line-by-line and write those lines to a temporary file (ie., read a line, write a line), until you reach the point where you want to insert the line, then write that line to the file, then carry on reading and writing the remaining lines of the input file. (cont.) – Cary Swoveland Jan 23 '15 at 23:06
  • cont.) When you are finished and both files are closed, you can optionally delete the original file and rename the new file to the name of the original file. I think most Rubyists would recommend you do it that way (and it may be the only way if the file is sufficiently large). If you do read the entire file into memory and then insert the new line, you can either overwrite the original file or write to a temporary file and optionally delete and rename. The latter is a safer choice in case something goes wrong while writing the file. – Cary Swoveland Jan 23 '15 at 23:09

5 Answers5

9

Assuming you want to do this with the FileEdit class.

Chef::Util::FileEdit.new('/path/to/file').insert_line_after_match(/three/, 'four')
Tejay Cardon
  • 4,193
  • 2
  • 16
  • 31
0

Here is the example ruby block for inserting 2 new line after match:

ruby_block "insert_lines" do
  block do
    file = Chef::Util::FileEdit.new("/path/of/file")
    file.insert_line_after_match("three", "four")
    file.insert_line_after_match("four", "five")
    file.write_file
  end
end

insert_line_after_match searches for the regex/string and it will insert the value in after the match.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
-1

This is an in memory solution. It looks for complete lines rather than doing a string regex search...

def add_after_line_in_memory path, findline, newline
  lines = File.readlines(path)
  if i = lines.index(findline.to_s+$/)
    lines.insert(i+1, newline.to_s+$/) 
    File.open(path, 'wb') { |file| file.write(lines.join) }
  end
end

add_after_line_in_memory 'onetwothreefive.txt', 'three', 'four'
Matt
  • 68,711
  • 7
  • 155
  • 158
-1

The following Ruby script should do what you want quite nicely:

# insert_line.rb
#   run with command "ruby insert_line.rb myinputfile.txt", where you
#   replace "myinputfile.txt" with the actual name of your input file
$-i = ".orig"
ARGF.each do |line|
  puts line
  puts "four" if line =~ /^three$/
end

The $-i = ".orig" line makes the script appear to edit the named input file in-place and make a backup copy with ".orig" appended to the name. In reality it reads from the specified file and writes output to a temp file, and on success renames both the original input file (to have the specified suffix) and the temp file (to have the original name).

This particular implementation writes "four" after finding the "three" line, but it would be trivial to alter the pattern being matched, make it count-based, or have it write before some identified line rather than after.

pjs
  • 18,696
  • 4
  • 27
  • 56
-2

An AWK Solution

While you could do this in Ruby, it's actually trivial to do this in AWK. For example:

# Use the line number to choose the insertion point.
$ awk 'NR == 4 {print "four"}; {print}' lines
one
two
three
four
five

# Use a regex to prepend your string to the matched line.
$ awk '/five/ {print "four"}; {print}' lines
one
two
three
four
five
Todd A. Jacobs
  • 81,402
  • 15
  • 141
  • 199
  • Maybe note `/^five$/` if you only want to match complete lines – Matt Jan 24 '15 at 00:00
  • 2
    I could and WANT to do this in Ruby, like the title says :) – Chris F Jan 24 '15 at 17:19
  • @Matt YAGNI. The anchors are not required for the OP's posted corpus. – Todd A. Jacobs Jan 24 '15 at 18:32
  • @ChrisF And you already have an accepted answer to do it. Luckily, Stack Overflow is not only here for *you*, but it's also here to help the billions of other people in the world who are *not* you, and this answer will help many of them. Your individual mileage, like your karma, may vary. – Todd A. Jacobs Jan 24 '15 at 18:43
  • @CogeGnome Until you get to "twentythree". I just mentioned noting it as the example being looked for _is_ a complete line and not being specific in regular expressions often causes unwanted behaviour. – Matt Jan 25 '15 at 11:41
  • @CodeGnome Downvoting because I do agree awk is a solution, but I hardly think someone searching on this use case will find it usefull. Someone doing another search on the 'add line in a file' will find other answers more focused to their use cases (perl, tcl, awk, bash). – Tensibai Jan 26 '15 at 08:39