how to edit a line having IPv4 address using sed command - regex

I need to modify an ntp configuration file by adding some options to the line containing Ip addresses.
I have been trying it for so long using sed command, but no able to modify the line unless i don't know the IP addresses.
Let say, i have few lines as,
server 172.0.0.1
server 10.0.0.1
I need to add iburst option after the ip address.
I have tried command like.. sed -e 's/(\d{1,3}\.\d{1.3}\.\d{1,3}\.\d{1,3})/ \1 iburst/g' ntp_file
or sed -e 's/^server +\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/server \1\.\2\.\3\ iburst/g' ntp_file
but its not modifying the line. Any kind of suggestions would be really appriciated.

The regex you have used as POSIX BRE cannot match the expected strings due to \d shorthand class that sed does not support, the misused dot inside a range quantifier and incorrect escaping of grouping and range quantifier delimiters.
You may use
sed -E -i 's/[0-9]{1,3}(\.[0-9]{1,3}){3}/ & iburst/g' ntp_file
The POSIX ERE (enabled with the -E option) expression means to match
[0-9]{1,3} - one to three digits
(\.[0-9]{1,3}){3} - three occurrences of a dot and one to three digits
The replacement pattern is & iburst where & stands for the whole match.
The g flag replaces all occurrences.

Related

How to prefer use of an optional part of a regular expression?

Is there a way of telling a regular expression (specifically sed) to prefer using an optional component when the input also matches without using that component?
I'm trying to extract a number from a string that may optionally be preceded by prefix. It works in the following cases:
echo dummy/123456/dummy | sed "s:.*/\(prefix\)\?\([0-9]\{3,\}\)/.*:\2:"
123456
echo dummy/prefix123456/dummy | sed "s:.*/\(prefix\)\?\([0-9]\{3,\}\)/.*:\2:"
123456
but if the string contains both a prefixed number and a "bare" number, it choses the bare number:
echo dummy/prefix123456/987654/dummy | sed "s:.*/\(prefix\)\?\([0-9]\{3,\}\)/.*:\2:"
987654
Is there a way of forcing sed to prefer the match including the prefix (123456)? All search results I've found talk of greedy/lazy options, which – as far as I can tell – don't apply here.
Clarifications
The dummy portions in the examples above may contain slashes.
The bit I'm interested in is either the first slash-delimited run of three or more digits (.../123456/...) or the first slash-delimited run of 3+ digits with a prefix (.../prefix123456/...), whichever occurs first.
You may try this sed command:
sed '
/.*\/prefix\([0-9]\{3,\}\)\/.*/{
s//\1/
b
}
s/.*\/\([0-9]\{3,\}\)\/.*/\1/
' file
which will print out
123456
123456
123456
123456
where the content of file is
dummy/123456/dummy
dummy/prefix123456/dummy
dummy/prefix123456/987654/dummy
dummy/987654/prefix123456/dummy
With GNU awk you could try following code. Written and tested with shown samples only.
awk 'match($0,/\/(prefix){0,1}([0-9]+)/,arr){print arr[2]}' Input_file
Explanation: Simple explanation would be, using GNU awk's match function. In it using regex (prefix){0,1}([0-9]+) which is having 2 capturing groups and its matched values are getting stored into array named arr and if condition is fine then printing 2nd element of that array.
sed BRE or ERE doesn't have a way to use lazy quantifier in starting .*?.
However, based on your use-cases, you may use this sed:
sed -E 's~[^/]*/(prefix){0,1}([0-9]{3,})/.*~\2~' file
123456
123456
123456
where input is:
cat file
dummy/123456/dummy
dummy/prefix123456/dummy
dummy/prefix123456/987654/dummy
Here we are using negated character class (bracket expression) [^/]* instead of .* to allow pattern to match 0 or more of any char that is not a /.
If you can consider perl then .*? with a negative lookahead will work for you:
perl -pe 's~^.*?/(?:prefix)?(\d{3,})(?!.*prefix\d{3}).*~$1~' file
RegEx Demo

sed remove first two octets from ip

I have a file that contains a bunch of ip addresses. I would like for my sed command to remove the first two octets of these.
ex. 172.0.0.1 should be changed to XXX.ZZZ.0.1
What I have now remove the lasts octets and replace it with an x!
sed -i 's/\(\([0-9]\{1,3\}\.\)\{3\}\)[0-9]\{1,3\}/\1XXX/g' "${file}"
This is run in a bash scripts that takes the file containing the ip addresses as input param.
I haven't been able to figure out how to do this with sed yet so I'll appreciate any help.
It seems you may use
sed -i.bak -E 's/\b([0-9]{1,3}\.){2}([0-9]{1,3}\.[0-9]{1,3})\b/XXX.ZZZ.\2/g' "${file}"
The -E (or -r on other OSes) enables the POSIX ERE syntax that allows using fewer escapes in the pattern (no need to escape grouping ( and ) symbols and the limiting/range quantifier {n,m}).
Details
\b - word boundary
([0-9]{1,3}\.){2} - two occurences of (Group 1) one to three digits and a dot
([0-9]{1,3}\.[0-9]{1,3}) - (Group 2) one to three digits, a . and again 1 to 3 digits
\b - word boundary
The whole match is replaced with Group 2 value (\2). The -i.bak will make the replacements in file, and an original copy with a .bak extension will be generated.
See the online demo.

Property File with Sed regex - Ignore first character for match

I have a test property file with this in it:
-config.test=false
config.test=false
I'm trying to, using sed, update the values of these properties whether they have the - in front of them or not. Originally I was using this, which worked:
sed -i -e "s/#*\(config.test\)\s*=\s*\(.*\)/\1=$(echo "true" | sed -e 's/[\/&]/\\&/g')/" $FILE_NAME
However, since I was basically ignoring all characters before the match, I found that when I had properties with keys that ended in the same value, it'd give me problems. Such as:
# The regex matches both of these
config.test=true
not.config.test=true
Is there a way to either ignore the first character for a match or ignore the initial - specifically?
EDIT:
Adding a little clarification in terms of what I'd want the regex to match:
config.test=false # Should match
-config.test=false # Should match
not.config.test=false # Should NOT match
sed -E 's/^(-?config\.test=).*/\1true/' file
? means zero or 1 repetitions of so it means the - can be present or not when matching the regexp.
I found some solution for a regex of a specific length instead of ignoring the first character with sed and awk. Sometimes the opposite does the same by an easier way.
If you only have the alternative to use sed I have two workaround depending on your file.
If your file looks like this
$ cat file
config.test=false
-config.test=false
not.config.test=false
you can use this one-liner
sed 's/^\(.\{11,12\}=\)\(.*$\)/\1true/' file
sed is looking at the beginning ^ of each line and is grouping \( ... \) for later back referencing every character . that occurs 11 or 12 times \{11,12\} followed by a =.
This first group will be replaced with the back reference \1.
The second group that match every character after the = to the end of line \(.*$\) will be dropped. Instead of the second group sed replaces with your desired string true.
This also means, that every character after the new string true will be chopped.
If you want to avoid this and your file looks like
$ cat file
config.test=true # Should match
-config.test=true # Should match
not.config.test=false # Should NOT match
you can use this one-liner
sed 's/^\(.\{11,12\}=\)\(false\)\(.*$\)/\1true\3/' file
This is like the example before but works with three groups for back referencing.
The content of the former group 2 is now in group 3. So no content after a change from false to true will be chopped.
The new second group \(false\) will be dropped and replaced by the string true.
If your file looks like in the example before and you are allowed to use awk, you can try this
awk -F'=' 'length($1)<=12 {sub(/false/,"true")};{print}'
For me this looks much more self-explanatory, but is up to your decision.
In both sed examples you invoke only one time the sed command which is always good.
The first sed command needs 39 and the second 50 character to type.
The awk command needs 52 character to type.
Please tell me if this works for you or if you need another solution.

Sed command and regular expressions

I need to change 'bind a.b.c.d:80' to 'bind x.b.f.d:80'. I wrote below command for this purpose but it is not working, i don't know why?
sed -i 's,bind *:35357,bind x.y.z.a:35357,' haproxy-sample.cfg
Any help would be appreciated.
Thanks in advance.
Executed from bash
$ echo bind 12.54.36.165:35357 | sed "s/bind [^:]\+:35357/bind a.y.z.a:35357/"
bind a.y.z.a:35357
I think you need somethink like this:
sed -r 's/^(bind\s+)([^.]+)\.([^.]+)\.([^.]+)\.([^.:]+)(:80(\s+.*)?)$/\1x.\3.f.\5\6/g' haproxy-sample.cfg
where you have to replace the literal .f. and x. with your required strings ( in the second part of the sed s).
It capture the various parts (bind, the four numbers of the address, the :80 up to lineend into \1 to \6 and recombine the captured strings with the b and f .
The problem is your pattern contains a lone * -- you're probably thinking about shell globbing patterns where * matches 0 or more of any characters. sed does not speak shell patterns, it speaks regular expressions.
Where you have just *, you need .* -- in a regular expression the . character is a wildcard that matches any one character, and * is a quantifier that matches zero or more of the preceding thing.
sed -i 's/\(bind \).*\(:35357\)/\1x.y.z.a\2/'

Find and replace using regex in sed

I want to replace
# Bulk Delete #
with
=== Bulk Delete ===
I am using the following sed command.
sed "s/#\([^#]*\)#/===\1===/g" filename
It works, but it also replaces
### Translation
with
======# Translation
How to prevent it and make it work in both mac and ubuntu?
The easiest way is to match the blanks too:
sed 's/# \([^#]*\) #/=== \1 ===/g' filename
Another way is to require multiple (one or more) non-hashes between the two hashes:
sed 's/#\([^#]\{1,\}\)#/===\1===/g' filename
The \{n,m\} is a quantifier notation that requires at least n occurrences and at most m occurrences of the pattern immediately before it, so it is generalization of the ?, * and + metacharacters (which can be represented by \{0,1\}, \{0,\}, and \{1,\} respectively). If m is missing, it means any number not smaller than n; if n is missing, it means any number not larger than m, and so is equivalent to 0. In my example, I'm using it as the classic, portable (to prehistoric versions of sed off Linux and Mac OS X) version of +.
Another way of writing that is:
sed 's/#\([^#][^#]*\)#/===\1===/g' filename
And you can combine the ideas, of course:
sed 's/# \([^#][^#]*\) #/=== \1 ===/g' filename
sed 's/# \([^#]\{1,\}\) #/=== \1 ===/g' filename
You could use + to enforce at least one character which is not a #
Since the sed in OSX does not support the enhanced regular expression syntax like + by default, you need to pass the -E flag to sed. And the good news is -E flag works well on *nix systems too. When using the -E flag, you can skip escaping the special regex characters like +, (, etc.
sed -E "s/#([^#]+)#/===\1===/g" filename