1

I have a simple script involving a for loop from bash that I am trying to get working in zsh. I had assumed that the shebang would ensure a POSIX compliant shell would be used (on my system I have /bin/sh -> dash*) so there wouldn't be any issues.

MWE script where ITEMS is actually the output of a command that lists packages e.g. ITEMS=$(pip freeze):

#!/bin/sh

# ITEMS=$(pip freeze)  # Example of useful command

ITEMS="Item1
Item2
Item3"  # Dummy variable for testing

for ITEM in $ITEMS; do
    echo $ITEM
    echo Complete
done

This is the output when I try to run the script in zsh:

$ source scratch.sh
Item1
Item2
Item3
Complete  # Undesired

$ . ./scratch.sh
Item1
Item2
Item3
Complete  # Undesired

$ bash scratch.sh
Item1
Complete
Item2
Complete
Item3
Complete  # Desired

$ sh scratch.sh
Item1
Complete
Item2
Complete
Item3
Complete  # Desired

When I run it in a bash terminal it works fine. I think I've misunderstood how the shebang is interpreted by zsh? Can someone please explain to me how it should be used such that when I run source scratch.sh or . ./scratch.sh I have the same output as if I had run sh scratch.sh? I know I could modify my for loop script to be compliant with zsh and bash natively, but I want to use /bin/sh -> dash so I'm always using a posix compliant shell and don't have to worry about bashisms or zshisms.

Apologies if this is a basic question, I did search for zsh, posix and shebang but didn't find a similar question.

  • 1
    /bin/sh is a symlink to /bin/dash*. If you want it to run with dash, then just run it as ./scratch.sh. The shebang is already #!/bin/sh. – Nasir Riley Apr 15 '20 at 2:21
  • 1
    Besides, the echo $ITEM in your ./scratch.sh is asking for trouble -- eg. ITEM="-n \nq"; echo $ITEM manages to do something different in dash, bash, zsh and ksh. – mosvy Apr 15 '20 at 2:42
  • On most non-ancient systems -- when given as the command and not as an argument to some (other) command -- this syntax is interpreted by the kernel not zsh or any other shell, and for that reason is now called a kerbang. – dave_thompson_085 Apr 15 '20 at 2:52
  • The first UNIX clone UNOS from 1980 introduced user space support for #! in 1984 in their default shell. All other platforms I am aware of only support #! in the kernel and that won't help you. – schily Apr 15 '20 at 9:03
2

The shebang only has an affect if you execute the script directly without specifying how to run it; that is, with something like ./scratch.sh or /path/to/scratch.sh or by putting it in a directory in your PATH and just using scratch.sh.

If you run it using some other command, that controls what's done with it (overriding the shebang). If you use bash scratch.sh, it runs in bash; if you use zsh scratch.sh, it runs in zsh; if you use sh, it runs in whatever sh is on your system (dash in your specific case).

If you use source scratch.sh or . scratch.sh, it runs in the current shell, whatever that is. That's the entire purpose of the . and source commands. And again, the shebang is ignored here.

  • Awesome, makes perfect sense. I'll add a scripts folder to my path and go from there. – Alwin Apr 15 '20 at 3:07
2

You can't do that. . script or source script simply includes script, it doesn't fork a separate standard or non-standard shell to do it. As to shebangs, for zsh they're just comments.

You can instruct zsh to (try to) temporarily emulate a standard shell, though. YMMV.

emulate sh -c '. ./scratch.sh'

emulate which_sh -c str will evaluate str with the specified emulation temporarily in effect, and more importantly, will make it "stick" to any functions defined during the evaluation of str, causing the emulation mode to be turned on automatically during their execution.

  • While the shebang is intended for the kernel and meant to be ignored (treated as a comment here) by the interpreter, zsh like a few other shells, will try to interpret that shebang by itself if execve() fails with ENOEXEC. That allows running scripts on systems that don't support shebangs (rare these days), but also avoid running sh on the file like other shells including bash would if there is a shebang specified with a non-executable interpreter. – Stéphane Chazelas Apr 16 '20 at 14:03
  • My answer is just about sourcing the file in the current shell, not about running it as a separate process. Thanks about the -c, I'll add that to the answer. – mosvy Apr 16 '20 at 14:13
  • My first comment was a note in reply to As to shebangs, for zsh they're just comments. – Stéphane Chazelas Apr 16 '20 at 14:20
  • Feel free to improve the answer, and make it less awkward. – mosvy Apr 16 '20 at 14:26

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.