1

Using zsh 5.8.1 (x86_64-apple-darwin21.0), I have a script that relies on zsh's default 1-based array indexing, and I want to ensure that any previous user setting doesn't interfere with that. The best course of action seems to be to store the user's current KSH_ARRAYS setting, then unset this option, then reset the option to its original setting once I'm done. I have the following:

#!/usr/bin/env zsh if [[ -o ksharrays ]]; then echo "setting ksharrays_is_set to 1..."; ksharrays_is_set=1; else echo "setting ksharrays_is_set to 0..."; ksharrays_is_set=0; fi unsetopt KSH_ARRAYS; # here is where my script runs if [[ ksharrays_is_set -eq 1 ]]; then echo "setting the ksharrays option..."; setopt ksharrays; fi 

To test this, I run setopt ksharrays in my terminal, followed by my script. However, I'm still seeing setting ksharrays_is_set to 0... echo'ed to the terminal.

When I add setopt ksharrays to line 2 (directly under the shebang), I see setting ksharrays_is_set to 1... and setting the ksharrays option... as expected.

Why is the option not being picked up when I set it in the terminal, but it is when I set it in the script?

EDIT: I also tried adding setopt ksharrays to both my ~/.zshrc and ~/.zshenv and sourceing those files, followed by running the above script. This, too, resulted in setting ksharrays_is_set to 0... as the output, rather than the expected output. I confirmed that the setopt command took effect by running it without args in my terminal:

$ setopt combiningchars interactive ksharrays login monitor promptsubst shinstdin zle 

EDIT 2: I add echo $(setopt) to the top of the script. The only thing that prints out is nohashdirs. I don't see combiningchars, interactive, or any of the other options that appear when I run setopt without args directly in my terminal.

3
  • I don't know zsh but would you also need to export ksharrays in your terminal for it to be passed into the script? Commented Aug 31, 2022 at 15:39
  • I doubt it, ksharrays is a shell option, not an environment variable. Commented Aug 31, 2022 at 15:51
  • @thrig I had assumed that a user's zsh shell options would influence any script run within that shell. If that's not the case, i.e. if options in interactive mode are separate from options inside a script, let me know. I'm new to shell scripting so that could be one of my "unknown unknowns". If so, I'd be curious to read more about that from the docs if you can point me in the right direction. Note that the zsh docs on options don't appear to discuss this one way or the other- that's the first place I checked before posting here. Commented Aug 31, 2022 at 19:40

1 Answer 1

1

When you run a program from another program, each program is its own thing. Just because you're running zsh from zsh doesn't make the second zsh somehow inherit certain settings from its parent.

You've written a zsh script. So when you execute /path/to/your/script, the operating system sees that the file starts with a shebang line and runs zsh from the command search path. It doesn't care who started that process. The new instance of zsh starts with default setting. Any change of settings such as zsh options would have to come from somewhere, such as the command line or a configuration file or the contents of your script.

There are certain settings that are inherited when a program calls another program, but they're settings that apply to all programs, not just to zsh. Mainly, environment variables are inherited. Zsh does not set options based on environment variables.

It would be completely different if you were using the . or source builtin from zsh. That would instruct zsh to read commands from a file as though they had been written in the file containing the . command. Unfortunately, there's no well-established terminology to distinguish those two cases: both are called “scripts”. A script can be executed (run /path/to/script from any program) or sourced (run . /path/to/script from zsh); the same file could be used in either way, but most scripts are only meant to be used in one way. Your script is a standalone program meant to be executed.

Going back to what happens when zsh starts to run a script that's being executed: when zsh runs a script, it doesn't load .zshrc (that's only for when zsh runs interactively), but it does load /etc/zshenv and ~/.zshenv. If you put setopt ksh_arrays, your script will pick up that setting. Putting any setopt command (or most anything really) in .zshenv is a terrible idea that will break scripts.

Once your script exits, nothing cares (or even remembers) what zsh options it enabled. So if you want to enable ksh_arrays for all the code in your script, just run setopt ksh_arrays near the beginning of the script, end of story.

If only part of your code needs certain options, put that part in a function and set the local_options option in your function. For example, to enable ksh_arrays while a function executes and restore the default setting when it exits, put setopt local_options ksh_array near the top of the function. Furthermore, code that needs to change certain options would often also break if certain other options were changed from the default. So when zsh code needs to run in a context where options may have changed from the default, put that code in a function and run emulate -LR zsh in that function. This makes the function run with default options, and also with local_options enabled so any changes to options will be undone when the function exits. Some code should obey certain ”end-user“ options such as mark_dirs; for such code, use emulate -L zsh (i.e. drop the -R) option. This is the right thing for completion function, for example.

3
  • Thanks, emulate -LR zsh seems to solve my immediate issue! For my own edification, is there something special about the combination of putting both the code and the local_options snippet inside a function, which wouldn't happen if I added the local_options snippet to a plain shell script? Commented Aug 31, 2022 at 23:45
  • Also, separate question RE: tab completion. Let's say I have a tab-complete function which was previously defined inside a file that has been source’ed. This file included the call to register the function via compctl -K _command_name command_name. In this case, I gather that the shell options which are available to the _command_name function aren’t actually evaluated until the function is called from my terminal tab. In other words, it doesn't matter what the shell options were when the script was sourced, only what they were when I hit the tab key. Is that correct? Commented Sep 1, 2022 at 2:24
  • 1
    @RichieThomas local_options or emulate -L is only useful inside a function. It's pointless if you want to set options for the whole executable script. Its effect is not limited to a sourced script. WRT function definitions, what matters is the options when the function is executed, which might indeed not match the options when the function was defined. Commented Sep 1, 2022 at 10:22

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.