Sed find and replace expression works with literal but not with variable interpolation - bash

For the following MVCE:
echo "test_num: 0" > test.txt
test_num=$(grep 'test_num:' test.txt | cut -d ':' -f 2)
new_test_num=$((test_num + 1))
echo $test_num
echo $new_test_num
sed -i "s/test_num: $test_num/test_num: $new_test_num/g" test.txt
cat test.txt
echo "sed -i "s/test_num: $test_num/test_num: $new_test_num/g" test.txt"
sed -i "s/test_num: 0/test_num: 1/g" test.txt
cat test.txt
Which outputs
0 # parsed original number correctly
1 # increment the number
test_num: 0 # sed with interpolated variable, does not work
sed -i s/test_num: 0/test_num: 1/g test.txt # interpolated parameter looks right
test_num: 1 # ???
Why does sed -i "s/test_num: $test_num/test_num: $new_test_num/g" test.txt not produce the expected result when sed -i "s/test_num: 0/test_num: 1/g" test.txt works just fine in the above example?

As mentioned in the comment, there is a white space in ${test_num}. Therefore in your sed there should not be an empty space between the colon and your variable.
Also I guess you should surround your variable with curly bracket {} to increase readability.
sed "s/test_num:${test_num}/test_num: ${new_test_num}/g" test.txt
test_num: 1
If you just want the number in ${test_num}, you can try something like:
grep 'test_num:' test.txt | awk -F ': ' '{print $2}'
awk allows to specify delimiter with more than 1 character.

Instead of grep|cut you can also use sed.
#! /bin/bash
exec <<EOF
test_num: 0
EOF
grep 'test_num:' | cut -d ':' -f 2
exec <<EOF
test_num: 0
EOF
sed -n 's/^test_num: //p'

When using regexp replace in sed there is special meaning to $ .
Suggesting to rebuild your sed command segments as follow:
sed -i 's/test_num: '$test_num'/test_num: '$new_test_num'/g' test.txt
Other option, use echo command to expand variables in sed command.
sed_command=$(echo "s/test_num:${test_num}/test_num: ${new_test_num}/g")
sed -i "$sed_command" test.txt

Related

How to pass an bash command line argument to sed -n p?

I want to output a specific line from bash argument using sed, I have tried many ways, but none work:
#!/bin/bash
sed -n '$2p' $1
sed -n '${2}p' $1
sed -n "$2p" $1
sed -n "${2}p" $1
sed -n ''"$2"'p' $1
How on earth do I get the correct result?
Try
sed -n "$2p" $1
Demo:
$seq 10 > file.txt
$cat temp.ksh
#!/bin/bash
set -x
sed -n "$2p" $1
$./temp.ksh file.txt 3
+ sed -n 3p file.txt
3
$

Cannot assign output of sed to a variable

I refered this
https://unix.stackexchange.com/questions/251388/prefix-and-suffix-strings-to-each-output-line-from-command
to add prefix to output of ls.
I want to store this output of ls:
file1
file2
file3
in to a variable with value:
/../file1 /../file2 /../file3
This is my .sh file:
PREFIX="/../"
OUTPUT_INLINE=$(ls | tr "\n" " ")
OUTPUT="${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
echo "${OUTPUT_INLINE}"
echo "${OUTPUT}"
Output is:
file1 file2 file3
It means variable OUTPUT contains nothing.
Even if I do:
echo "${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
I will get:
/../file1 /../file2 /../file3
What is wrong here ?
You are assigning OUTPUT variable this command
"${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
Which means nothing.
Do as you are already doing with OUTPUT_INLINE variable to assign the output of command.
OUTPUT=$(echo -n "${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g")
OUTPUT="${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g"
That pipes OUTPUT="${OUTPUT_INLINE}" into sed "s|\<|$PREFIX|g", which doesn’t do anything. I think you meant:
OUTPUT=$(printf '%s' "${OUTPUT_INLINE}" | sed "s|\<|$PREFIX|g")
but there’s lots of fragility here around different delimiter types, and you should be able to avoid all that:
PREFIX="/../"
for filename in *; do
printf '%s%s ' "$PREFIX" "$filename"
done

Shell Scripting: Delete all instances of an exact word from file (Not pattern)

I'm trying to delete every instance of a certain word in a file. I can't make it so that it doesn't delete the pattern from other words. For example if I want to remove the word 'the' from the file. It will remove 'the' from 'then' and leave me with just 'n'.
Right now I have tried:
sed s/"$word"//g -i final_in
And:
sed 's/\<"$word"\>//g' -i final_in
But neither of them have worked. I thought this would be pretty easy to Google, but every solution I find does not work properly.
$word='the'
$sed -r "s/\b$word\b//g" << HEREDOC
> Sample text
> therefore
> then
> the sky is blue
> HEREDOC
Sample text
therefore
then
sky is blue
\b=word boundary
# test
word='the'
echo 'aaa then bbb' | sed -r "s/$word//g"
# To match exacte word, you can add spaces :
word=then
echo 'aaa then bbb' | sed -e "s/ $word / /g"
# to modify a file
word='the'
cat file.txt | sed -r "s/ $word / /g" > tmp.txt
mv tmp.txt file.txt
# to consider ponctuations :
word=then
echo 'aaa. then, bbb' | sed -e "s/\([:.,;/]\)* *$word *\([:.,;/]\)*/\1 \2/g"

Bash echo -e equivalent using sed and tee

I am doing some find/replace thing with sed and using tee to write output file.Here is the command
# $1 source
# $2 Type
# $3 name
# $4 body
sudo sed "s/<!--ID-->/1/g" ./templates/tpl.txt \
| sed "s/<!--AUTHOR-->/myname/g" \
| sed "s/<!--TYPE-->/$2/g" \
| sed "s/<!--BODY-->/$4/g" \
| sed "s/<!--NAME-->/$3/g" \
| tee "$3.txt" > /dev/null
In the output file I see "n" in place of new lines . I need the same effect of as of the following
(but after template substitution )
echo -e "$4" > "$3.txt"
I am bash learner and please help me furnish my code
Edit
$4 contains multiline string (e.g a mysql function /procedure or trigger ) with comments etc
thanks
If you plan to use tee for writting privileged files, you have to use sudo with tee.
You could:
sed < template/tpl.txt -e "
s/<!--ID-->/1/g;
s/<!--AUTHOR-->/myname/g;
s/<!--TYPE-->/$2/g;
s/<!--NAME-->/$3/g;
/<!--BODY-->/{s/<!--BODY-->/$4/;s/\\n/\n/g;}
" | sudo tee "$3" >/dev/null
other way: in bash ,you could use:
echo "${4//\\n/$'\n'}"
so:
sed "s/<!--BODY-->/${4//\\n/$'\n'}/;"
could work too.
With Mac's sed this may work better:
sed < template/tpl.txt "s/<\!--ID-->/1/g;
s/<\!--AUTHOR-->/myname/g;
s/<\!--TYPE-->/$2/g;
s/<\!--BODY-->/${4//\\n/\\$'\n'}/
" | sudo tee "$3" >/dev/null
Explanation: ${4//\\n/\\$'\n'} are bashism, we could use as you use bash. Mac sed don't support \n as newline, but support a newline if escaped by a backslash \, so in RHS, \n could be written (in bash): \\$'\n' :
echo abc\\$'\n'def
abc\
def

SED: First and last empty lines not removed

I'm running the following but it's returning with empty lines at the top and bottom of the new file.
How do I output to a new file without these empty lines?
input | sed -E '/^$/d' > file.txt
The following has no effect either.
sed '1d'
sed '$d'
I'm unsure of where the expression has problems.
If you are comfortable using awk then this would work -
awk 'NF' INPUT_FILE > OUTPUT_FILE
grep . file_name > outfile would do the job for you.
This might work for you:
echo -e " \t\r\nsomething\n \t \r\n" | sed '/^\s*$/d' | cat -n
1 something
N.B. This removes all blank lines, to preserve blank lines in the body of a file use:
echo -e " \t\r\n something\n \nsomething else \n \t \r\n" |
sed ':a;$!{N;ba};s/^\(\s*\n\)*\|\(\s*\n\)*$//g'
something
something else

Resources