Find and replace variables containing non-escaped characters with sed - bash

I can use this, to find all instances of "fly" and replace it with "insect" in my file:
sed -i 's/fly/insect/g' ./animals.txt
How can I find a BASH variable and replace it with another BASH variable? E.g.:
name=$(echo "fly")
category=$(echo "insect")
sed -i 's/$name/$category/g' ./animals.txt
Update:
I am using GNU sed version 4.2.1. When I try the solutions below, it reports this error:
sed: -e expression #1, char 73: unknown option to `s'
Update:
I discovered why the error is coming up. $category frequently contains lots of symbols (e.g. "/", "$", "#", "!", brackets, etc.).
ame=$(echo "fly")
category=$(echo "in/sect")
sed -i "s/$name/$category/g" ./animals.txt
The above code will create the same error:
sed: -e expression #1, char 7: unknown option to `s'
Is there a way to let sed complete the replaces, even when it encounters these symbols?

Using double quotes
Use double quotes to make the shell expand variables while keeping whitespace:
sed -i "s/$name/$category/g" ./animals.txt
Note: if you need to put backslashes in your replacement (e.g. for back references), you need double slashes (\& contains the pattern match):
Using single quotes
If you've a lot shell meta-characters, you can do something like this:
sed -i 's/'$pattern'/'$category'/g' ./animals.txt
I discovered why the error is coming up. $category frequently contains lots of symbols (e.g. "/", "$", "#", "!", brackets, etc.).
If the substitution or replacement portion contains characters like / then we can use different sets of sed delimiters. You can use something like - # % , ; : | _ etc. any character that does not happen to occur in your substitution and replacement.

Use double-quotes so the shell will interpolate your variables:
sed -i "s/$name/$category/g" ./animals.txt

Use double quotes:
sed -i "s/$name/$category/g" ./animals.txt

Related

Bash to comment a line in LaTeX .cls file with %, using sed command

I am trying to use bash with sed to replace this
\printfield{year}%
by this
%\printfield{year}%
There are 4 spaces at the start of the line (although I am not sure if this matters). Basically, this is a LaTeX *.cls file and I need to comment out every occurrence of \printfield{year}%.
My attempt was based on this SO post
sed -i 's/\printfield{year}%/%\printfield{year}%/g' mydir/xxxxx.cls
however it produces this output
\%printfield{year}%
where the % sign is in the wrong place - it should be at the start of the line (1st character), but it is appearing as the 2nd character. I also tried sed -i "...".i.e. using double quotes, but it still gave the same wrong output (above) as single quotes.
Is there a way to comment out this line by placing a % at the beginning of the line, using sed in bash?
System Info
Ubuntu 18.10
backslash needs escaping
$ sed 's/\\printfield{year}%/%&/g' file
note that you don't have to repeat matched string again, & is for that.
\p
The \ is an escape character. In order to print \ you need to specify it twice. The \p escapes to just p and the \ is ignored. Use:
sed 's/\\printfield{year}%/%\\printfield{year}%/g'
This is because bash is interpreting the \ character as an escape character, thus in your sed command
sed -i 's/\printfield{year}%/%\printfield{year}%/g' mydir/xxxxx.cls
its trying to replace escaped 'p' printfield with the other command, to get around this escape your \ with another \
so
sed -i 's/\\printfield{year}%/%\\printfield{year}%/g' mydir/xxxxx.cls

Using dollar sign in sed for both variable replacement and character

I try to use sed to change a line in a file named convergence.gnu
I have a variable named lafila, which is a file name
Currently, I can do:
lafila="nGas060.dat"
sed -i "6s/.*/plot \"$lafila\" using 1:2 with l/" convergence.gnu
This changes correctly the sixth line of my convergence.gnu file to:
plot "nGas062.dat" using 1:2 with l
However, now I want to include a dollar sign in the replaced line to obtain instead:
plot "nGas062.dat" using ($1/1000):2 with l
Can you tell me what to change in my sed command? If I escape a dollar sign it does not work properly. Double dollars also don't work.
I believe your issue is actually being caused by the forward slash in ($1/1000), which clashes with the slashes being used to delimit the various components of the sed command. You either need to escape that forward slash as well, or alternatively use a different character for delimiting the sed strings. Either of the below should work:
lafila="nGas060.dat"
sed -i "6s/.*/plot \"$lafila\" using (\$1\/1000):2 with l/" convergence.gnu
or
lafila="nGas060.dat"
sed -i "6s,.*,plot \"$lafila\" using (\$1/1000):2 with l," convergence.gnu
Using a different delimiting character can be a good way to make your sed string look neater and avoid the leaning toothpick syndrome.
echo foo | sed "s,foo,/there/are/a/lot/of/slashes/here,"
is much nicer than
echo foo | sed "s/foo/\/there\/are\/a\/lot\/of\/slashes\/here/"
Use single quotes:
sed -i '6s/.*/plot "'$lafila'" using ($1\/1000):2 with l/' convergence.gnu
Single quotes protect double quotes and $ is not interpreted inside them. However, you do need to escape /.
See also:
Difference between single and double quotes in Bash
Try it:
lafila="nGas060.dat"
sed -i "6s#.*#plot \"$lafila\" using (\\\$1/1000):2 with l#" convergence.gnu
You need only to escape backslash and after the dollar sign.
\\ + \$ == \\\$

Escaping in bash within perl

I'm running something in perl and have the following command, which deletes consecutive duplicate lines (only keeping one of them)
system("sed -i '$!N; /^\(.*\)\n\1$/!p; d' *[13579].csv");
However, when I run this, I get the following error:
sed: -e expression #1, char 11: unterminated address regex
I have a feeling it has to do with my escaping, but I'm not too certain as I am rather inexperienced with perl and bash. I know the dollar signs should be escaped, but what about the backslashes? Does anyone have a good resource they can point me to to learn more about escaping bash within perl? Thanks!
When putting sed in Perl it can be fussy, there's a couple things you could do. The first would be to change the type of quotes you wrap around the command system is running and the sed pattern (flip outer to single, inner to double); the other option would be to escape the \ characters in sed.
system('sed -i "$!N;/^\(.*\)\n\1$/!p;d" *filename');
Note: since your filename uses a special characters there might be escaping needed for that to work with globbing (eg. *\\[13579].csv); escaping would be something like this:
system("sed -i '$!N;/^\\(.*\\)\\n\\1\$/!p;d' *\\[13579].csv");
If your file name happens to include spaces then those would need escaping as well.
system("sed -i '$!N;/^\\(.*\\)\\n\\1\$/!p;d' *\\[12345]\\ \\[13579].csv");
sed would then find any files matching *[12345] [13579].csv and in-place edit them.

sed not working correctly when using variables

source=<!--jta-data-source>jdbc/FCBDataSource</jta-data-source-->
destination=<jta-data-source>jdbc/FCBDataSource</jta-data-source>
sed -i "s/$source/$destination/g" /home/rohan/R2.5LZN/UIReleasedArea/obp.ui.domain/persistence.xml
I am getting error sed: -e expression #1, char 44: unknown option to s
Consider what happens when the substitution occurs. The command becomes:
sed -i 's/<!--jta-data-source>jdbc/FCBDataSource</jta-data-source-->/<jta-data-source>jdbc/FCBDataSource</jta-data-source>/g' some_filename
Then, sed sees s/<!--jta-data-source>jdbc/FCBDataSource</j… and thinks that you want to replace an occurrence of <!--jta-data-source>jdbc with the text FCBDataSource<, and the s command has an illegal j modifier (and other junk).
You need to pick a delimiter character that does not appear in either the pattern or the replacement text. A , will do.
destination='<!--jta-data-source>jdbc/FCBDataSource</jta-data-source-->'
source='<jta-data-source>jdbc/FCBDataSource</jta-data-source>'
sed -i "s,$source,$destination,g"
/home/rohan/R2.5LZN/UIReleasedArea/obp.ui.domain/persistence.xml
The problem was with the variable
Quotes were required

sed replace command

I want to uncomment one of these two statements as per condition:
#export CATALINA_OPTS="-javaagent:$TOMCAT_INSTALL_LOCATION/$APACHE_TOMCAT_VERSION/lib/oi-mule-libs/usage/injector.jar"
#export CATALINA_OPTS="-javaagent:$TOMCAT_INSTALL_LOCATION/$APACHE_TOMCAT_VERSION/lib/agents/cl-proxy-agent.jar"
I used following sed command:
#sed -i 's/#export CATALINA_OPTS="-javaagent:$TOMCAT_INSTALL_LOCATION/$APACHE_TOMCAT_VERSION/lib/oi-mule-libs/usage/injector.jar"/export CATALINA_OPTS="-javaagent:$TOMCAT_INSTALL_LOCATION/$APACHE_TOMCAT_VERSION/lib/oi-mule-libs/usage/injector.jar"/' $TOMCAT_HOME/bin/init-script.sh
I got following error:
sed: -e expression #1, char 85: unknown option to `s'
Why did I get this error?
How could I achieve my purpose by modifying above command?
You should change your delimeters to the sed command. Your delimiter is '/' which also appers in your pattern and that gives an error. Try this:
sed 's:<pattern to be replaced>:<replacement pattern>:g'
Here g is for global, s for substitute. You can use any other delimiter not appearing in your pattern, I have used colon ':' in the example
You need to either escape slash characters with backslashes or use another character as a delimiter for the "s"ubstitute command (as pointed out by sleeping_dragon)
With that in mind:
sed -e s/^#(.*\/injector.jar.*)/\1/ -e s/^#(.*\/cl-proxy-agent.jar.*)/\1/ $my_file
making the regular expression in round brackets as unique as required to identify the exact string you need uncommenting.

Resources