how to replace "/" in a POSIX sh string
Asked Answered
A

2

16

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?

Antirachitic answered 20/2, 2014 at 15:51 Comment(1)
For what it's worth, ash implements this nowadays.Sterne
H
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')
Hejira answered 20/2, 2014 at 15:58 Comment(4)
@Antirachitic The POSIX shell language doesn't require it to work at all. Look at the list of expansions in pubs.opengroup.org/onlinepubs/009695399/utilities/… -- you'll see that ${foo//bar/baz} isn't on the list.Hejira
@Antirachitic ...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.Hejira
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 (').Alain
@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.Hejira
A
5

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.

Alain answered 7/1, 2023 at 0:1 Comment(2)
This is the correct solution if strictly posix is needed. Clever and elegant.Hoopla
@FWDekker, the quotes you edited in aren't in Open Group's POSIX reference's examples, see pubs.opengroup.org/onlinepubs/9699919799/utilities/… I don't know if they're unsupported anywhere, but I'd rather keep the example conforming to the standard's example so I know it's POSIX. Do you mind if I unroll your edit?Alain

© 2022 - 2024 — McMap. All rights reserved.