You can just use sed
to do the fixup for you:
printf '%s\n' "$i" "$j" |
sed 's/[]\$*&/.^[]/\\&/g;H;$!d
x;y|\n|/|;s|.*|s&/g|' |
sed -f - /path/to/infile
So this s///
ubstitution will escape any/all BRE metacharacters in input:
s/[]\$*&/.^[]/\\&/g
...by prefixing each with a backslash. The first sed
then saves a copy of the first line - $i
- in H
old space by prepending a \n
ewline character to it. The $i
line is then d
eleted because it is !
not the $
last. The next line - the $j
line - is also the last, and after it gets the same treatment as the first, it is not d
eleted. Instead, it ex
changes the hold and pattern buffers and operates on the concatenated results. At this point pattern space looks like:
\n1\.0\n2\.0
...so we y///
translate all \n
ewlines for /
slashes, s///
ubstitute .*
all of pattern-space for &
itself plus a prepended s
and an appended /g
which gets us:
s/1\.0/2\.0/g
This is then autoprinted to the second sed
which is reading stdin - or -f -
- as its script. When the first sed
is finished and closes the pipe between them, the second sed
starts applying...
s/1\.0/2\.0/g
...to every line in its named input file - which is here /path/to/infile
.
I wrote your file like this:
printf '%04s%04s%04s%04s\n' \
0 0 -1 0 1 0 0 0 0 -1\
0 0 1.5 2.0 1.0 0 >/tmp/temp
Which got me a file like...
0 0 -1 0
1 0 0 0
0 -1 0 0
1.5 2.0 1.0 0
I then wrote a different version of your script like:
ii=0.0
for i in 1.0 2.0 3.0 4.0
do str2=$i
printf '\033[41m## %s \033[0m\n' \
"str2 = $str2" "$ii $str2"
printf %s\\n "$ii" "$str2"
ii=$str2
done |
sed ' s/[]\$^&*./[]/\\&/g;H;x
s|^\(\n\)\(.*\)\n\(.*\)\n\(.*\)\n\(.*\)|\
bs\5\1:i\5\1i\\\1\2\\\1\3\1:s\5\1s/\4/\5/gp;ti\5|p
s|||;h;d' |
sed -f - /tmp/temp
Which uses the shell only to generate the strings, but allows sed
to do all of the data processing. Notice that though two sed
s are called each is only ever called once.
When I run it the results are:
0 0 -1 0
1 0 0 0
0 -1 0 0
1.5 2.0 2.0 0
## str2 = 2.0
## 1.0 2.0
1.5 3.0 3.0 0
## str2 = 3.0
## 2.0 3.0
1.5 4.0 4.0 0
## str2 = 4.0
## 3.0 4.0
1.5 4.0 4.0 0
The lines beginning with #
are colored red as I expect you mean them to be. sed
only writes them when a s///
ubstitution is successful. The script that the first sed
writes for the second looks like:
bs1\.0
:i1\.0
i\
[41m## str2 = 1\.0 [0m\
[41m## 0\.0 1\.0 [0m
:s1\.0
s/0\.0/1\.0/gp;ti1\.0
bs2\.0
:i2\.0
i\
[41m## str2 = 2\.0 [0m\
[41m## 1\.0 2\.0 [0m
:s2\.0
s/1\.0/2\.0/gp;ti2\.0
bs3\.0
:i3\.0
i\
[41m## str2 = 3\.0 [0m\
[41m## 2\.0 3\.0 [0m
:s3\.0
s/2\.0/3\.0/gp;ti3\.0
bs4\.0
:i4\.0
i\
[41m## str2 = 4\.0 [0m\
[41m## 3\.0 4\.0 [0m
:s4\.0
s/3\.0/4\.0/gp;ti4\.0
Note though that that though it appears the [
strings are not escaped this is just the effect of my terminal on the output - which winds up eating the char immediately following \033
. When the second sed
receives the script the input is like \033\[...
but the output it i
nserts to stdout is \033[...
sed "s/${i//./\\.}/$j/g" infile
. – don_crissti Mar 12 '15 at 04:01&
or.
differently depending on context. I think that maybe I am not alone in this regard - and this is why RHS,[aic]
all require two backslashes to represent one literally even when they can serve no other purpose in context. At least, that's my assumption. I can't figure on another reason for it. – mikeserv Mar 12 '15 at 04:29