2

Update Looking through this link, I updated the array subscript to start with 1 instead of 0 and seems like the array variable is read properly.

#!/bin/bash

#Root folders for Git update.
    ROOT[1]="/Users/ayusman/dev/repos1"
    ROOT[2]="/Users/ayusman/dev/repos2"

#do some things with the ROOT variable.

But I start seeing errors else where for my previously declared shell functions not being recognized.

command not found: git

So I think the generalized question is:

How do I port my perfectly running shell scripts written to run in bash shell to zsh shell?

I recently upgraded my Mac to macOs Catalina 10.15.2

One of my shell scripts that looks like this:

#!/bin/bash

#Root folders for Git update.
    ROOT[0]="/Users/ayusman/dev/repos1"
    ROOT[1]="/Users/ayusman/dev/repos2"

#do some things with the ROOT variable.

This used to work fine when my default shell was a bash shell. All I am doing here is adding a few folders to an array variable that I am going to use in the shell script. Post the upgrade and making my default shell as zsh, I see the following error when I start the script (through an alias in my .zsrc file)

[ayusman@~10:39AM]$updateExpediaGitRepos 
/Users/ayusman/scripts/updateGitRepos.sh:250: ROOT: assignment to invalid subscript range
[ayusman@~10:39AM]$

I verified that when I change my shell back to bash (by doing chsh like below), the same script runs as expected.

chsh -s /bin/bash

So essentially the question is can I not use my existing bash scripts as it is and run from a zsh shell?

Thanks, Ayusman

  • What's your zsh version? – Inian Jan 17 at 5:23
  • I simply echoed the $ZSH_VERSION It's 5.7.1 – Ayusman Jan 17 at 5:25
4

It sounds like you're asking zsh to interpret that script. You don't say what updateExpediaGitRepos is, but I suppose that alias sources that script, using the . or source builtin command. Those are the commands to tell the current shell interpreter to interpret code in a file, so the shebang (#! /bin/bash) is not relevant. The shebang is only used by the kernel when you try to execute a script.

Presumably, that updateGitRepos.sh is used to customize the user's environment by modifying attributes of the user's shell (like setting environment variables, defining functions, aliases...), in which case executing it would not help.

bash and zsh are interpreters for two different and incompatible languages (though they have things in common as they both took a lot of inspiration from the Bourne shell, Korn shell and C shell). You can't generally expect one to be able to interpret code written for the other.

In particular, array implementations are very different.

zsh arrays are really arrays, whose indices start at 1 like in most other shells and tools typically used from the shell (more on that at Is there a reason why the first element of a Zsh array is indexed by 1 instead of 0?).

bash arrays are copied from the Korn shell. They are sparse arrays (more like associative array with keys limited to positive integers) and indices start at 0.

zsh supports a number of emulations which can be used to interpret code written for other shells. In particular, it's got a ksh emulation mode which could be useful for parsing bash scripts using arrays here.

emulate ksh

to start it.

Or:

emulate ksh -c 'some code'

To evaluate code in that emulation mode. Functions declared in that mode inherit the emulation mode.

Among other things, in the ksh emulation mode, the KSH_ARRAYS option is turned on which makes array indices start at 0 (but are still not sparse).

So here, you could try to modify your alias so that it does:

emulate ksh -c '. /Users/ayusman/scripts/updateGitRepos.sh'

instead of

. /Users/ayusman/scripts/updateGitRepos.sh

That may or may not be enough in your case. That emulation mode only improves compatibility with ksh, it doesn't make zsh a ksh clone. And ksh is not bash.

If updateGitRepos.sh is indeed intended to be sourced into the user's shell, then you should have different versions of the script for each of the supported shell each written in the corresponding language, or use a syntax that is compatible to all the supported shells, which generally precludes arrays (but see Test for array support by shell for possible approaches).

If that script is only meant to be executed, not interpreted by the user's interactive shell, then make sure you execute it, as /path/to/the/script which thanks to the shebang will make sure the right interpreter is started to interpret it, not source /path/to/the/script, not zsh /path/to/the/script (bash /path/to/the/script would also work here since it's a bash script (despite the misleading .sh extension)).

| improve this answer | |
3

The behavior is because of how arrays work in zsh shell, their indices start at 1 and not at 0. So any access to elements at index 0 will throw the error you are seeing.

From the zsh manual - 15.2.1 Array Subscripts

15.2.1 Array Subscripts

Individual elements of an array may be selected using a subscript. A subscript of the form [exp] selects the single element exp, where exp is an arithmetic expression which will be subject to arithmetic expansion as if it were surrounded by $((...)). The elements are numbered beginning with 1, unless the KSH_ARRAYS option is set in which case they are numbered from zero

If the KSH_ARRAYS option is not set, then by default accesses to an array element with a subscript that evaluates to zero return an empty string, while an attempt to write such an element is treated as an error. For backward compatibility the KSH_ZERO_SUBSCRIPT option can be set to cause subscript values 0 and 1 to be equivalent; see the description of the option in Description of Options.

So, to emulate the behavior of arrays starting with index 0, set this option at the top of the script

setopt ksh_arrays

To clarify why the script was exhibiting zsh behavior, even when the she-bang interpreter was set to #!/bin/bash. Possibly the script was ran with an explicit invocation as zsh <script> in which case the OS does not recognize the interpreter line at the top of the script. Once the script is made itself an executable (chmod +x) and ran with ./<script>, the interpreter provided in the top of the script would be honoured.

| improve this answer | |
  • thanks, I also landed on the same link. However, since I am explicitly mentioning at the start of the shell script that the shell is a bash shell; shouldn't that be honored? – Ayusman Jan 17 at 5:33
  • @Ayusman: That will not be honoured, if the script is run with an explicit interpreter as zsh <script-name>. Only if the script is made itself as an executable and ran with ./<script-name> the she-bang #! is parsed and the specified interpreter is used – Inian Jan 17 at 5:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.