Reputation: 2477
I would like an approach to do a replace using the sed command that escapes a "pattern" (string) to be used in another sed command. This escape process must include handling for multi-line strings pattern.
To illustrate I present the code below. It works perfectly (well tested so far), but fails when we have strings with multiple lines (see "STRING_TO_ESCAPE").
#!/bin/bash
# Escape TARGET_STRING.
read -r -d '' STRING_TO_ESCAPE <<'HEREDOC'
$N = "magic_quotes_gpc = <b>"._("On")."</b>";
$D = _("Increase your server security by setting magic_quotes_gpc to 'on'. PHP will escape all quotes in strings in this case.");
$S = _("Search for 'magic_quotes_gpc' in your php.ini and set it to 'On'.");
$R = ini_get('magic_quotes_gpc');
$M = TRUE;
$this->config_checks[] = array("NAME" => $N , "DESC" => $D , "RESULT" => $R , "SOLUTION" => $S , "MUST" => $M );
HEREDOC
ESCAPED_STRING=$(echo "'${STRING_TO_ESCAPE}'" | sed 's/[]\/$*.^|[]/\\&/g')
ESCAPED_STRING=${ESCAPED_STRING%?}
TARGET_STRING=${ESCAPED_STRING#?}
# NOTE: The single quotes in "'${STRING_TO_ESCAPE}'" serve to prevent spaces
# being "lost" at the beginning and end of the string! The manipulations with
# "ESCAPED_STRING" are used to remove them. When we use sed with the file being
# input (flag "-i") this problem does not occur.
# Escape REPLACE_STRING.
read -r -d '' STRING_TO_ESCAPE <<'HEREDOC'
/* NOTE: "Magic_quotes_gpc" is no longer required. We taught GOsa2 to deal with it (see /usr/share/gosa/html/main.php). By Questor */
/* Automatic quoting must be turned on */
/* $N = "magic_quotes_gpc = <b>"._("On")."</b>";
$D = _("Increase your server security by setting magic_quotes_gpc to 'on'. PHP will escape all quotes in strings in this case.");
$S = _("Search for 'magic_quotes_gpc' in your php.ini and set it to 'On'.");
$R = ini_get('magic_quotes_gpc');
$M = TRUE;
$this->config_checks[] = array("NAME" => $N , "DESC" => $D , "RESULT" => $R , "SOLUTION" => $S , "MUST" => $M ); */
HEREDOC
ESCAPED_STRING=$(echo "'${STRING_TO_ESCAPE}'" | sed 's/[]\/$*.^|[]/\\&/g')
ESCAPED_STRING=${ESCAPED_STRING%?}
REPLACE_STRING=${ESCAPED_STRING#?}
# Do the replace.
STRING_TO_MODIFY=$(cat file_name.txt)
MODIFIED_STRING=$(echo "'${STRING_TO_MODIFY}'" | sed 's/$TARGET_STRING/$REPLACE_STRING/g')
MODIFIED_STRING=${MODIFIED_STRING%?}
MODIFIED_STRING=${MODIFIED_STRING#?}
echo "$MODIFIED_STRING"
Thanks! =D
Upvotes: 0
Views: 91
Reputation: 180938
This escape process must include handling for multi-line strings pattern.
I think you're barking up the wrong tree. If you're trying to match a multiline pattern then the most significant problem is not how to escape the pattern, but rather how to write a sed
script that will successfully match anything to it.
The problem is that sed
reads input one line at a time. There are various ways to collect multiple lines and to operate on such collections, but you need to do that explicitly in the program. sed
is therefore a poor choice for attempting to match arbitrary multiline text. To make your task feasible, you would want to know how many lines the pattern will contain, so as to write your sed
program to be specific for that. Even then, this might be a better job for Perl.
Update:
Because I like sed
, however, here's an example of how you could write a sed
program that matches multiline patterns:
#!/bin/sed -f
# Build up a three-line window in the pattern space
:a
/\(.*\
\)\{2\}/! { N; ba; }
# A(nother) multiline pattern. If the pattern fails to match then the
# first line of the pattern space is printed and deleted, then
# we loop back to reload.
/^The\
quick\
brown$/! { P; D; ba; }
# Do whatever we want to do in the event of a match
s/brown/red/
# If control reaches here then the whole pattern space is printed,
# and if any input lines remain then we start again from the beginning
# with an initially-empty pattern space.
Example input:
$ ./ml.sed <<EOF
The
quick
brown
fox
jumped over
the lazy dog.
EOF
Output:
The
quick
red
fox
jumped over
the lazy dog.
Note well that newlines are matched as ordinary characters, but that literal newlines in pattern or replacement text need to be escaped in the normal way, for syntactic reasons.
Update 2:
Here's a variation the replaces appearances of the three-line sequence
brown
fox
jumped over
with the three-line sequence
red
pig
is fat
. Of course there are many other ways to accomplish the same thing with sed
, and one of the others might be preferable to this for your particular purposes.
#!/bin/sed -f
:a
/\(.*\
\)\{2\}/! { N; ba; }
/^brown\
fox\
jumped over$/! { P; D; ba; }
s/.*/red\
pig\
is fat/
Upvotes: 1