Sed Replace A Multi-Line String

From Chorke Wiki
Revision as of 09:04, 24 December 2022 by Shahed (talk | contribs) (→‎References)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Example

mkdir -p ${HOME}/Documents/sed_playground
tee   -a ${HOME}/Documents/sed_playground/000-default.conf >/dev/null <<EOF
#
#This is some test comments
#    Skip this
#

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options +ExecCGI
    AddHandler cgi-script .cgi .pl
    Require all granted
</Directory>
EOF

APACHE_CONF_FILE="${HOME}/Documents/sed_playground/000-default.conf"
DIRECTORY_FIND_LEAD_EXP='<Directory "\/var\/www\/cgi-bin">'

DIRECTORY_FIND_FULL_EXP=$(cat <<EOF
${DIRECTORY_FIND_LEAD_EXP}\n\
[ ]*AllowOverride None\n\
[ ]*Options +ExecCGI\n\
[ ]*AddHandler cgi-script \.cgi \.pl\n\
[ ]*Require all granted\n\
<\/Directory>
EOF
)
DIRECTORY_FILL_FULL_EXP=$(cat <<EOF
#<Directory "\/var\/www\/cgi-bin">\n\
    #AllowOverride None\n\
    #Options +ExecCGI\n\
    #AddHandler cgi-script \.cgi \.pl\n\
    #Require all granted\n\
#<\/Directory>
EOF
)

Complex:

echo "complex"
echo "sedding"
sed -i "/${DIRECTORY_FIND_LEAD_EXP}/{N;N;N;N;N;s|${DIRECTORY_FIND_FULL_EXP}|${DIRECTORY_FILL_FULL_EXP}|}" ${APACHE_CONF_FILE}

Simple:

echo "simple"
echo "sedding"
sed -z "s|${DIRECTORY_FIND_FULL_EXP}|${DIRECTORY_FILL_FULL_EXP}|" -i ${APACHE_CONF_FILE}

${HOME}/Documents/sed_playground/000-default.conf
#
#This is some test comments
#    Skip this
#

<Directory "/var/www/cgi-bin">
    AllowOverride None
    Options +ExecCGI
    AddHandler cgi-script .cgi .pl
    Require all granted
</Directory>
${HOME}/Documents/sed_playground/000-default.conf
#
#This is some test comments
#    Skip this
#

#<Directory "/var/www/cgi-bin">
    #AllowOverride None
    #Options +ExecCGI
    #AddHandler cgi-script .cgi .pl
    #Require all granted
#</Directory>

  • sed separate | used to instead of / to avoid escaping of / and .
  • EOF block used to avoid escaping of "
  • Lets have a look:

DIRECTORY_FIND_FULL_EXP=$(cat <<EOF
<Directory "/var/www/cgi-bin">\n\
[ ]*AllowOverride None\n\
[ ]*Options +ExecCGI\n\
[ ]*AddHandler cgi-script .cgi .pl\n\
[ ]*Require all granted\n\
</Directory>
EOF
)
DIRECTORY_FILL_FULL_EXP=$(cat <<EOF
#<Directory "/var/www/cgi-bin">\n\
    #AllowOverride None\n\
    #Options +ExecCGI\n\
    #AddHandler cgi-script .cgi .pl\n\
    #Require all granted\n\
#</Directory>
EOF
)

Explanation

  1. DIRECTORY_FIND_LEAD_EXP='<Directory "\/var\/www\/cgi-bin">' Special characters must be escaped with a backslash \.
  2. [ ]* This will match 0 or many whitespaces. Standard RegEx notation
  3. sed -i "/${DIRECTORY_FIND_LEAD_EXP}/{ This will search the file, line-by-line, for ${DIRECTORY_FIND_LEAD_EXP} <Directory "/var/www/cgi-bin">. Note that the search pattern must not contain newline. If, and only if, sed finds ${DIRECTORY_FIND_LEAD_EXP} in the file, then it will proceed with executing the sub-code within the curly braces {}. It will start from the pattern matching line of file.
  4. N;N;N;N;N; N tells sed to read the next line after the pattern and attach it to current line. It is important to understand that sed is designed to replace only 1 line at a time, so N will basically force sed to read 2 lines and consider them as a single line with a single newline \n between the two. The newline in the second line will be ignored. By chaining 5 Ns, we are instructing sed to read 6 lines of the file starting with the pattern matching line.
  5. s|${DIRECTORY_FIND_FULL_EXP}|${DIRECTORY_FILL_FULL_EXP}| Replace ${DIRECTORY_FIND_FULL_EXP} with ${DIRECTORY_FILL_FULL_EXP}. Note the presence of newlines in the variables. Understanding this part will take some trial and error.

References