gsub and multiple-replacement

G

Greg Hacke

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

7stud --

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

Phillip Gawlowski

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}

=A0I've read through a ton of material and am looking for the best method=
 
7

7stud --

require 'stringio'

str =<<'ENDOFSTRING'
Record for WendyQ with (e-mail address removed) from Germany
Record for bohH with (e-mail address removed) 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 ***
 
G

Greg Hacke

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} = (e-mail address removed)
{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?
 
7

7stud --

Greg Hacke 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} = (e-mail address removed)
{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 analysis?
 
J

Jesús Gabriel y Galán

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:
=A0{username} =3D (e-mail address removed)
=A0{userroot} =3D bob
=A0{password} =3D 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 <%=3D
username %> and evaluate the template with a binding that contains a
variable username. For example:


username =3D "Bob"
password =3D "pwd"
expanded_file =3D ERB.new(File.read("template.erb"), nil, "%<>").result(bin=
ding)

template.erb:

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

Jesus.
 
G

Greg Hacke

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"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PayloadContent</key>
<array>
<dict>
<key>EmailAddress</key>
<string>{username}</string>
<key>Host</key>
<string>exchange.domain.com</string>
<key>MailNumberOfPastDaysToSync</key>
<integer>3</integer>
<key>Password</key>
<string>{password}</string>
<key>PayloadDescription</key>
<string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
<key>PayloadDisplayName</key>
<string>Exchange</string>
<key>PayloadIdentifier</key>
<string>com.domain.enterprise.eas</string>
<key>PayloadOrganization</key>
<string>TipsHouse</string>
<key>PayloadType</key>
<string>com.apple.eas.account</string>
<key>PayloadUUID</key>
<string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>SSL</key>
<true/>
<key>UserName</key>
<string>domain\{userroot}</string>
</dict>
</array>
<key>PayloadDescription</key>
<string>Profile Test</string>
<key>PayloadDisplayName</key>
<string>TipsHouse</string>
<key>PayloadIdentifier</key>
<string>com.domain.enterprise</string>
<key>PayloadOrganization</key>
<string>Domain</string>
<key>PayloadRemovalDisallowed</key>
<false/>
<key>PayloadType</key>
<string>Configuration</string>
<key>PayloadUUID</key>
<string>A6B7D66D-1179-4E85-A005-4DAACD4EDF0F</string>
<key>PayloadVersion</key>
<integer>1</integer>
</dict>
</plist>

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.)
<dict>
<key>EmailAddress</key>
<string>[email protected]</string>
<key>Host</key>
<string>exchange.domain.com</string>
<key>MailNumberOfPastDaysToSync</key>
<integer>3</integer>
<key>Password</key>
<string>MyPassword</string>
<key>PayloadDescription</key>
<string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
<key>PayloadDisplayName</key>
<string>Exchange</string>
<key>PayloadIdentifier</key>
<string>com.domain.enterprise.eas</string>
<key>PayloadOrganization</key>
<string>TipsHouse</string>
<key>PayloadType</key>
<string>com.apple.eas.account</string>
<key>PayloadUUID</key>
<string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
<key>PayloadVersion</key>
<integer>1</integer>
<key>SSL</key>
<true/>
<key>UserName</key>
<string>domain\greghacke</string>
</dict>

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)}
 
J

Jesús Gabriel y Galán

7stud -- wrote in post #997885:

OK, here's the inbound:
<?xml version=3D"1.0" encoding=3D"UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version=3D"1.0">
<dict>
=A0<key>PayloadContent</key>
=A0<array>
=A0 =A0<dict>
=A0 =A0 =A0<key>EmailAddress</key>
=A0 =A0 =A0<string>{username}</string>
=A0 =A0 =A0<key>Host</key>
=A0 =A0 =A0<string>exchange.domain.com</string>
=A0 =A0 =A0<key>MailNumberOfPastDaysToSync</key>
=A0 =A0 =A0<integer>3</integer>
=A0 =A0 =A0<key>Password</key>
=A0 =A0 =A0<string>{password}</string>
=A0 =A0 =A0<key>PayloadDescription</key>
=A0 =A0 =A0<string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
=A0 =A0 =A0<key>PayloadDisplayName</key>
=A0 =A0 =A0<string>Exchange</string>
=A0 =A0 =A0<key>PayloadIdentifier</key>
=A0 =A0 =A0<string>com.domain.enterprise.eas</string>
=A0 =A0 =A0<key>PayloadOrganization</key>
=A0 =A0 =A0<string>TipsHouse</string>
=A0 =A0 =A0<key>PayloadType</key>
=A0 =A0 =A0<string>com.apple.eas.account</string>
=A0 =A0 =A0<key>PayloadUUID</key>
=A0 =A0 =A0<string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
=A0 =A0 =A0<key>PayloadVersion</key>
=A0 =A0 =A0<integer>1</integer>
=A0 =A0 =A0<key>SSL</key>
=A0 =A0 =A0<true/>
=A0 =A0 =A0<key>UserName</key>
=A0 =A0 =A0<string>domain\{userroot}</string>
=A0 =A0</dict>
=A0</array>
=A0<key>PayloadDescription</key>
=A0<string>Profile Test</string>
=A0<key>PayloadDisplayName</key>
=A0<string>TipsHouse</string>
=A0<key>PayloadIdentifier</key>
=A0<string>com.domain.enterprise</string>
=A0<key>PayloadOrganization</key>
=A0<string>Domain</string>
=A0<key>PayloadRemovalDisallowed</key>
=A0<false/>
=A0<key>PayloadType</key>
=A0<string>Configuration</string>
=A0<key>PayloadUUID</key>
=A0<string>A6B7D66D-1179-4E85-A005-4DAACD4EDF0F</string>
=A0<key>PayloadVersion</key>
=A0<integer>1</integer>
</dict>
</plist>

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.)
<dict>
=A0 =A0 =A0<key>EmailAddress</key>
=A0 =A0 =A0<string>[email protected]</string>
=A0 =A0 =A0<key>Host</key>
=A0 =A0 =A0<string>exchange.domain.com</string>
=A0 =A0 =A0<key>MailNumberOfPastDaysToSync</key>
=A0 =A0 =A0<integer>3</integer>
=A0 =A0 =A0<key>Password</key>
=A0 =A0 =A0<string>MyPassword</string>
=A0 =A0 =A0<key>PayloadDescription</key>
=A0 =A0 =A0<string>Configures device for use with Microsoft Exchange
ActiveSync services.</string>
=A0 =A0 =A0<key>PayloadDisplayName</key>
=A0 =A0 =A0<string>Exchange</string>
=A0 =A0 =A0<key>PayloadIdentifier</key>
=A0 =A0 =A0<string>com.domain.enterprise.eas</string>
=A0 =A0 =A0<key>PayloadOrganization</key>
=A0 =A0 =A0<string>TipsHouse</string>
=A0 =A0 =A0<key>PayloadType</key>
=A0 =A0 =A0<string>com.apple.eas.account</string>
=A0 =A0 =A0<key>PayloadUUID</key>
=A0 =A0 =A0<string>FE477C13-BF4F-4EA0-BE09-3968EC40C952</string>
=A0 =A0 =A0<key>PayloadVersion</key>
=A0 =A0 =A0<integer>1</integer>
=A0 =A0 =A0<key>SSL</key>
=A0 =A0 =A0<true/>
=A0 =A0 =A0<key>UserName</key>
=A0 =A0 =A0<string>domain\greghacke</string>
=A0 =A0</dict>

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 |
=A0 =A0 new_file.puts(File.open(user_file, 'r') do | original_file |
=A0 =A0 original_file.read.gsub(/\{username\}/, username)
=A0 =A0 end)
end
FileUtils.mv("/tmp/replaceable2.txt", user_file)

=A0text=3D File.read user_file
=A0File.open(user_file, 'w+'){|f| f << text.gsub(/\{username\}/,
username)}

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

variables =3D {"username" =3D> "Bob", "password" =3D> "pwd"}

file_contents =3D File.read("master_file").read
file_contents.gsub!(/\{(.*?)\}/) {|m| variables[$1]}

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

Jesus.
 
7

7stud --

You can also read the file line by line, and use a hash of replacements
with gsub:

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

pattern = '{.*?}'

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

7stud --

And here is a xml parsing solution:

require 'rexml/document'
include REXML

xml =<<ENDOFXML
#your xml here
ENDOFXML

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

pattern = '{.*?}'
doc = REXML::Document.new(xml)

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

puts doc.to_s


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

domain\{userroot}

A backslash has a special meaning in ruby strings. You need to change
that to a forward slash(or escape it).
 

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,769
Messages
2,569,582
Members
45,066
Latest member
VytoKetoReviews

Latest Threads

Top