SED one liner to uncomment and replace first occurrence of a pattern - regex

I have this settings.conf file in linux defined as follows:
Section A
first-setting = value
# second-setting = off
third-setting = value
Section B
first-setting = value
# second-setting = off
third-setting = value
I would like to uncomment # second-setting = off of Section A only (first occurrence), and set the value to on.
So far, I have this:
cat settings.conf | sed '/^# second.*/ {s/^#//;s/off/on/}'
Any tips?

Is this what you had in mind?
sed '0,/#/s/#\(.*\) off/\1 on/' settings.conf
Or if your on osx with non-gnu sed:
sed '1,/#/s/#\(.*\) off/\1 on/' settings.conf

sed -e '1,/^Section B/s/# second-setting = off/ section-setting = on/' settings.conf

Related

Finding a string with sed, then replacing a number within that string with incremented number

I have a file that contains somehting like this:
[project]
name = "sinntelligence"
version = "1.1.dev12"
dependencies = [
"opencv-python",
"matplotlib",
"PySide6",
"numpy",
"numba"
]
Now I want to find the "version" string and increment the last number after "dev". Thus in the above example I would like to change
version = "1.1.dev12"
to
version = "1.1.dev13"
and so forth. With grep I was able to get this line with this regular expression:
grep -P "^version.*dev[0-9]+"
But since I want to replace something in a file I thought it would make more sense to use sed instead. However, with sed I don't even find that line (i.e. nothing is replaced) with this:
sed -i "s/^version.*dev[0-9]+/test/g" sed-test.txt
Any ideas 1) what I am doing wrong here with sed and 2) how can increase that "dev" number by one and write that back to the file (with just typical Ubuntu Linux command line tools)?
You used grep with -P option that enables the PCRE regex engine, and with sed you are using a POSIX BRE pattern. That is why you do not even match that line.
Then, with sed, you won't be able to easily eval and change the number, you can do that with perl:
perl -i -pe 's/^version.*dev\K(\d+)/$1 + 1/e' sed-test.txt
See the online demo:
#!/bin/bash
s='[project]
name = "sinntelligence"
version = "1.1.dev12"
dependencies = [
"opencv-python",
"matplotlib",
"PySide6",
"numpy",
"numba"
]'
perl -pe 's/^version.*dev\K(\d+)/$1 + 1/e' <<< "$s"
Output:
[project]
name = "sinntelligence"
version = "1.1.dev13"
dependencies = [
"opencv-python",
"matplotlib",
"PySide6",
"numpy",
"numba"
]
what I am doing wrong here with sed
You have to use -E option to enable extended regular expressions:
$ sed -E "s/^version.*dev[0-9]+/test/g" sed-test.txt
[project]
name = "sinntelligence"
test"
dependencies = [
"opencv-python",
"matplotlib",
"PySide6",
"numpy",
"numba"
]
how can increase that "dev" number by one and write that back to the
file (with just typical Ubuntu Linux command line tools)?
I'd use awk, below is the adaptation of solution in this Ed Morton's
answer:
awk -i inplace '/^version/ {split($3,lets,/[0-9]+"$/,digs); $3=lets[1] digs[1]+1 "\""} 1' sed-test.txt

Sed substitution with Apex character

I'm trying to implement the following substitution
sed -i 's/$config['default_host'] = '';/$config['default_host'] = 'localhost';/' /etc/roundcube/config.inc.php
but it's not working.
What i want to do to is replace $config['default_host'] = ''; with $config['default_host'] = 'localhost'; inside the file /etc/roundcube/config.inc.php
Any ideas?
You should escape the special characters, because sed consider $ as a end of the character in a line
sed "s/\$config\['default_host'\] = '';/\$config['default_host'] = 'localhost';/" fileName
Using Grouping concept
sed "s/\(\$config\['default_host'\] = \)'';/\1'localhost';/" fileName
Output:
$config['default_host'] = 'localhost';

How do I replace this text with quotation marks?

I want to replace
$rcmail_config['default_host'] = '';
in /var/lib/roundcube/config/main.inc.php
with
$rcmail_config['default_host'] = 'localhost';
I've tried:
sed -i "s/$rcmail_config['default_host'] = '';/$rcmail_config['default_host'] = 'localhost';/g" /var/lib/roundcube/config/main.inc.php
and
sed -i s/$rcmail_config['default_host'] = '';/$rcmail_config['default_host'] = 'localhost';/g /var/lib/roundcube/config/main.inc.php
But it does not work.
What could I try next?
You need to escape the $ and [ symbols and also you don't need to repeat the same string in the replacement part. Instead of this, you may use capturing groups.
sed -i "s/\(\$rcmail_config\['default_host'\] = \)'';/\1'localhost';/g" file

How to trim lines includes specific string with sed

I have separate files include path string for each like ;
path = /aaa/bbb/ccc.com/user#ccc.com/dddd/user#yahoo.com/
path = /aaa/bbb/ccc.com/user#ccc.com/dddd/user#hotmail.co.uk/
path = /aaa/bbb/ccc.com/user#ccc.com/dddd/user#abc.xxx.co.uk/
path = /aaa/bbb/ccc.com/user#ccc.com/dddd/user55#ccc.com/
what i want to trim lines like;
path = /aaa/bbb/ccc.com/user/dddd/.user#yahoo/
path = /aaa/bbb/ccc.com/user/dddd/.user#hotmail/
path = /aaa/bbb/ccc.com/user/dddd/.user#abc/
path = /aaa/bbb/ccc.com/user/dddd/.user55#ccc.com/
I am almost be able to achieve with below (all strings are in separate files but at the 15th line)
sed -r '15s!#[^/]+(/[^/]+/[^.#]+#[^.]+).*$!\1/!g' $file
however, i have an issue with dot part that cuts it as ;
path = /aaa/bbb/ccc.com/user/dddd/user55#ccc/
instead, it should have been;
path = /aaa/bbb/ccc.com/user/dddd/.user55#ccc/
Thanks in advance,
Using a pattern with three capture groups should do what you need. The first group will capture the portion behind the initial # (as a group we omit from the replacement), the second group will include the /dddd/ portion, and the third being the complete user#somewhere with a prepended .
's!(#.+\..+)(/.+/)(.+#.+)!\2.\3!g'
Depending on your version of bash you could use it like this:
sed -i.bak -r 's!(#.+\..+)(/.+/)(.+#.+)!\2.\3!g' $file
↳ (GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
sed -i bak -E 's!(#.+\..+)(/.+/)(.+#.+)!\2.\3!g' $file
↳ GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin12)
result:
path = /aaa/bbb/ccc.com/user/dddd/.user#yahoo.com/
path = /aaa/bbb/ccc.com/user/dddd/.user#hotmail.co.uk/
path = /aaa/bbb/ccc.com/user/dddd/.user#abc.xxx.co.uk/
path = /aaa/bbb/ccc.com/user/dddd/.user55#ccc.com/
It's a little unclear if you want to keep the full extension on the end of the last match; if not sed is probably not the best choice because it can't do look-ahead, look-behind assertions, nor toggle greedy in any straight-forward manner. In the case that's a deal breaker, you could use this pattern on one of many other avenues:
(#.+\..+)(/.+/)(.+#.+?)(\..*/)
result:
path = /aaa/bbb/ccc.com/user/dddd/.user#yahoo
path = /aaa/bbb/ccc.com/user/dddd/.user#hotmail
path = /aaa/bbb/ccc.com/user/dddd/.user#abc
path = /aaa/bbb/ccc.com/user/dddd/.user55#ccc
You would have to use two matches:
sed -E 's/(.*?\..*?)\/(.*?)#\1/\1\/\2/g'
Regex: (.*?\..*?)\/(.*?)#\1
Replacement: \1\/\2
Flags: g (Global)
Result:
path = /aaa/bbb/ccc.com/user/dddd/user#yahoo.com/
path = /aaa/bbb/ccc.com/user/dddd/user#hotmail.co.uk/
path = /aaa/bbb/ccc.com/user/dddd/user#abc.xxx.co.uk/
path = /aaa/bbb/ccc.com/user/dddd/user55#ccc.com/
sed -E 's/(\w+#\w+)[\w\.]*/\1/g'
Regex: (\w+#\w+)[\w\.]*
Replacement: \1
Flags: g (Global)
Result:
path = /aaa/bbb/ccc.com/user/dddd/user#yahoo/
path = /aaa/bbb/ccc.com/user/dddd/user#hotmail/
path = /aaa/bbb/ccc.com/user/dddd/user#abc/
path = /aaa/bbb/ccc.com/user/dddd/user55#ccc/
If the -E switch is not available on your version of sed, then you might have to use perl.
Example:
perl -pe 's/(.*?\..*?)\/(.*?)#\1/\1\/\2/g' -i filename.ext
If I try this in bash, I get the following result:
root#home [~]# echo "path = /aaa/bbb/ccc.com/user#ccc.com/dddd/user55/" | sed -E 's/(.*?\..*?)\/(.*?)#\1/\1\/\2/g'
path = /aaa/bbb/ccc.com/user/dddd/user55/
root#home [~]# echo "path = /aaa/bbb/ccc.com/user/dddd/user55/" | sed -E 's/(\w+#\w+)[\w\.]*/\1/g'
path = /aaa/bbb/ccc.com/user/dddd/user55/

How to exclude patterns in regex conditionally in bash?

This is the content of input.txt:
hello=123
1234
stack=(23(4))
12341234
overflow=345
=
friends=(987)
Then I'm trying to match all the lines with equal removing the external parenteses (if the line has it).
To be clear, this is the result I'm looking for:
hello=123
stack=23(4)
overflow=345
friends=987
I toughth in something like this:
cat input.txt | grep -Poh '.+=(?=\()?.+(?=\))?'
But does not returns nothing. What am I doing wrong? Do you have any idea to do this? I'm so interested.
Using awk:
awk 'BEGIN{FS=OFS="="} NF==2 && $1!=""{gsub(/^\(|\)$/, "", $2); print}' file
hello=123
stack=23(4)
overflow=345
friends=987
Here is an alternate way with sed:
sed -nr ' # Use n to disable default printing and r for extended regex
/.+=.+/ { # Look for lines with key value pairs separated by =
/[(]/!ba; # If the line does not contain a paren branch out to label a
s/\(([^)]+)\)/\1/; # If the line contains a paren find a subset and print that
:a # Our label
p # print the line
}' file
$ sed -nr '/.+=.+/{/[(]/!ba;s/\(([^)]+)\)/\1/;:a;p}' file
hello=123
stack=23(4)
overflow=345
friends=987