Gsub and multiple-replacement

So I have a file that I am replicating per user.
I’m copying the file,

Profile_path=’/path/to/file/’
Profile_file=‘myfile.txt’
copy(Profile_path + Profile_file, Profile_path + username + ‘.txt’)

This obviously goes off without a hitch. Now, I need to find and replace
three specific values within the file itself.

So within the file we have:

Record for {username} with {email} from {country}

I’ve read through a ton of material and am looking for the best method.
Here are a few caveats:
I don’t need a temp file.
I am unconcerned with overwriting the username.txt file with itself.
I have to replace three values within the file.

Basically, I want to do something like:

text = File.read(Profile_path+username+’.txt’)
File.open(Profile_path+username+’.txt’,'w+){|f| f <<
text.gsub(regex_to_find,
text_to_put_in_place)}

But what is the best way to do this for three variables?

Here are a few caveats:
I don’t need a temp file.

Yes, you do.

I am unconcerned with overwriting the username.txt file with itself.

Ok, you still need a tempfile.

Now, if you say you are unconcerned with losing all the data in your
file, then you don’t need a tempfile.

The bottom line is this: you don’t replace anything in a file. Instead,
you read in the original file, and write out the altered lines to a new
file. When you are done, you can delete the original file and rename
the new file to the old file name.

On Tue, May 10, 2011 at 11:16 PM, Greg H. [email protected]
wrote:

I don’t need a temp file.
I am unconcerned with overwriting the username.txt file with itself.
I have to replace three values within the file.

How about using ERB and a template?

http://ruby-doc.org/stdlib/libdoc/erb/rdoc/classes/ERB.html

Load the template, substitute the values in your template with what’s
user specific, and write the template out to the path you want?

That way, you don’t have to copy, don’t need a tempfile, and can scale
up to any amount of users. :wink:


Phillip G.

Though the folk I have met,
(Ah, how soon!) they forget
When I’ve moved on to some other place,
There may be one or two,
When I’ve played and passed through,
Who’ll remember my song or my face.

require ‘stringio’

str =<<‘ENDOFSTRING’
Record for WendyQ with [email protected] from Germany
Record for bohH with [email protected] from USA
ENDOFSTRING

file = StringIO.new(str)

file.each do |line|
words = line.split(’ ')
words[2] = ‘***’
words[4] = ‘***’
words[6] = ‘***’

puts words.join(’ ')
end

–output:–
Record for *** with *** from ***
Record for *** with *** from ***

7stud – wrote in post #997852:

OK, I think it would be better to be a bit more specific…

User logs into site. Credentials are verified via LDAP.

At login, I am copying a .mobileconfig template file (xml/plist) and
saving it as {username}.profile.mobileconfig
This file contains placeholders for:
{username} = [email protected]
{userroot} = bob
{password} = mypassword

I need to read it, modify it, save it and replace it. At that point, the
user will get the index and be afforded to download this file.

Thoughts?

Greg H. wrote in post #997877:

7stud – wrote in post #997852:

OK, I think it would be better to be a bit more specific…

User logs into site. Credentials are verified via LDAP.

At login, I am copying a .mobileconfig template file (xml/plist) and
saving it as {username}.profile.mobileconfig
This file contains placeholders for:
{username} = [email protected]
{userroot} = bob
{password} = mypassword

I need to read it, modify it, save it and replace it. At that point, the
user will get the index and be afforded to download this file.

Thoughts?

Yeah, what is so hard about posting a sample of the original file, as
well as the modified file that you want to end up with, and leaving out
all your confusing descriptions?

On Wed, May 11, 2011 at 2:49 AM, Greg H. [email protected] wrote:

{userroot} = bob
{password} = mypassword

I need to read it, modify it, save it and replace it. At that point, the
user will get the index and be afforded to download this file.

Thoughts?

How big is the file? Is it acceptable to have it all in memory?
Do you have control over the format of the placeholders? If so, I’d go
with Philip’s suggestion of using ERB. Just change {username} to <%=
username %> and evaluate the template with a binding that contains a
variable username. For example:

username = “Bob”
password = “pwd”
expanded_file = ERB.new(File.read(“template.erb”), nil,
“%<>”).result(binding)

template.erb:

The user is <%= username %>
The password is <%= password %>

Jesus.

On Wed, May 11, 2011 at 3:45 PM, Greg H. [email protected] wrote:

Password PayloadType PayloadType ActiveSync services. PayloadVersion

end
FileUtils.mv(“/tmp/replaceable2.txt”, user_file)

text= File.read user_file
File.open(user_file, ‘w+’){|f| f << text.gsub(/{username}/,
username)}

You can use the block form of gsub to do only one pass:

variables = {“username” => “Bob”, “password” => “pwd”}

file_contents = File.read(“master_file”).read
file_contents.gsub!(/{(.*?)}/) {|m| variables[$1]}

#and then write the file contents to another file, delete and rename

Jesus.

posted by “Jesús Gabriel y Galán”

file_contents = File.read(“master_file”).read

You have an extra read() there.

You can also read the file line by line, and use a hash of replacements
as an argment to gsub:

#untested, but should be pretty close

replacements = {
‘{username}’ => ‘BOB’,
‘{password}’ => ‘BOB-PASSWORD’,
‘{userroot}’ => ‘BOB-USERROOT’
}

pattern = ‘{.*?}’

File.open(‘out.xml’) do |outfile|
IO.foreach(‘in.xml’) do |line|
outfile.print line.gsub(/#{pattern}/, replacements)
end
end

#delete infile and rename outfile

7stud – wrote in post #997885:

Yeah, what is so hard about posting a sample of the original file, as
well as the modified file that you want to end up with, and leaving out
all your confusing descriptions?

OK, here’s the inbound:

<?xml version="1.0" encoding="UTF-8"?> PayloadContent EmailAddress {username} Host exchange.domain.com MailNumberOfPastDaysToSync 3 Password {password} PayloadDescription Configures device for use with Microsoft Exchange ActiveSync services. PayloadDisplayName Exchange PayloadIdentifier com.domain.enterprise.eas PayloadOrganization Domain PayloadType com.apple.eas.account PayloadUUID FE477C13-BF4F-4EA0-BE09-3968EC40C952 PayloadVersion 1 SSL UserName domain\{userroot} PayloadDescription Profile Test PayloadDisplayName Domain PayloadIdentifier com.domain.enterprise PayloadOrganization Domain PayloadRemovalDisallowed PayloadType Configuration PayloadUUID A6B7D66D-1179-4E85-A005-4DAACD4EDF0F PayloadVersion 1

And the outbound replaces elements in only the array area (in this case,
just the EAS but other options may be included - LDAP, IMAP, etc.)

EmailAddress
[email protected]
Host
exchange.domain.com
MailNumberOfPastDaysToSync
3
Password
MyPassword
PayloadDescription
Configures device for use with Microsoft Exchange
ActiveSync services.
PayloadDisplayName
Exchange
PayloadIdentifier
com.domain.enterprise.eas
PayloadOrganization
Domain
PayloadType
com.apple.eas.account
PayloadUUID
FE477C13-BF4F-4EA0-BE09-3968EC40C952
PayloadVersion
1
SSL

UserName
domain\greghacke

Now, the initial file is provided to me may be replaced as needed. I
would like to avoid converting it to ERb as I cannot gaurentee anyone
else will do the work to ensure it stays updated and correct.

I’ve dug around - really - and found two examples for single element
replacement.

My belief is that there are a plethora of options execute this. I would
like to do something like:
File.copy(master,user_file)
File.open(‘/tmp/temp_file.txt’, ‘w+’) do | new_file |
new_file.puts(File.open(user_file, ‘r’) do | original_file |
original_file.read.gsub(/{username}/, username)
end)
end
FileUtils.mv(“/tmp/replaceable2.txt”, user_file)

text= File.read user_file
File.open(user_file, ‘w+’){|f| f << text.gsub(/{username}/,
username)}

And here is an xml parsing solution(which requires reading the whole
document into memory):

require ‘rexml/document’
include REXML

f = File.new( “input.xml” )
doc = REXML::Document.new(f)

replacements = {
‘{username}’ => ‘BOB’,
‘{password}’ => ‘BOB-PASSWORD’,
‘{userroot}’ => ‘BOB-USERROOT’
}

pattern = ‘{.*?}’

XPath.each(doc, ‘//string’) do |element|
text = element.text
element.text = text.gsub(/#{pattern}/, replacements)
end

puts doc.to_s

f.close

Note that you are using an unfortunate character in the string:

domain{userroot}

A backslash has a special meaning in ruby strings, so make sure to use
single quoted strings.