Linux Madness: Bash Substring Replacement

Wed 2014-02-05

So I was investigating some strange bug in nanoblogger where it would sometimes garble relative links, when I found the following gem in a shell script:

${BASE_URL//\//\\/}

where for example:

BASE_URL="../../../"

So what does this fence of / and \\ do except of being visually interesting?

We can test it interactively in a shell:

$ BASE_URL="../../../"
$ echo "${BASE_URL//\//\\/}"
..\/..\/..\/

So it seems to escape all our / as \/ -- but how?

To see this, one has to know how to do substring replacement in bash. In greater detail this is explained here: tldp.

To replace all matches of substring in string with replacement one has to run

${string//substring/replacement}
        AA         B

Here the // above AA means »replace all matches«, the / over B is used as a separator.

In our case we want to replace all occurrences of / in $string with an escaped \/.

But as / is used as a separator between substring and string we have to escape it as \/ (11).

To further complicate things, we want a literal backslash in our replacement, so we have to escape that one too as \\/ (222).

When we put everything together, we get our nice expression form the beginning:

${BASE_URL//\//\\/}
          AA11B222

TL;DR: Not only in perl is it possible to produce code in »write-only« style!

Tags:

This text by Ludger Sandig is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.