Appending to the middle of a line in a textfile sed - sed

I am completely new to sed script. I have been researching how to add text to a file and managed to get the text I want adding to the correct line in the file but can not find a way to add it to the correct position!
so the line I have in the text file looks like this
listen_addresses = 'localhost, 192.0.0.0' # what IP address(es) to listen on;
I want to add an IP so the line looks like:
listen_addresses = 'localhost, 192.0.0.0, 192.0.0.0' # what IP address(es) to listen on;
Through trial and error I only have:
sed -i '/listen_addresses/ s/.*/&,192.0.0.0/' testfile
which gives:
listen_addresses = 'localhost, 192.0.0.0' # what IP address(es) to listen on; 192.168.0.0
how do I go about adding it to the correct position?

There are many ways. One of them could be to search for last ' and use parentheses to save the data matched. I changed single quotes to double quotes because I want to match one of them inside the regular expression:
sed -i "/listen_addresses/ s/^\(.*\)\('\)/\1, 192.0.0.0\2/" testfile
^\(.*\): Matches from the beginning until end of line (greeding).
\('\): Backtrack from the end until a '. So it will match the last one in the string.
\1: The content saved between first pair of parentheses.
, 192.0.0.0: Literal string.
\2: Content saved between second pair of parentheses.

Just replace the ' # part of the line:
sed -i "/listen_addresses/ s/' #/, 192.0.0.0' #/" testfile
Note that I used double quotes so the single quote can be easilly inserted.

Related

How can I search and replace for two characters with a string in between and only replace the latter character using sed?

Take the following string, for example:
some random text*
- sed is actually not that easy.*
other text
How can I search for lines containing both - and *, then replace the asterisk in that line with a string?
(For Example) Using the string "test" to replace the asterik in matched lines, the output would look this:
some random text*
- sed is actually not that easy.test
other text
I tried
sed -i '' 's/\- .*\*/\n\n:::\n/g';
But the problem with that is that it replaces the whole line, instead of just the asterisk.
If you want to match and keep the hyphen at the start of the line, and replace the asterix at the end of the line, you can use a capture group \(...\) for what you want to keep and use that group in the replacement with \1
sed -i 's/^\(- .*\)\*$/\1test/' file
The contents of file will be:
some random text*
- sed is actually not that easy.test
other text
If the characters are not directly at the start or end of the string, you can remove the anchors ^ and $

How to parse sed regex syntax?

sed -i "0,/test/s//#test/g" file.txt
I do not know how to parse this regex. It is commenting out test by putting #, but my questions are
what is "0," at the beginning?
what is it not like "s/test/#test/g" ? aka why is /s is in the middle?
Any help is appreciated.
Lets break it down into smaller pieces:
https://www.gnu.org/software/sed/manual/sed.html#sed-script-overview
sed commands follow this syntax:
[addr]X[options]
X is a single-letter sed command. [addr] is an optional line address. If [addr] is specified, the command X will be executed only on the matched lines.
And
https://www.gnu.org/software/sed/manual/sed.html#Range-Addresses
An address range can be specified by specifying two addresses separated by a comma (,). An address range matches lines starting from where the first address matches, and continues until the second address matches (inclusively)
In the case of 0,/test/s//#test/g the address part is 0,/test/ because s is the command. An address part of 0,/test/ means the s command is only executed on lines inside that range. If the sed command was s/test/#test/g there wouldn't be an address part and the s command would be attempted on every line in the file.
https://www.gnu.org/software/sed/manual/sed.html#index-addr1_002c_002bN
A line number of 0 can be used in an address specification like 0,/regexp/ so that sed will try to match regexp in the first input line too. In other words, 0,/regexp/ is similar to 1,/regexp/, except that if addr2 matches the very first line of input the 0,/regexp/ form will consider it to end the range, whereas the 1,/regexp/ form will match the beginning of its range and hence make the range span up to the second occurrence of the regular expression.
Note that this is the only place where the 0 address makes sense; there is no 0-th line and commands which are given the 0 address in any other way will give an error.
So in 0,/test/s//#test/g, the address part 0,/test/ runs the s command only on the first line that matches /test/ - even if it is the first line.
https://www.gnu.org/software/sed/manual/sed.html#index-empty-regular-expression
The empty regular expression ‘//’ repeats the last regular expression match (the same holds if the empty regular expression is passed to the s command).
So 0,/test/s//#test/g is the same as 0,/test/s/test/#test/g because the empty regular expression matches the one that was used in the address part - but it can be left out because writing the same regex twice just makes the whole command less readable.
In conclusion:
s/test/#test/g does the replacement on every line in the file that contains test
0,/test/s//#test/g does the replacement only on the first line in the file that contains test

sed match first word replace full line

I know this should be straight forward but I'm stuck, sorry.
I have two files both contain the same parameters but with different values. I'm trying to read one file line at a time, get the parameter name, use this to match in the second file and replace the whole line with that from file 1.
e.g. rw_2.core.fvbCore.Param.isEnable 1 (FVB_Params)
becomes
rw_2.core.fvbCore.Param.isEnable true (FVB_Boolean)
The lines are not always the same length but I always want to replace the whole line.
The code I have is as follows but it doesn't make the substitutions and I can't work out why not.
while read line; do
ParamName=`awk '{print $1}'`
sed -i 's/$ParamName.*/$line/g' FVB_Params.txt
done < FVB_Boolean.txt
You need your sed command within double quotes if you want those variables to be replaced with their values. You have single quotes, so sed is actually looking for strings with dollar signs to replace with the string '$line', not whatever your shell has in the $line variable.
In short, sed's not seeing the values you want. Switch to double quotes.

Removing quotes between two strings

I have a file I am processing and have gotten stuck format is as follows:
"HostRecord","SOMEFILE","InsideView","legacy.name.com","1.2.3.4","1.2.3.5","some comment",""
I need to remove the "" between the IP addresses, but leave the , so in the end I would have the output look like this:
"HostRecord","SOMEFILE","InsideView","legacy.name.com","1.2.3.4,1.2.3.5","some comment",""
I thought well I just use sed and have tried multiple things. My latest is something like this:
sed -e 's/\(.*[0-9]\{1,3\}=1&\)\"\(,=2&\)\"\([0-9]\{1,3\}=3&\)/\1\2\3/'
That doesn't seem to have any effect at all, not really picky on a particular tool to do the job, I just need to get it completed. Anyone have any thoughts?
Why not use something that has real CSV processing? Assuming you gather IP addresses until you get to something that doesn't look like an IP address, this should work:
ruby -rcsv -e '
cout = CSV.new $stdout, force_quotes: true
CSV.new(ARGF).each do |row|
out_row = row.shift(4)
ips = row.shift
while row[0] =~ /^\d+\.\d+\.\d+\.\d+$/
ips += ",#{row.shift}"
end
cout << out_row + [ ips ] + row
end' infile.csv >outfile.csv
Using a flag available in the substitution "s" command ,(like the common g,p, or d flag) we can only substitute a Nth occurence. So, for example:
<file sed 's/"//10'
will remove the 10th double quote ' " '.
<file sed -e 's/"//11' -e 's/"//10'
will remove the 11th quote and then the 10th quote on every line.
sed 's|\([0-9]\)","\([0-9]\)|\1,\2|g'
This will save each single digit [0-9] found surrounding a "," into two groups. We can then replace the pattern with the first group \1, followed by a comma ,, followed by the second group \2. We add the "g" flag which makes the substitute command work on every occurrence found on the line.
Let's say we have a file test_sed with the following lines:
"HostRecord","SOMEFILE","InsideView","legacy.name.com","1.2.3.4","1.2.3.5","some comment",""
"HostRecord","SOMEFILE","InsideView","legacy.name.com","1.2.3.4","1.2.3.5","10.10.10.10","192.168.1.12","some comment",""
Use two sed commands though pipeline sequence(output redirection):
cat test_sed | sed -re 's#"(([0-9]{1,3}\.){3}[0-9]{1,3})","(([0-9]{1,3}\.){3}[0-9]{1,3})"#"\1,\3"#gp;'
| sed -rn 's#,(([0-9]{1,3}\.){3}[0-9]{1,3})","(([0-9]{1,3}\.){3}[0-9]{1,3}),#,\1,\3,#gp;'
The output:
"HostRecord","SOMEFILE","InsideView","legacy.name.com","1.2.3.4,1.2.3.5,10.10.10.10,192.168.1.12","some comment",""
"HostRecord","SOMEFILE","InsideView","legacy.name.com","1.2.3.4,1.2.3.5,10.10.10.10,192.168.1.12","some comment",""
([0-9]{1,3}\.){3}[0-9]{1,3}) represents IP address

sed + match line and add word on the first string in line

I have the following file
more file
machine1 network netmask broadcast
how to add the "_NAME" after the first word in the line that have the "network netmask broadcast" words ?
remark sed also need to match only "network netmask broadcast" and then to add _NAME to the first word in the line
example: what I need to get after execute sed command
machine1_NAME network netmask broadcast
This should work, since we're not using the g modifier, it'll only match once per line
sed -e 's/^[^ ]*\>/&_NAME/'
It matches non-space characters up to the first word boundary, replacing it with itself and appends _NAME. As mentioned, without the g modifier it'll only match once per line, and if there are leading white space on the line, just remove the first ^-anchor.
Edit
you only wanted it to match on specific lines, so here goes:
sed -e '/network netmask broadcast$/s/^[^ ]*\>/&_NAME/'
The first part is a selector, which makes sure the substitution is only performed on lines where network netmask broadcast ends the line. To have it match any lines with those words, just remove the $-anchor, and add * (space-asterisk) to the spaces to make them flexible. But you probably already knew that.. :)
sed '/network.*netmask.*broadcast/s|^\([ \t]*\)\(.[^ \t]*\) |\1\2_NAME|' file