-1

I encountered some unexpected behaviour of my bash. This is my in-/output:

nepumuk@pc:~$  type URL
URL is a function
URL () 
{ 
    echo -e "${_//%/\\x}"
}
nepumuk@pc:~$  URL %2f
URL
nepumuk@pc:~$  URL %2f
/
nepumuk@pc:~$  URL %2F
/
nepumuk@pc:~$  type URL
URL is a function
URL () 
{ 
    echo -e "${_//%/\\x}"
}
nepumuk@pc:~$   URL %2f
URL
nepumuk@pc:~$  URL %2f
/
nepumuk@pc:~$  URL %2f
/
nepumuk@pc:~$  URL %2f
/
nepumuk@pc:~$    URL %2f
/
nepumuk@pc:~$   URL %2f
/

The corresponding part in the bash function file is:

url() {
        : "${*//+/ }"
        echo -e "${_//%/\\x}"
}

export -f url

URL() { echo -e "${_//%/\x}" }

export -f URL

During testing I changed the content of URL to echo -e "${@//%/\\x}" to see wether something changes. Opening a new terminal (emulator?) window with the latter being used, I get

nepumuk@pc:~$  URL %2f
/usr/share/bash-completion/bash_completion
nepumuk@pc:~$  URL %2f
/
nepumuk@pc:~$  URL %2f
/

Although I could fully reproduce (and use the former for it gives the desired output), but I'd be happy having an explanation.

Nepumuk
  • 687

1 Answers1

3

In bash the $_ variable expands to the last argument of the previously run command (among other things).

url() {
   : "${*//+/ }"
   echo -e "${_//%/\\x}"
}

Makes some sort of sense as the expansion of ${*//+/ } is first passed as one any only one argument to the : no-op command.

That's intended to replace +s with spaces in the positional parameters and concatenate the resulting strings with the first character of $IFS inbetween (the usual effect of $* when quoted).

Then the second command takes that resulting string, replaces the %s with \x in it and passes the result to echo.

In bash, when the posix and xpg_echo options are not both enabled, its echo builtin supports a -e option which can be used to expand some escape sequences, some of those are \xHH which expands to the byte with value corresponding to the 0xHH hexadecimal number. So it will replace %HH with the corresponding byte value so in effect do URI %XX decoding.

If you remove the first : command though, $_ in the second command will be whatever last argument was passed to the command run before that function was invoked.

Here it seems whoever wrote that function used that $_ trick to avoid having to use a temporary variable. That however makes the code unnecessarily complicated and hard to read and understand.

uri_decode() {
  local IFS=' ' # make sure arguments are joined with spaces rather than
                # make some assumption on what $IFS may currently contain

local tmp="$*" # positional parameters joined with spaces

tmp=${tmp//+/ } # replace +s with spaces

tmp=${tmp//[%]/\x} # replace % with \x. Using [%] instead of % for # increased portability.

printf '%b\n' "$tmp" # pass the result to printf for expanding of the \x # sequences, avoiding echo whose behaviour depends on # the environment, and would give incorrect results # for strings like -Enee }

And if you want a variant that doesn't do the + -> space translation, just remove the tmp=${tmp//+/ } part.