Replace/substitute adversarial substring variables

2020-04-18 05:44发布

问题:

I have three unescaped adversarial shell variables.

$mystring
$old
$new

Remember, all three strings are adversarial. They will contain special characters. They will contain everything possible to mess up the replace. If there is a loophole in your replace, the strings will exploit it.

What is the simplest function to replace $old with $new in $mystring? (I couldn't find any solution in stack overflow for a generic substitution that will work in all cases).

回答1:

There's nothing fancy here -- the only thing you need to do to ensure that your values are treated as literals in a parameter expansion is to ensure that you're quoting the search value, as described in the relevant section of BashFAQ #21:

result=${mystring/"$old"/$new}

Without the double quotes on the inside, $old would be interpreted as a fnmatch-style glob expression; with them, it's literal.


To operate on streams instead, consider gsub_literal, also described in BashFAQ #21:

# usage: gsub_literal STR REP
# replaces all instances of STR with REP. reads from stdin and writes to stdout.
gsub_literal() {
  # STR cannot be empty
  [[ $1 ]] || return

  # string manip needed to escape '\'s, so awk doesn't expand '\n' and such
  awk -v str="${1//\\/\\\\}" -v rep="${2//\\/\\\\}" '
    # get the length of the search string
    BEGIN {
      len = length(str);
    }

    {
      # empty the output string
      out = "";

      # continue looping while the search string is in the line
      while (i = index($0, str)) {
        # append everything up to the search string, and the replacement string
        out = out substr($0, 1, i-1) rep;

        # remove everything up to and including the first instance of the
        # search string from the line
        $0 = substr($0, i + len);
      }

      # append whatever is left
      out = out $0;

      print out;
    }
  '
}

some_command | gsub_literal "$search" "$rep"

...which can also be used for in-place replacement on files using techniques from the following (yet again taken from the previously-linked FAQ):

# Using GNU tools to preseve ownership/group/permissions
gsub_literal "$search" "$rep" < "$file" > tmp &&
  chown --reference="$file" tmp &&
  chmod --reference="$file" tmp &&
  mv -- tmp "$file"


标签: regex bash shell