I'm having some problems with the following expression from a bash script:
ipreg=^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$
sed -i 's/\"https:\/\/'$ipreg':9999\",/\"https:\/\/'${newip}':9999\",/g' /usr/share/config.file
Sed can't find the old Ip address with the var ipreg, but if i put in the exact address it does get replaced with the new IP.
I think you are making this too complex. I don't see a reason to match IPv4 addresses so tightly.
Why not simplify it a bit like this:
sed -i "s|https://[0-9.]*:9999|https://$newip:9999|" /usr/share/config.file
Also, you probably don't want anchors in your regex, since it's in the middle of a string. ^ matches the beginning of a line and $ matches the end - you don't want those in the middle of your match pattern.
Many sed dialects do not support {1,3} repetition. Try with backslashes before the curlies, and/or see if your sed has an -r or -E option to enable Extended Regular Expression (ERE) syntax.
In the worst case, just write the repetitions in longhand, or maybe simplify them to just * if you can live with the rather remote possibility for false positives.
As an aside, you should use quotes aroind anything which contains shell metacharacters.
This could work, taking into account the comment of tripleee.
$ cat config.file
.. / "https://10.10.10.10:9999";, /* #scratch /configuration/config.js/5 * * ==== default_route *
$ cat changeip.sh
#!/bin/bash
ipreg='[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
newip='127.0.0.1'
sed -ri 's/"https:\/\/'"${ipreg}"':9999"/"https:\/\/'"${newip}"':9999"/' "$1"
cat "$1"
$ ./changeip.sh config.file
.. / "https://127.0.0.1:9999";, /* #scratch /configuration/config.js/5 * * ==== default_route *
Does the IP occur more than once? Only than you will need the global option.
Related
I have a string in text file where i want to replace the version number. Quotation marks can vary from ' to ". Also spaces around = can be there and can be not as well:
$data['MODULEXXX_VERSION'] = "1.0.0";
For testing i use
echo "_VERSION'] = \"1.1.1\"" | sed "s/\(_VERSION.*\)[1-9]\.[1-9]\.[1-9]/\11.1.2/"
which works perfectly.
When i change it to search in the file (the file has the same string):
sed "s/\(_VERSION.*\)[1-9]\.[1-9]\.[1-9]/\11.1.2/" -i test.php
, it does not find anything.
After after playing with the search part of regex, i found one more odd thing:
sed "s/\(_VERSION.*\)[1-9]\./\1***/" -i test.php
works and changes the string to $data['MODULEXXX_VERSION'] = "***0.0";, but
sed "s/\(_VERSION.*\)[1-9]\.[1-9]/\1***/" -i test.php
does not find anything anymore. Why?
I am using Ubuntu 17.04 desktop.
Anyone can explain what am I doing wrong? What would be the best command for replacing version numbers in the file for the string $data['MODULEXXX_VERSION'] = "***0.0";?
The main problem is that [1-9] doesn't match the 0s in the version number. You need to use [0-9].
Besides that, you may use the following sed command:
sed -r 's/(.*_VERSION['\''"]]\s*=\s*).*/\1"1.0.1";/' conf.php
This doesn't look at the current value, it simply replaces everything after the =.
I've used -r which enables extended posix regular expressions which makes it a bit simpler to formulate the pattern.
Another, probably cleaner attempt is to store the conf.php as a template like conf.php.tpl and then use a template engine to render the file. Or if you really want to use sed, the file may look like:
$data['FOO_VERSION'] = "FOO_VERSION_TPL";
Then just use:
sed 's/FOO_VERSION_TPL/1.0.1/' conf.php.tpl > conf.php
If there are multiple values to replace:
sed \
-e 's/FOO/BAR/' \
-e 's/HELLO/WORLD/' \
conf.php.tpl > conf.php
But I recommend a template engine instead of sed. That becomes more important when the content of the variables to replace may contain characters special to 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/'
I want to substitute a String from a file which is:
# - "server1"
My first attempt was something like this:
sed -i 's/#\ -\ "\server1"\.*/ChangedWord/g' file
But I get an error if I try it like this.
So there is to be another way to handle whitespaces, I guess I have to use \s or [[:space:]]. But for some how I am not able to make it work.
I think you are complicating the expression too much. This should be enough:
sed 's/^#[[:space:]]*-[[:space:]]*"server1".*/ChangedWord/' file
It looks for those lines starting with # followed by 0 to n spaces, then "server1" and then anything. In such case, it replaces the line with ChangedWord.
Note I am using [[:space:]] to match the spaces, since it is a more compatible way (thanks Tom Fenech in comments).
Note also there is no need to use g in the sed expression, because the pattern can occur just once per line.
Test
$ cat a
hello
# - "server1"
hello# - "server1"
$ sed 's/^#[[:space:]]*-[[:space:]]*"server1".*/ChangedWord/' a
hello
ChangedWord
hello# - "server1"
The actual fault was the missing escaping from the double quotes:
ssh -i file root#IP sed 's/^#[[:space:]]*-[[:space:]]*\"server1\".*/ChangedWord/' file
That did it for me. Thanks for all your support
rghome is right, you don't need those backslashes in front of spaces as the expression is wrapped in quotes. In fact, they're causing the error: sed is telling you that \<Space> is not a valid option. Just remove them and it should work as expected:
sed -i 's/# - "server1"/ChangedWord/' file
I have a text file infile1 with 1,000's of lines.
I wish to use sed to extract the occuring instances of a regex pattern match to outfile2.
NB
Each instance of the regex pattern match may occur more than once on each line of infile1.
Each instance of the extracted regex pattern should be printed to a new line in outfile2.
Does anyone know the syntax within sed to place the regex into?
ps the regex pattern is
\(Google[ ]{1,3}“[a-zA-Z0-9 ]{1,100}[., ]{0,3}”\)
Thank you :)
I think you want
grep -oE 'Google[ ]{1,3}"[a-zA-Z0-9 ]{1,100}[., ]{0,3}"' filename
-o tells grep to print only the matches, each on a line of its own, and -E instructs it to interpret the regex in extended POSIX syntax, which your regex appears to be.
Note that [ ] could be replaced with just a space, and you might want to use [[:alnum:] ] instead of [a-zA-Z0-9 ] to cover umlauts and suchlike if they exist in the current locale.
Addendum: It is also possible to do this with sed. I don't recommend it, but you could write (using GNU sed):
sed -rn 's/Google[ ]{1,3}"[A-Za-z0-9 ]{1,100}[., ]{0,3}"/\n&\n/g; s/[^\n]*\n([^\n]*\n)/\1/g; s/\n[^\n]*$//p' filename
To make this work with older versions of BSD sed, use -En instead of -rn. -r and -E enable extended regex syntax. -r was historically used by GNU sed, -E by BSD sed; newer versions of them support both for compatibility. -n disables auto-printing.
The code works as follows:
# mark all occurrences of the regex by circumscribing them with newlines
s/Google[ ]{1,3}"[A-Za-z0-9 ]{1,100}[., ]{0,3}"/\n&\n/g
# Isolate every other line from the pattern space (the matches). This will
# leave the part behind the last match...
s/[^\n]*\n([^\n]*\n)/\1/g
# ...so we remove it afterwards and print the result of the transformation if it
# happened (the s///p flag does that). The transformation will not happen if
# there were no matches in the line (because then no newlines will have been
# inserted), so in those cases nothing will be printed.
s/\n[^\n]*$//p
It can be done with sed too, but it isn't pretty:
sed -n ':start /foo/{ h; s/\(foo\).*/\1/; s/.*\(foo\)/\1/; p; g; s/foo\(.*\)/\1/; b start; }' infile1 >outfile2
-- provided that you replace the four occurences of foo above with your pattern Google {1,3}“[a-zA-Z0-9 ]{1,100}[., ]{0,3}”.
Yeah, I told you it isn't pretty. :)
I have a shell variable:
all_apk_file="a 1 2.apk x.apk y m.apk"
I want to replace the a 1 2.apk with TEST, using the command:
echo $all_apk_file | sed 's/(.*apk ){1}/TEST/g'
The .*apk means end with apk, {1} means only match one time, but it doesn't work; I only got the original variable as output: a 1 2.apk x.apk y m.apk
Can anyone tell me why?
First, to enable the regular expressions you're familiar with in sed, you need to use the -r switch (sed -r ...):
echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/g'
# returns TESTy m.apk
Look at what it returns: TESTy m.apk. This is because the .* is greedy, so it matches as much as it possibly can. That is, the .* matches a 1 2.apk x, and you've said you want to replace .*apk, being a 1 2.apk x.apk with 'TEST', resulting in TESTy m.apk (note the following space after the '.apk' in your regular expression, which is why the match doesn't extend all the way to the last '.apk', which has no space following it).
Usually one could change the .* to .*? to make it non-greedy, but this behaviour is not supported in sed.
So, to fix it you just have to make your regex more restrictive.
It is hard to tell what you want to do - remove the first three words where the third ends in '.apk' and replace with 'TEST'? In that case, one could use the regular expression:
[a-z0-9]+ +[a-z0-9]+ +[a-z0-9]+\.apk
in combination with the 'i' switch (case insensitive).
You will have to give your logic for deciding what to remove (first three words, any number of words up to the first '.apk' word, etc) in order for us to help you further with the regex.
Secondly, you've put the 'g' switch in your regex. This means that all matching patterns will be replaced, and you seem to only want the first to be replaced. So remove the 'g' switch.
Finally, all of thse in combination:
echo $all_apk_file | sed -r 's/[a-z0-9]+ +[a-z0-9]+ +[a-z0-9]+\.apk/TEST/i'
# TEST x.apk y m.apk
This might work for you:
echo "$all_apk_file" | sed 's/apk/\n/;s/.*\n/TEST/'
TEST x.apk y m.apk
As to why your regexp did not work see #mathematical.coffee and #Jonathan Leffler's excellent explanations.
s/apk/\n/ is synonymous with s/apk/\n/1 which means replace the first occurence of apk with \n. As sed uses the \n as a record separator we know that it cannot occur in any initial strings passed to the sed commands. With these two facts under our belts we can split strings.
N.B. If you wanted to replace upto the second apk then s/apk/\n/2 would fit the bill. Of course for the last occurence of apk then .*apk comes into play.
One part of the problem is that in regular sed, the () and {} are ordinary characters in patterns until escaped with backslashes. Since there are no parentheses in the variable's value, the regex never matches. With GNU sed, you can also enable extended regular expressions with the -r flag. If you fix that problem, you will then run into the problem that .* is greedy, and the g modifier actually doesn't change anything:
$ echo $all_apk_file | sed 's/\(.*apk \)\{1\}/TEST/g'
TESTy m.apk
$ echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/g'
TESTy m.apk
$ echo $all_apk_file | sed -r 's/(.*apk ){1}/TEST/'
TESTy m.apk
$
It only stops there because there isn't a space after m.apk in the echoed value of the variable.
The issue now is: what is it that you want replaced? It sounds like 'everything up to and including the first occurrence of apk at the end of a word. This is probably most easily done with trailing context or non-greedy matching as found in Perl regular expressions. If switching to Perl is an option, do so. If not, it is not trivial in normal sed regular expressions.
$ echo $all_apk_file | sed 's/^[^.]* [^.][^.]*\.apk /TEST /'
TEST x.apk y m.apk
$
This looks for anything without dots in it, followed by a blank, followed by no dots again, and .apk; this means that the first dot allowed is the one in 2.apk. It works for the sample data; it would not work if the variable contained:
all_apk_file="a 1.2 2.apk m.apk y.apk 37"
You'll need to tune this to meet your requirements.