user1773603
user1773603

Reputation:

Sourcing a .sh file under zsh : addvar:1: bad substitution error message

I have the following .sh file that I would like to be able to source under zsh, which is my default shell. The beginning of the file is (after there are only export variable = path) :

function addvar () {
local tmp="${!1}" ;
tmp="${tmp//:${2}:/:}" ; tmp="${tmp/#${2}:/}" ; tmp="${tmp/%:${2}/}" ;
export $1="${2}:${tmp}" ;
}

At the execution : $ source file.sh

I get the following error message :

addvar:1: bad substitution

Could anyone see what's wrong ?

ps : I tried to put directly at the top : #!/bin/bash and execute directly by $ ./file.sh

but it doesn't do anything (none variables exported).

Upvotes: 1

Views: 690

Answers (1)

John Bollinger
John Bollinger

Reputation: 181744

I have the following .sh file that I would like to be able to source under zsh, which is my default shell.

That filename extension suggests that the file is intended for a Bourne-family shell, but it says nothing about which one, and various members of the family have various incompatibilities with each other. If there is a shebang line (a line beginning with #! and appearing as the first line of the file) then it may give a more specific indication of the shell for which the file is intended, but that has functional relevance only when the file is executed directly, not when it is sourced.

I get the following error message :

addvar:1: bad substitution

Could anyone see what's wrong ?

The problem is with

local tmp="${!1}" ;

, and specifically with ${!1}. In Bash, this is an indirect variable reference, which expands to the value of the variable named by the expansion of ${1}. In Zsh, however, it is simply invalid, so you cannot use that code as-is in that shell.

It looks like the function is supposed to add the value specified as its second argument to the path-like value of the environment variable named by its first argument, removing any previous appearances of that value. Since you cannot use it as-is in Zsh, I would suggest just not. You might instead use a function that computes the appropriate updated value, and leaves the reassignment to the caller. Example:

add_path_element() {
    local tmp="${1//:${2}:/:}"
    tmp="${tmp/#${2}:/}"
    tmp="${tmp/%:${2}/}"
    echo -n "${tmp}:${2}"
}

# Instead of "addvar PATH /dir/to/add":
export PATH=$(add_path_element "$PATH" /dir/to/add)

It should also be possible to implement a version of the original addvar function that works in Zsh (and Bash) by use of the eval built-in, but I cannot recommend that approach to you because eval is very dangerous. I mention it here just in case someone else suggests it. As a basic rule, do not use eval until you are sufficiently expert at shell programming to thoroughly understand why you should not use eval.

Upvotes: 2

Related Questions