Using dollar sign in sed for both variable replacement and character - bash

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.
\\ + \$ == \\\$

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

Remove "../" from text file using sed

I have a text file containing text such as this
../path-to-image/folder1/image.jpg path-to-another-image/folder2/image.png
I would like to remove the "../" part and obtain
path-to-image/folder1/image.jpg path-to-another-image/folder2/image.png
I have tried using sed with
sed -i 's#../##g' file.txt
But I obtain the following:
path-to-imafoldeimage.jpg path-to-another-imafoldeimage.png
All the slashes and some other characters were removed and thus the path to my images was broken.
I looked up how to make it match exactly the string using
\<\>
sed 's#\<../\>#%%#g' file.txt
But the output is identical to input. Is there a way to remove "../" using sed? I need this from command line since I have about 10 files with similar path structures which I will copy into a bunch of directories. Meaning I can't do this manually.
.s have special meaning in regex syntax, and need to be escaped.
Either [.] (creating a character class of size one) or \. will suffice; I strongly advise the former, as it works properly in a wider array of quoting contexts. Thus:
sed -i 's#[.][.]/##g' file.txt
Dots are special characters in regex. They mean any character (except a newline). So you need to escape them with backslashes in the sed command:
sed -i 's#\.\./##g' file.txt
Do sed -i 's/\.\.\///g' file.txt
's/\.\.\///g' replaces ../ with an empty string, as of the syntax 's/string/replacement/g'
\.\.\/ escapes the dots and the slash, which is necessary because dots and slashes are special characters in regex. After escaping \.\.\/, the string reads ../.
The following two slashes surround the replacement string, which is empty in this case.
Edit:
For easier legibility (and to avoid escaping the slash):
sed -i 's#\.\.\/##g' file.txt. This is much closer to your initial attempt, and as a revised explanation, \.\./ translates to ../, as the slash no longer needs to be escaped. The dots are still special characters and must be escaped with the backslash.

using variables in regex?

Part of a shell script that I am creating takes a plain text list of files...
11111.jpg
22222.jpg
33333.jpg
...and appends a user-defined prefix that is stored in a variable to create a list of paths that looks like this:
user/defined/prefix/11111.jpg
user/defined/prefix/22222.jpg
user/defined/prefix/33333.jpg
I am attempting to use sed to add the prefix in this manner:
sed -e 's/^/prefix/' oldFile > newFile.new
The variable is getting assigned correctly:
echo $selectedPrefix
user/defined/prefix
Put no combinations of single quotes, double quotes of whatever seem to get sed to use the ACTUAL value of the variable instead of just the variable name.
sed -e 's/^/$selectedPrefix/' oldFile > newFile.new
Yields:
$selectedPrefix11111.jpg
$selectedPrefix22222.jpg
$selectedPrefix33333.jpg
Help! I'm sure the solution is simple but I feel like I've tried everything....
As mentionned by Cyrus, you need to used " (double quote) instead ' (single quote) if you want the variable replacement because single quoted string are interpreted literally so it doesn't see $selectedPrefix as a variable but as the string value of $selectedPrefic hence what you saw.
Since you are working with paths in you sed, you are correct in assuming that you should use a different separator for your sed comment. I usually prefer using | but ~ would also work.
so basically you could have:
sed -e "s~^~$selectedPrefix~" oldFile > newFile.new
This code would solve your problem:
selectedPrefixEscaped="$(echo "$selectedPrefix" | sed 's/\//\\\//g')" && sed -e "s/^/$selectedPrefixEscaped/" oldFile > newFile.new
Just using a different delimiter on sed would leave you open to problems when (if) the path contains the new delimiter (ex.: /folder/folder#5/file.txt would be problematic if using # as sed delimiter).

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.

Find and replace variables containing non-escaped characters with sed

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

Resources