bash scripting: find and replace multiword string - linux

I'm trying to write a bash script to configure a server and I need to change the line:
listen = /var/run/php5-fpm.sock
to equal the following:
listen = 127.0.0.1:9000
in the file:
/etc/php5/fpm/pool.d/www.conf
So I've been looking at tutorials for using sed and I've tried the following command to no avail:
$~: sed -i 's//var/run/php5-fpm.sock/127.0.0.1:9000/g' /etc/php5/fpm/pool.d/www.conf
$~: sed: -e expression #1, char 8: unknown option to `s'
I've tried escaping the forward slash with a backslash: '/' but I think I'm on the wrong track. There must be a better way to do this?
Thanks for your help.

This is because you are trying to replace the character '/' in the pattern, and this character is used to delimit the 's///' expression. You have two choices, you can escape every '/' character with '/'or - and this is the one I prefer, use a different character to delimit the pattern and replacement string - I tend to use '!'
The character immediately after the 's' is used to delimit the expressions.
sed -i 's!/var/run/php5-fpm.sock!127.0.0.1:9000!g' /etc/php5/fpm/pool.d/www.conf
I actually have got into the habit of ALWAYS using '!' for sed, and perl - as you end up having to escape less characters and ultimately save time.

Related

How to escape certain characters for sed? [duplicate]

This question already has answers here:
What special characters must be escaped in regular expressions?
(13 answers)
Closed 2 years ago.
I want to remove a line from a file and understand this can be done with sed.
The command below fails because of unterminated address regex
sed -i '/$settings['file_temp_path'] = '../tmp';/d' file.php
Having read the the answer at unterminated address regex while using sed . I now understand this is because characters [ and / must be escaped.
Having tried this, the code below is still unsuccessful
sed -i '/$settings\['file_temp_path'] = '..\/tmp';/d' file.php
What is wrong with this? What am I missing?
This might work for you (GNU sed):
sed -i '\#\$settings\['\''file_temp_path'\''\] = '\''\.\./tmp'\'';#d' file
When the regexp/replacement contains the match delimiter (usually /), it can either be escaped/quoted or the delimiters can be altered i.e. /\/tmp/ or \#/tmp#. Note that in the case of the substitution command s/\/tmp/replacement/ can also be s#/tmp#replacement# and leading delimiter does not need to escaped/quoted.
Meta characters i.e. ^,$,[,],*,.,\ and & must be escaped/quoted by \or placed in a character class e.g. . should be \. or [.].
As a rule of thumb, sed commands should be enclosed in single quotes ' and for single quotes to be included in the regexp they should be replaced by '\'' which closes off the existing commands, shell escapes/quotes a ' and reopens the next sed command.
Using double quotes " may also be used but may have unexpected side effects as they are open to shell interpolation.
N.B. If the regexp/substitution delimiter is put inside a character class it does not need to be escaped/quoted i.e. if / is the delimiter then [/] is the same as \/. Also note, that {,},|,? and + should not be escaped/quoted if they are to represent their literal value unless the -E or -r sed command line option is invoked, in which case they should be i.e + represents the plus sign as does \+ when the -E is invoked, whereas \+ and + when the -E or -r is invoked represent one or more of the preceding character/group.
You need to escape several special characters in your pattern, including $.
Example content of file.php:
foo
$settings['file_temp_path'] = '../tmp';
bar
Example code:
$ sed -i "s/\$settings\['file_temp_path'\] = '..\/tmp';//" file.php
$ cat file.php
foo
bar

Linux SED command syntax with escapes [duplicate]

This question already has answers here:
How to insert strings containing slashes with sed? [duplicate]
(11 answers)
Closed 6 years ago.
I am trying to replace a script call with a script call from a sub directory.
For example, originally I had ./output.sh in my script to call the output.sh script in the current directory.
I want to replace ./output.sh with ../output.sh so that it calls output.sh in the parent directory.
I tried
sed -i -e 's/../\output.sh/./\output.sh/g' scriptName.sh
This returns with
char 17: unknown option to 's'
Any help with the sed escape character syntax would be great.
Sed is bad at this; you'll risk turning an already existing ../output.sh into .../output.sh if you're not careful.
This is the best sed can do:
sed -i 's#output\.sh#../$#g' scriptName.sh
(I'm using # in place of / so that there are no forward slashes to escape. Sed accepts any punctuation character in place of forward slash.)
Note that this will convert ../output.sh to ../../output.sh but at least it doesn't create that triple-dot error.
Instead, try perl:
perl -pie 's#(?<!\.\./)(output\.sh\b)#../$1#g' scriptName.sh
This uses a negative look-behind to ensure it doesn't traverse to the parent's parent. It also allows using \b to denote a word break just in case you have something like output.shelf somewhere.
This would do the trick. Substitutes every appearance of ./output.sh for ../output.sh :
sed 's/\.\/output\.sh/\.\.\/output\.sh/g' scriptName.sh
The escape character is \. You should use it to escape:
Every dot .. The dot is used as any character in regex.
Every slash /. The slash character is used as delimiter between regex on the scommand.
The slash character is special in sed's s command if you formulate it as s/something/replacement/flags, so the slashes in your file paths cause the error. Fortunately, you can use any other character right after s, for example s#something#replacement#flags. So, replacing the command with s#\./output.sh#../output.sh#g should do the trick.
Note two additional changes: you have to escape the dot in first expression since it's also a special character in regex and you also got reversed order of search expression and replacement (thanks charli for noticing this). You don't need the backslashes before o characters, either.
Alternatively, you can use / after s but escape the literal slashes you want to replace by preceding them with backslashes: s/\.\/output.sh/..\/\output.sh/g. It seems in your code you tried to use this solution but put the backslashes after instead of before the slashes.

How do I find and replace text using sed using ~ as delimiter

Good-day,
In a Bash shell script I'm putting together, I am trying to find this text: /usr/local/freeswitch/log/freeswitch.log and replace it with: /var/log/freeswitch/freeswitch.log in this file: /etc/fail2ban/jail.local
This is what I have tried so far, both of which result in the error: sed: -e expression #1, char 75: unterminated `s' command
Attempt #1
sed -i 's~usr/local/freeswitch/log/freeswitch.log~var/log/freeswitch/freeswitch.log' /etc/fail2ban/jail.local
Attempt #2
sed -i 's~usr/local/freeswitch/log/freeswitch.log/var/log/freeswitch/freeswitch.log' /etc/fail2ban/jail.local
My research shows that since the text I'm searching for includes the "/" character, I should be using a different delimiter "~" to separate the find and replace strings. But looks like I'm doing something wrong, any assistance would be appreciated, thanks.
The structure of a sed substitution command is s/PATTERN/REPLACEMENT/ (note the delimiter at the end of the command).
You're right, you can change the delimiter to a different character, so if you're going to use ~ you need to put one of those at the end of the command.

Unterminated `s' command with sed troubleshooting

I have a problem with sed. I want to replace the entire specific line number for multiple lines in multiples documents.
This the bash command for 1 specific line in 1 specific document:
BNAME=$(basename $FILE .pdb)
psfgen1="pdb ./sedpdb/${BNAME}.pdb/"
sed -i '8s/'.*'/'${psfgen1}'/' ./psfgen.inp
And I get this error :
sed: -e expression #1, char 60: unterminated `s' command
Is anyone know how to solve this issue? Thanks!
I can see two things wrong:
There are forward slashes in the string that you're attempting to use in the sed command. These will be interpreted as part of the command, so you should use a different delimiter.
The * is unquoted, so will be glob-expanded by the shell to the names of all the files in the directory.
Reliably using shell variables in string substitutions is non-trivial but can be done using one of the approaches shown in the answers to this question.
In your case, it looks like you can probably get away with using another character as the delimiter, such as #:
sed -i "8s#.*#${psfgen1}#" ./psfgen.inp

Some text replaccement issues. Please advise

I'm new at bash scripting and I have a problem. I want to replace one line in a file with another one. This is my file:
/home/iosub/linux/fis2 b7c839bf081421804e15c51d65a6f8fc -
/home/iosub/linux/e212CIosub/doi 7edd241b1f05a9ea346c085b8c9e95c5 -
/home/iosub/linux/e212CIosub/test2 7edd241b1f05a9ea346c085b8c9e95c5 -
/home/iosub/linux/e212CIosub/xab ed4940ef42ec8f72fa8bdcf506a13f3d -
/home/iosub/linux/e212CIosub/test1 8af599cfb361efb9cd4c2ad7b4c4e53a -
/home/iosub/linux/e212CIosub/xaa 8cf57351b5fc952ec566ff865c6ed6bd -
/home/iosub/linux/e212CIosub/test3 74420c2c4b90871894aa51be38d33f2c -
Without the blank lines. I want to replace a line with another one.
for example /home/iosub/linux/e212CIosub/test1 8af599cfb361efb9cd4c2ad7b4c4e53a -
with /home/iosub/linux/e212CIosub/test1 d2199e312ecd00ff5f9c12b7d54c97f1 -
I have /home/iosub/linux/e212CIosub/test1 in a variable and the new code in another variable.
I know that I must use sed. How can I do it?
I've tried a lot of combinations like:
sed "/$1/d" cont > cont2;
where $1 is /home/iosub/linux/e212CIosub/test1
And after that I concatenate the new string to the file. This places the new line at the end of the file, it would be a good solution, if it would work . . . But it doesn't.It gives an error: sed: -e expression #1, char 5: extra characters after command
I've also tried the replacement method, but I didn't get any result.
Any solution would be good. Thank's
sed "s#$1#$var#g" -i filename
This will replace all occurences of $1 with $var in file filename inplace.
edit added '\' in front of # chars for initial regex delimiters
when you have a '/' char in your search pattern, it breaks the use of that character ('/') as a reg-exp delimiter. Use a Regex delimiter that is NOT in your regex, i.e.
sed "\#$1#d" cont > cont2
Then continue with your processing.
But given your example, why don't you do something like
sed "s\#\($1\)(.*$)#\1 $2 -#" cont > cont2
This matches your value from $1, essentially deletes the text after your $1 value, and prints the $1 value followed by a space, your $2 value (d2....), a space and the ending '-' character.
Edit (Finally), I'll mention that some legacy Unix sed's (aix in particular) seem to not accept escaping the regex delimiter char (but I don't have access to a system to verify that now). In that case, you have to escape all of the any-and-all '/'s in your regex search pattern, like \/home\/iosub\/linux\/e212CIosub\/test1, yikes!
I hope this helps.

Resources