Ruby way to update file lines

M

Mark Probert

Hi.

I have a need to update a config file. It uses '#' as a line comment
marker. The config file looks like:

# file comment
#
name1:foo1:bar:eek:ther
name2:foo2:bar:eek:ther
name3:foo3:bar:eek:ther
name4:foo4:bar:eek:ther

The name:foo combination is unique.

What is the most efficient way to read in the file, update the line, and
then write it back to the file. One update would be to place a '#' in col
1

#name3:foo3:bar:eek:ther

Any thoughts appreciated.

-mark.
 
K

Kirk Haines

On Thu, 19 Aug 2004 10:45:54 +0900, Mark Probert wrote
What is the most efficient way to read in the file, update the line,
and then write it back to the file. One update would be to place a
'#' in col 1

#name3:foo3:bar:eek:ther

Any thoughts appreciated.

Unless the file is very large, my initial thought is just to read the file
to an array. Edit, then rewrite the file.


Kirk Haines
 
R

Robert Klemme

Mark Probert said:
Hi.

I have a need to update a config file. It uses '#' as a line comment
marker. The config file looks like:

# file comment
#
name1:foo1:bar:eek:ther
name2:foo2:bar:eek:ther
name3:foo3:bar:eek:ther
name4:foo4:bar:eek:ther

The name:foo combination is unique.

What is the most efficient way to read in the file, update the line, and
then write it back to the file. One update would be to place a '#' in col
1

#name3:foo3:bar:eek:ther

Any thoughts appreciated.

-mark.

Some code thoughts below

Regards

robert


# match keys
COMMENT = lambda {|key, val| "# #{key}:#{val}"}
DELETE = lambda { nil }

tasks = {
"foo:bar" => COMMENT,
"foo1:bar1" => DELETE,
}

conf = File.readlines("conf.txt")

# manipulate
conf.map! do |line|
if /^([^:]+:[^:]+):(.*)$/ =~ line && (fun = tasks[$1])
fun.call $1, $2
else
line
end
end

# remove deleted lines
conf.compact!

File.open("conf-1.txt", "w") {|io| io.puts conf }

#
# alternative -----------------------------------------
#

# use regexp as line matcher
tasks = {
/^foo1:bar1:/ => lambda {|line| "# #{line}"},
/^\s*#\s*foo.bar:/ => lambda {|line| line.sub(/^\s*#\s*/, '' )},
}


conf = File.readlines("conf-1.txt")

# manipulate
conf.map! do |line|
p,f = tasks.find {|patt, fun| patt =~ line}
f ? f.call( line ) : line
end

# remove deleted lines
conf.compact!

File.open("conf-2.txt", "w") {|io| io.puts conf }
 
K

Kristof Bastiaensen

Hi.

I have a need to update a config file. It uses '#' as a line comment
marker. The config file looks like:

# file comment
#
name1:foo1:bar:eek:ther
name2:foo2:bar:eek:ther
name3:foo3:bar:eek:ther
name4:foo4:bar:eek:ther

The name:foo combination is unique.

What is the most efficient way to read in the file, update the line, and
then write it back to the file. One update would be to place a '#' in col
1

#name3:foo3:bar:eek:ther

Any thoughts appreciated.

-mark.

You could use inplace edit. This can make ruby work like awk.
For example:

ruby -aF: -ni.bak -e 'print "#" if $F[0] == "name3"' configfile

KB
 
M

Mark Probert

Robert Klemme said:
Some code thoughts below

Regards

robert


# match keys
COMMENT = lambda {|key, val| "# #{key}:#{val}"}
DELETE = lambda { nil }

tasks = {
"foo:bar" => COMMENT,
"foo1:bar1" => DELETE,
}

conf = File.readlines("conf.txt")

# manipulate
conf.map! do |line|
if /^([^:]+:[^:]+):(.*)$/ =~ line && (fun = tasks[$1])
fun.call $1, $2
else
line
end
end

# remove deleted lines
conf.compact!

File.open("conf-1.txt", "w") {|io| io.puts conf }

I liked this one :) Thanks.

One minor correction: COMMENT and DELETE should be lower case. Ruby 1.8
spits and error about constants otherwise.

-mark.
 
R

Robert Klemme

Mark Probert said:
Robert Klemme said:
Some code thoughts below

Regards

robert


# match keys
COMMENT = lambda {|key, val| "# #{key}:#{val}"}
DELETE = lambda { nil }

tasks = {
"foo:bar" => COMMENT,
"foo1:bar1" => DELETE,
}

conf = File.readlines("conf.txt")

# manipulate
conf.map! do |line|
if /^([^:]+:[^:]+):(.*)$/ =~ line && (fun = tasks[$1])
fun.call $1, $2
else
line
end
end

# remove deleted lines
conf.compact!

File.open("conf-1.txt", "w") {|io| io.puts conf }

I liked this one :) Thanks.

Thanks, too! :)
One minor correction: COMMENT and DELETE should be lower case. Ruby 1.8
spits and error about constants otherwise.

They are meant to be constants because they *are* constant. I just put both
variants into a single file but you'll likely need only one of them, don't
you?

Regards

robert
 
M

Mark Probert

Robert Klemme said:
They are meant to be constants because they *are* constant. I just
put both variants into a single file but you'll likely need only one
of them, don't you?
When I leave them as constants, Ruby complains with the following error:

deln.rb:37: dynamic constant assignment
COMMENT = lambda {|key, val| "# #{key}:#{val}" }
^

In the end, it worked out well. My code snippet looks like:

# create a function map for the items we want to delete
comment = lambda {|key, val| "# #{key}:#{val}" }
delete = lambda {|key, val| nil }
checkmap = {}
@nodelist.each do |n|
key = "#{n.name}:#{n.ip}"
checkmap[key] = (@remove) ? delete : comment
end

Where I now have the option of making the deletion permanent or just
commenting it out. Which is perfect for my application.

Regards,

-mark.
 
R

Robert Klemme

Mark Probert said:
When I leave them as constants, Ruby complains with the following error:

deln.rb:37: dynamic constant assignment
COMMENT = lambda {|key, val| "# #{key}:#{val}" }
^

Then you got the scoping wrong. Constants should be defined on top level or
class / module scope, otherwise they are of not much use: what do you gain
by reevaluationg and assigning an expression on each method invocation?
That's not what constants are intended for. You probably did something like
this:
SyntaxError: compile error
(irb):2: dynamic constant assignment
def foo() FOO = "bar" end
^
from (irb):2

While it should be

FOO = "bar"
def foo() ... end
# use FOO
or

class Any
FOO = "bar"
# use FOO
end

Both of them don't trigger warnings unless you do multiple assignments to
the same constant:
(irb):2: warning: already initialized constant FOO
=> "bar"
In the end, it worked out well. My code snippet looks like:

# create a function map for the items we want to delete
comment = lambda {|key, val| "# #{key}:#{val}" }
delete = lambda {|key, val| nil }
checkmap = {}
@nodelist.each do |n|
key = "#{n.name}:#{n.ip}"
checkmap[key] = (@remove) ? delete : comment
end

Where I now have the option of making the deletion permanent or just
commenting it out. Which is perfect for my application.

Fine.

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top