grep the input file with keyword, then generate new report - ruby

cat infile
abc 123 678
sda 234 345 321
xyz 234 456 678
I need grep the file for keyword sda and report with first and last column.
sda has the value of 321
If you know bash script, I need a function in ruby as in below bash(awk) script:
awk '/sda/{print $1 " has the value of " $NF}' infile

How about something like this?
File.open("infile", "r").each_line do |line|
next unless line =~ /^sda/ # don't process the line unless it starts with "sda"
entries = line.split(" ")
var1 = entries.first
var2 = entries.last
puts "#{var1} has the value of #{var2}"
end
I don't know where you are defining the "sda" matcher. If it's fixed, you can just put it in there.
If not, you might try grabbing it from commandline arguments.

key, *_, value = line.split
next unless key == 'sda' # or "next if key != 'sda'"
puts your_string
Alternatively, you could use a regexp matcher in the beginning to see if the line starts with 'sda' or not.

Related

Why does my IO.write insert a "%" sign at the end of output?

I use this line to read from temp.dat, which contains "100"
fahrenheit = IO.read("temp.dat").to_i * 9 / 5 + 32
Now, to write this result in another file;
Method 1
f = File.new("temp.out", "w")
f.puts fahrenheit
cat temp.out
212
Method 2
IO.write("temp.out", fahrenheit)
cat temp.out
212%
Why does my IO.write insert a “%” sign at the end of output?
It doesn't. Here's the binary content of the file. That % character is the command prompt of your shell, which is confused by the lack of EOL in the file. POSIX-compliant text files should always end lines with end-of-line character.

Ruby: Insert a string after/before a pattern

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

command to print all the lines until the first match

I am trying to print all the lines from a file before the first match. I have the same entries again in the file, but I don't need that lines. Tried
awk "{print} /${pattern}/ {exit}" and sed "/$pattern/q" (my serach is based on a variable). But both these commands are printing all the line before the last match
ex: my file is like
abc
bcd
def
xyz
def
lmno
def
xvd
when my pattern is 'def', i just need abc and bcd . but the above commands are printing, all the lines before the last 'def'. could you please provide some idea
This should work:
awk '!'"/${pattern}/{print} /${pattern}/ {exit}" input_file.txt

Bash scripting: Get value before a count of linex between 2 words

The problem is as follows.
File:
Name1
command
data1
data2
data3
done
Name2
command
data4
data5
done
Name1
command
data6
done
In the file above, I want to count the lines between "command" and "done" and give a list of names where this count is more then 1.
the output here should be:
Name1
Name2
I've experimented with:
sed -n "/command/,/done/p" | count
Any idea's?
This can be a way:
$ awk '/done/ {if (t>1) print name} {t++} /^Name/ {name=$0} /command/ {t=0}' a
Name1
Name2
Explanation
/done/ {if (t>1) print name} if the line contains done, print the title of the block just if the counter is > 1.
{t++} increment the counter in any case.
/^Name/ {name=$0} store the line value.
/command/ {t=0} if the line contains command, reset the counter.
How about this awk:
awk '/^[A-Z]/{name=$0;count=-1;next}/done/&&count>1{print name;next}{count++}' file
Output:
Name1
Name2
That says, if the line starts with a left-aligned capital letter, save that as the name of the command and set the counter to -1 ... so that it will be zero after the next line when "command" appears". Then move to next line without further ado. If the line matches the string "done" and the counter is greater than one, print the name we saved earlier and move to next line. Increment counter for all other lines.
If Name are not fixed string then you can use:
awk '/command/{cmd=1; n=0;next} !cmd{p=$1;next} cmd{n++} /done/{if (n>2) print p; cmd=0; next}' dat
Name1
Name2

How do I join two lines of a file by matching pattern, in Ruby or Bash?

I'm using a Ruby script to do a lot of manipulation and cleaning to get this, and a bunch of other files, ready for import.
I have a really large file with some data that I'm trying to import into a database. There are some data issues with newline characters being in the data where they should not be, messing with the import.
I was able to solve this problem with sed using this:
sed -i '.original' -e ':a' -e 'N' -e '$!ba' -e 's/Oversight Bd\n/Oversight Bd/g' -e 's/Sciences\n/Sciences/g' combined_old_individual.txt"
However, I can't call that command from inside a Ruby script, because Ruby messes up interpreting the newline characters and won't run that command. sed needs the non-escaped newline character but when calling a system command from Ruby it needs a string, where the newline character needs to be escaped.
I also tried doing this using Ruby's file method, but it's not working either:
File.open("combined_old_individual.txt", "r") do |f|
File.open("combined_old_individual_new.txt","w") do |new_file|
to_combine = nil
f.each_line do |line|
if(/Oversight Bd$/ =~ line || /Sciences$/ =~ line)
to_combine = line
else
if to_combine.nil?
new_file.puts line
else
combined_line = to_combine + line
new_file.puts combined_line
to_combine = nil
end
end
end
end
end
Any ideas how I can join lines where the first line ends with "Bd" or "Sciences", from within a Ruby script, would be very helpful.
Here's an example of what might go in a testfile.txt:
random line
Oversight Bd
should be on the same line as the above, but isn't
last line
and the result should be
random line
Oversight Bdshould be on the same line as the above, but isn't
last line
With ruby (My first attempt at a ruby answer):
File.open("combined_old_individual.txt", "r") do |f|
File.open("combined_old_individual_new.txt","w") do |new_file|
f.each_line do |line|
if(/(Oversight Bd|Sciences)$/ =~ line)
new_file.print line.strip
else
new_file.puts line
end
end
end
end
You have to realize that sed normally works line by line, so you cannot match for \n in your initial pattern. You can however match for the pattern on the first line and then pull in the next line with the N command and then run the substitute command on the buffer to remove the newline like so:
sed -i -e '/Oversight Bd/ {;N;s/\n//;}' /your/file
Run from Ruby (without -i so that the output goes to stdout):
> cat test_text
aaa
bbb
ccc
aaa
bbb
ccc
> cat test.rb
cmd="sed -e '/aaa/ {;N;s/\\n//;}' test_text"
system(cmd)
> ruby test.rb
aaabbb
ccc
aaabbb
ccc
Since you are asking in bash, here is a pure-bash solution:
$ r="(Oversight Bd|Sciences)$"
$ while read -r; do printf "%s" "$REPLY"; [[ $REPLY =~ $r ]] || echo; done < combined_old_individual.txt
random line
Oversight Bdshould be on the same line as the above, but isn't
last line
$

Resources