Reputation: 89
My goal is to change all occurrences of a pattern, but only between two other patterns.
#!/bin/bash
while [ "$( sed -n -e "/^echo.*foo.*>>/p" file.txt | wc -l )" -gt 0 ]; do
sed -i -e "/^echo.*foo.*>>/ s/foo/moo/" file.txt
done
The above code will change foo to moo if it occurs between ^echo and >>.
Before:
foo
echo "foo"
foo >> foo
foo echo "foo" >> foo
echo "poo" >> foo
echo "foo" >> foo
echo "foo foo" >> foo
echo "foofoofoo" >> foo
After:
foo
echo "foo"
foo >> foo
foo echo "foo" >> foo
echo "poo" >> foo
echo "moo" >> foo
echo "moo moo" >> foo
echo "moomoomoo" >> foo
However, here is the ugly challenge. What I am actually wanting to do is to escape all backslashes in the same area like follows:
Before:
\
echo "\"
\ >> \
\ echo "\" >> \
echo "poo" >> \
echo "\" >> \
echo "\ \" >> \
echo "\\\" >> \
After:
\
echo "\"
\ >> \
\ echo "\" >> \
echo "poo" >> \
echo "\\" >> \
echo "\\ \\" >> \
echo "\\\\\\" >> \
Without using Perl or Python, what would be a way to do this? Thanks in advance! :)
Upvotes: 1
Views: 62
Reputation: 203189
Good grief, just use awk:
$ awk 'match($0,/^(echo)(.*)(>>.*)/,a) {gsub(/\\/,"\\\\",a[2]); $0=a[1] a[2] a[3]}1' file
\
echo "\"
\ >> \
\ echo "\" >> \
echo "poo" >> \
echo "\\" >> \
echo "\\ \\" >> \
echo "\\\\\\" >> \
The above uses GNU awk for the 3rd arg to match(), with other awks you'd use substr()s.
Upvotes: 1
Reputation: 89547
You can do it using a substitution and without a while loop:
sed '/^echo.*\\.*>>/{s/~/~~/g;s/\\/\\\\~/g;:a;s/^\(echo.*\\\\\)~\(.*>>\)/\1\2/;ta;s/\\\\~/\\/g;s/~~/~/g;}' file
details:
/^echo.*\\.*>>/ { # condition
s/~/~~/g; # protect the '~'
s/\\/\\\\~/g; # change all backslashes to double + '~'
:a; # define the label "a"
s/^\(echo.*\\\\\)~\(.*>>\)/\1\2/; # change a "\\~" inside "echo...>>" to "\\"
ta; # if something is replaced go to label "a"
s/\\\\~/\\/g; # restore backslashes outside "echo...>>"
s/~~/~/g; # restore '~'
}
Note: if needed, you can be more rigorous using [^>]
instead of the dots, or to be totally sure \([^>]*\(>[^>][^>]*\)*\)
in the third replacement, but for this last one you must change the replacement string to \1
:
s/^\(echo\([^>]*\(>[^>][^>]*\)*\)*\\\\\)~/\1/;
Upvotes: 0
Reputation: 1419
I have copied your shell script, but it doesn't work on my station.
But if your file looks like this:
$ cat file
foo
echo "foo"
foo >> foo
foo echo "foo" >> foo
echo "poo" >> foo
echo "moo" >> foo
echo "moo moo" >> foo
echo "moomoomoo" >> foo
You can try this
sed -e 's/foo/\\/g' -e 's/moo/\\\\/g' file
If you want to have it inline you have to set the -i
flag.
Upvotes: 0