4

I'm developing a POSIX compliant script in which I want to do a dynamic replace of strings, something like:

sed "s/${pattern}/${replace}/g"

Problem: if the pattern includes the delimiting character, sed returns error, and I want to be able to use all possible characters.


  1. Attempt (with regex): using a non printable delimiter character:

    ( Edited to correct syntax)

    d=$(printf '\1')
    sed "s${d}${pattern}${d}${replace}${d}g"
    

    Probably not portable.


  1. Attempt (with regex): if pattern contains a certain delimiter, uses another delimiter

    (edited: now in POSIX-WAY):

    test "${pattern#*$delim}" != "${pattern}" && delim='@'
    sed "s${delim}${pattern}${delim}${replace}${delim}g"
    

    Not bad but if the pattern contain both / more @ sed would return error.


  1. Edited Solution (without regex): Following this, escaping chars related to regular expressions.

    pattern=$(printf '%s\n' "${pattern}" | sed 's:[][\/.^$*]:\\&:g') 
    replace=$(printf '%s\n' "${replace}" | sed 's:[\/&]:\\&:g;$!s/$/\\/')
    

Is there a better POSIX compliant solution with or without sed?

  • I would use awk – RomanPerekhrest Oct 13 '17 at 12:12
  • 1
    Replacing a string or regex? If the former, you'll want to escape regex metacharacters, too. I'd use one pass to escape all occurrences of / (and others?) in the pattern-to-be-matched-against (and its replacement); then that sed command is sufficient. Unless of course the pattern-to-be-matched-against contains newlines. If, on the other hand, you care only about fixed strings, Pascal strings (i.e. those with a length prefix) and some extremely simple C code can get you what you want – Fox Oct 13 '17 at 12:24
  • See also case pattern in (*"$delim"*) ;; (*) delim=@; esac. Note that "Solution 3" is POSIX. Does that not work for you? – Stéphane Chazelas Oct 13 '17 at 16:23
  • Okay I've posix it, thanks for the corrections :-) – Emmett Brown Oct 13 '17 at 16:23
  • 1
    If you want to use character with code 1 as delimiter, POSIXly, you'd need c=$(printf '\1'); sed "s$c$pattern$c$replacement${c}g", but POSIX doesn't give you any guarantee that that character can be used as delimiter. It could be the newline or slash or @ or backslash character for instance. (not the case in practice though). – Stéphane Chazelas Oct 13 '17 at 16:26
  • Fox: C is attractive, at the moment I'm with sh but in the future I'll work with POSIX C.

    Stéphane and Fox: Solution 3 works and is fine for me because I need to do it in both ways with and without regex, as my question was about strings so it's okay as well. You're right about the non-printable characters, it's nothing portable, I'll look about for some way to do it.

    – Emmett Brown Oct 13 '17 at 17:01

0 Answers0