15

To replace substring in the bash string str I use:

str=${str/$pattern/$new}

However, I'm presently writing a script which will be executed with ash.

I have a string containing '/' and I want to use the above syntax inorder to replace the '/' in my string but it does not work.

I tried:

str=${str///a}
str=${str/\//a}
str=${str/'/'/a}

But they do not work

How I can fix that?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Anis_Stack
  • 3,306
  • 4
  • 30
  • 53

2 Answers2

20

This parameter expansion is a bash extension to POSIX sh. If you review the relevant section of IEEE standard 1003.1, you'll see that it isn't a required feature, so shells which promise only POSIX compliance, such as ash, have no obligation to implement it, and no obligation for their implementations to hew to any particular standard of correctness should they do so anyhow..

If you want bash extensions, you need to use bash (or other ksh derivatives which are extended similarly).

In the interim, you can use other tools. For instance:

str=$(printf '%s' "$str" | tr '/' 'a')

or

str=$(printf '%s' "$str" | sed -e 's@/@a@g')
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • 1
    @Anis_Stack The POSIX shell language doesn't require it to work at all. Look at the list of expansions in http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html -- you'll see that `${foo//bar/baz}` isn't on the list. – Charles Duffy Feb 20 '14 at 16:04
  • @Anis_Stack ...and since `ash` only promises to be compatible with POSIX sh, and POSIX sh doesn't require the feature, any implementation that's only half-baked isn't even a bug, it's just unspecified behavior. – Charles Duffy Feb 20 '14 at 16:06
  • Some old versions of sed need an endline, or they don't give any output, thus `str=$(printf '%s\n' "$str"|tr / a)` . Trivial strings like `/` or `a` don't need to be escaped with apostrophes (`'`). – Jaakko Salomaa Jan 06 '23 at 23:16
  • @JaakkoSalomaa, I agree, but it's better to encourage overquoting rather than underquoting. Format strings in particular often contain backslashes, and those _do_ need to be in single quotes to be treated literally; encouraging habitual use means folks don't make mistakes in other common cases. – Charles Duffy Jan 12 '23 at 21:17
1

POSIX string substitutions can be used to create a 100% POSIX compatible function that does the replacement. For short strings, this is considerably faster than command substitution, especially under Cygwin, whose fork(2) copies the parent process's address space on top of creating processes being generally slow in Windows.

replace_all() {
    RIGHT=$1
    R=

    while [ -n "$RIGHT" ]; do
        LEFT=${RIGHT%%$2*}

        if [ "$LEFT" = "$RIGHT" ]; then
            R=$R$RIGHT
            return
        fi

        R=$R$LEFT$3
        RIGHT=${RIGHT#*$2}
    done
}

It works like this:

$ replace_all ' foo bar baz ' ' ' .
$ echo $R
.foo.bar.baz.

With regards to performance, replacing 25% of characters in a 512 byte string runs roughly 50 times faster with replace_all() than command substitution under the Cygwin dash(1). However, the execution time evens out around 4 KiB.

Jaakko Salomaa
  • 106
  • 1
  • 4