Managing a configuration file using sed via shell script - shell

I need to manage a configuration file in Linux using a shell script, that will read and write settings to and from the cfg file. I have following function to write a new value of a key in existing config file used in my script.
set_setting() {
sed -i "s/$1=[^ ]*/$1=$2/" $3
}
Usage:
set_setting keyname NewValue /home/Config.cfg
Thus, this would replace the value of keyname in Config.cfg to NewValue.
I also have a function get_setting which uses source command to read the config file. It accepts similarly two parameters, keyname and the source config file, and returns the value of the supplied key.
Now the problem is that, I have config file as follows.
name=Abc
surname=Xyz
And when I call the set_setting function as set_setting name Def /home/Config.cfg, ideally it should change value of name from Abc to Def, but along with changing value of key name, it also changes the value of surname from Xyz to Def resulting into following in config file.
name=Def
surname=Def
I suspect this is due to having the term "name" common in both keys, as when I changed the key surname to surnames, it worked as expected and only value of name was changed. Also note that, the value for the key can be anything except the space.
I'm new to shell scripting and recently discovered sed command.
I've also refered to SO questions this and this. While both of them were quite helpful, I'm stuck with above mentioned situation.

You should anchor your match to the beginning of the line:
sed -i "s/^$1=[^ ]*/$1=$2/" $3
Note the ^ in front of $1. This regex operator anchors the expression to the beginning of the line, effectively requiring the name being everything from the first character to the = sign. There's also an end-of-line anchor called $ (which you do not need here).

Related

How to add bash variable to text file after matched keywords?

I have a file(yaml) with the term "name:" and I would like to add variable($var) from bash and insert into yaml. I am able to find the key words and add variable after that:
sed -i "s/name:/& $var/" "yaml file"
However the variable keep added up in yaml file, as name: abc def ghi(I would like to have single name only)
How to fix it? also how can I add some text after $var, something like "$var-role"?
Thanks.
You need to replace the whole line, not just name:. So add .* to the regexp to match everything after it on the line.
sed -i "s/name:.*/name: $var/" "yaml file"
You can't use & in this version because that would include the rest of the line as well.
If you want to add more text, just put it after the variable.
sed -i "s/name:.*/name: ${var}-role/" "yaml file"
Put {} around the variable name to separate it from the following text (not really needed when the text begins with -, but would be needed if it began with a character that can be part of a variable name).

Replace literal at HTML using Unix shell script

I have a series of scripts declared in an HTML with the following format:
xxx.jfhdskfjhdskjfhdskjfjioe3874.bundle.js
The part between the periods is a dynamic hash, but it will always be an alphanumeric with the same positions. My problem is that I need to dynamically modify that hash, with the new generated files, which are in the same directory as the HTML itself. Is there a clean way to do it in Unix with a script?
You must be more specific: You want to generate new hashes for all scripts in this directory or need just a tool to change one by one basis? where do you get new hashes from? Below I attached simple script to change the part between first and second period sign. Script should be called with old name as first argument and new hash as second argument. It could be compressed to just one line but I used variables for clarity.
#! /bin/sh
OLDNAME=$1
NEWHASH=$2
NEWNAME=$(printf "%s" "$OLDNAME" | sed "s/^\([^\.]*\)\.[^\.]*\.\(.*\)/\1\.$NEWHASH\.\2/")
echo $NEWNAME

Assign BASH variable from file with specific criteria

A config file that the last line contains data that I want to assign everything to the RIGHT of the = sign into a variable that I can display and call later in the script.
Example: /path/to/magic.conf:
foo
bar
ThisOption=foo.bar.address:location.555
What would be the best method in a bash shell script to read the last line of the file and assign everything to the right of the equal sign? In this case, foo.bar.address:location.555.
The last line always has what I want to target and there will only ever be a single = sign in the file that happens to be the last line.
Google and searching here yielded many close but non-relative results with using sed/awk but I couldn't come up with exactly what I'm looking for.
Use sed:
variable=$(sed -n 's/^ThisOption=//p' /path/to/magic.conf)
echo "The option is: $variable")
This works by finding and removing the ThisOption= marker at the start of the line, and printing the result.
IMPORTANT: This method absolutely requires that the file be trusted 100%. As mentioned in the comments, anytime you "eval" code without any sanitization there are grave risks (a la "rm -rf /" magnitude - don't run that...)
Pure, simple bash. (well...using the tail utility :-) )
The advantage of this method, is that it only requires you to know that it will be the last line of the file, it does not require you to know any information about that line (such as what the variable to the left of the = sign will be - information that you'd need in order to use the sed option)
assignment_line=$(tail -n 1 /path/to/magic.conf)
eval ${assignment_line}
var_name=${assignment_line%%=*}
var_to_give_that_value=${!var_name}
Of course, if the var that you want to have the value is the one that is listed on the left side of the "=" in the file then you can skip the last assignment and just use "${!var_name}" wherever you need it.

Using sed to find and replace recursively

I am using a chef recipe to update a configuration file on my node.The contents of the file look something like follows:
server server1.domain.com
server server2.domain.com
I have a ruby array defined in my attribute file as follows:
default['servers'] = %w(xyz.domain.com abc.domain.com)
I want to use sed recursively to replace the server values in the file, such that my file is updated as such:
server xyz.domain.com
server abc.domain.com
I tried the following ruby loop in my recipe:
(node['servers']).each_with_index do |ntserver,index|
bash "server set" do
code <<-EOH
sed -i 's|server .*|server #{node['servers'].at(index)}|' /etc/ntp.conf
EOH
end
end
But after the chef-client is ran and the changes are applied respectively, the contents of configuration file are as follows:
server abc.domain.com
server abc.domain.com
I am new to sed command so can't figure out where i'm going wrong.
Any help will be appreciated.
By design you should not modify files with Chef. Instead you overwrite the whole file with cookbook_file resource or, if you need to insert some dynamic values into the file, with template resource.
The sed command (the way you use it) is quite simple; it only performs (inplace in the given file due to the -i option) a substitution of each string matching the pattern server .* by the string server #{node['servers'].at(index)}. It does this throughout the whole file, so each loop changes all occurrences in the whole file.
What bothers me is that you write that in the original version you've got server1.domain.com but in the pattern you've got server .* (meaning server, followed by a space , and any amount of other characters .*). Because of the space, this should not match anything, so nothing should be changed at all. But maybe you just put that space in there by mistake when posting your question. I'll assume that there was no such space in your actual code because this way it would fit the observed phenomenon.
So, to change only one line at a time, you should have a counter in your loop and have the number of the iteration in the search pattern, so that it is server1.* for the first iteration, server2.* for the second and so on. Then each iteration will change only exactly one line and you should get your required result.

Concatenating strings fails when read from certain files

I have a web application that is deployed to a server. I am trying to create a script that amoing other things reads the current version of the web application from a properties file that is deployed along with the application.
The file looks like this:
//other content
version=[version number]
build=[buildnumber]
//other content
I want to create a variable that looks like this: version-buildnumber
Here is my script for it:
VERSION_FILE=myfile
VERSION_LINE="$(grep "version=" $VERSION_FILE)"
VERSION=${VERSION_LINE#$"version="}
BUILDNUMBER_LINE=$(grep "build=" $VERSION_FILE)
BUILDNUMBER=${BUILDNUMBER_LINE#$"build="}
THEVERSION=${VERSION}-${BUILDNUMBER}
The strange thing is that this works in some cases but not in others.
The problem I get is when I am trying to concatenate the strings (i.e. the last line above). In some cases it works perfectly, but in others characters from one string replace the characters from the other instead of being placed afterwards.
It does not work in these cases:
When I read from the deployed file
If I copy the deployed file to another location and read from there
It does work in these cases:
If I write a file from scratch and read from that one.
If I create my own file and then copy the content from the deployed file into my created file.
I find this very strange. Is there someone out there recognizing this?
It is likely that your files have carriage returns in them. You can fix that by running dos2unix on the file.
You may also be able to do it on the fly on the strings you're retrieving.
Here are a couple of ways:
Do it with sed instead of grep:
VERSION_LINE="$(sed -n "/version=/{s///;s/\r//g;p}" $VERSION_FILE)"
and you won't need the Bash parameter expansion to strip the "version=".
OR
Do the grep as you have it now and do a second parameter expansion to strip the carriage return.
VERSION=${VERSION_LINE#$"version="}
VERSION=${VERSION//$'\r'}
By the way, I recommend habitually using lowercase or mixed case variable names in order to reduce the chance of name collisions.
Given this foo.txt:
//other content
version=[version number]
build=[buildnumber]
//other content
you can extract a version-build string more easily with awk:
awk -F'=' '$1 == "version" { version = $2}; $1 == "build" { build = $2}; END { print version"-"build}' foo.txt
I don't know why your script doesn't work. Can you provide an example of erroneous output?
From this sentence:
In some cases it works perfectly, but in others characters from one string replace the characters from the other instead of being placed afterwards.
I can't understand what's actually going on (I'm not a native English speaker so it's probably my fault).
Cheers,
Giacomo

Resources