1

I know how to add string(or text) to end of the file as well as beginning of the file, but could not google out the way to insert string (with a variable) after/before a pattern.

For example,

root@vikas:~# cat /etc/resolv.conf
search reachvikas.com
nameserver 192.168.1.27
root@vikas:~#

Now, I want to add another nameserver like below.

root@vikas:~# cat /etc/resolv.conf
search reachvikas.com
nameserver 192.168.181.2
nameserver 192.168.1.27
root@vikas:~#

I can do this with sed easily, but just looking a way out with Ruby.

Update: I have written below code, but this replaces the last line does not adds one. I guess tweaking file.seek would help me, but not sure how.

File.open('/etc/resolv.conf', 'r+') do |file|  
  count = Integer(0)  
  file.each do |line|  
  #puts count  
    if count == 1  
      #puts count.to_i  
      file.seek(-1, IO::SEEK_CUR)  
      file.puts("\nnameserver 192.168.181.2")  
     end  
   count += 1  
   end  
end 

2 Answers 2

1

Here's a Ruby "one-liner" that does what I think you're trying to do. I created a resolv.conf file matching your first file contents. Then the following Ruby "one-liner", which I broke into several lines for readability, searches for a line that begins with "nameserver" and inserts an arbitrary list of new namservers with IPs you define.

$ cat resolv.conf
search reachvikas.com
nameserver 192.168.1.27

$ ruby -wnl -i.$SECONDS -e '
BEGIN { server_ips = %w(
  ip1
  ip2
  ip3
  ) }
if $_.start_with?("nameserver")
  server_ips.each{ |ip| puts "nameserver #{ip}"; }
end
puts $_
' resolv.conf

$ ls resolv.conf*
resolv.conf       resolv.conf.27729

$ cat resolv.conf
search reachvikas.com
nameserver ip1
nameserver ip2
nameserver ip3
nameserver 192.168.1.27

$ cat resolv.conf.27729
search reachvikas.com
nameserver 192.168.1.27

If you truly want it as a one-liner, you have to add semicolons where line breaks are needed:

ruby -wnl -i.$SECONDS -e 'BEGIN { server_ips = %w(ip1 ip2 ip3); }; if $_.start_with?("nameserver") ; server_ips.each{|ip| puts "nameserver #{ip}";}; end; puts $_;' resolv.conf

The -i.$SECONDS flag tells the Ruby interpreter to modify your input file in-place and to save the original version with a filename extension of $SECONDS, which is the number of seconds your terminal session has been alive. That makes it very unlikely you will permanently clobber a good file with bad code. The backup copies are there if you need them. You just have to clean up afterwards.

EDIT: Here's a short script that inserts rows into an existing file. Note that this does not save multiple copies of the input file like the one-liner does. This script reads an input file (resolv.conf), saves modified output to a temp file, then renames that temp file, replacing the original file. You would run this in the terminal like this $ ./script.rb resolv.conf

Script:

#! /usr/bin/env ruby

require 'tempfile'
require 'fileutils'

server_ips = %w(
  ip1
  ip2
  ip3
)

input_file = ARGV[0]
temp_file = Tempfile.new("#{input_file}.temp")
modified = false

begin
  File.open(input_file, 'r') do |file|
    file.each_line do |line|
      if modified == false && line.start_with?('nameserver')
        server_ips.each do |ip|
          temp_file.puts "nameserver #{ip}"
        end
        modified = true
      end
      temp_file.print line
    end
  end
  temp_file.close
  FileUtils.mv(temp_file.path, input_file)
ensure
  temp_file.close!
end

See the Ruby documentation for the Tempfile class for an explanation of the begin... ensure... end usage and the explicit close on the Tempfile object.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks Jamin, this code works fine when I use it on bash shell, but not inside a ruby script. How can I reformat this code to use inside a ruby script. Forgive my ignorance on this, I am a newbie in ruby. Also, I have written a code too, which could help me. Please see my update in the original question
Hi, Vikas. See my edit above. Regarding your added code using the 'r+' mode for modifying a file in-place, I've honestly never done it that way. Maybe it's out of habit, but I've always created temp files with modified content and then renamed them to replace an existing file.
I see your edits below (can only comment here). It works, but you could clean it up by using %q instead of %qw on the first line. %qw gives an array of quoted words. If you just want to insert a static block of text, you could use %q (single quote operator) and then later, remove the txt_to_insert.each do |ip| ... end loop and instead just print the whole text block to your file with temp_file.puts txt_to_insert. Keep in mind that %q() keeps line breaks within the parentheses so you should start with %q(nameserver 192.168.181.2 then a line break.
Perfect, I've modified my code as per your suggestions. Thanks.
0

Many thanks Jamin. I have slightly modified your code to suit my future needs as well, like if someone wants to add a block of lines with spaces before a keyword/pattern. This is what I have come up with. May be this helps someone.

txt_to_insert = %q(nameserver 192.168.181.2
nameserver 8.8.8.8)

input_file = "/etc/resolv.conf"
temp_file = Tempfile.new("tmp_file.temp")
modified = false

begin
  File.open(input_file, 'r') do |file|
    file.each_line do |line|
      if modified == false && line.start_with?('nameserver')
        temp_file.puts txt_to_insert
        modified = true
      end
     temp_file.print line
    end
  end
  temp_file.close
  FileUtils.mv(temp_file.path, input_file)
  ensure
    temp_file.close!
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.