The confusion here stems from a misunderstanding of what it means to be an interactive shell.
A Z shell is not interactive just because it happens to be running a prompt-read-execute loop. One can have a shell that is not interactive running a prompt-read-execute loop, reading its commands from a file or from a pipe. cat | zsh
yields exactly that.
A Z shell is interactive if it has no command or script to run (i.e. it drops into that prompt-read-execute loop) and its standard input is a terminal. This is a decision made by the shell program at startup. Lots of things are affected by the decision and are consequences of it, such as what startup files to process and the initial values of the $-
shell variable and interactive
shell option. See the manual for all of the various things that occur "in an interactive shell".
There are some very slight variations among shells on the exact rule used. Some (such as the Z shell here) only test standard input to see whether it is a terminal. The Single UNIX Specification says that both standard input and standard error should be checked. zsh 2>&1 | cat
yields an interactive Z shell, but ksh 2>&1 | cat
will yield a non-interactive Korn shell.
The Z shell also decides whether it goes into a prompt-read-execute loop, runs a command, or executes a script. That is a separate decision, whose result feeds into the am-I-interactive decision. All three choices can be either interactive or non-interactive, with use of the -i
option.
The -i
option is simply a way of forcing a shell to think that it is interactive, even if its standard input is not a terminal, or it is given a command or a script to run. It is a bit tricky to demonstrate that with a pipeline such as cat | zsh -i
because ZLE causes some odd behaviour (more on which at https://unix.stackexchange.com/a/434839/5132) that muddies the water a bit. But you can see it with ksh -i 2>&1 | cat
which yields an interactive Korn shell.
The -s
option is similarly present to override the other (what-to-do) decision, based when not overridden upon the presence of a first argument, to execute a script rather than run a prompt-read-execute loop. The -c
option affects that other decision, too.
zsh wibble
tries to execute a script, and is non-interactive because of that.
zsh -s wibble
does not try to execute a script but forces the what-to-do decision to choose to enter a prompt-read-execute loop, and (when standard input is a terminal) is interactive.
zsh -c wibble
is non-interactive, because it is running a command.
The combinations fall out logically from here.
zsh -si wibble
is still (likewise) interactive, the override forcing the am-I-interactive decision the same way that it is being made anyway (because -s
is forcing the choice of going into the prompt-read-execute loop).
zsh -i wibble
tries to execute a script, but the script (if it looks) will think that it is an interactive shell and the interactive EOF behaviour will occur when the Z shell reaches the end of the script.
zsh -ci wibble
still runs a command, but if that command looks (if it is a builtin or a shell function) it will think that it is an interactive shell.
… with two exceptions:
zsh -cs wibble
runs a command, in a shell that thinks that it is non-interactive.
zsh -csi wibble
likewise runs a command, in a shell that thinks that it is interactive.
-c
effectively overrides -s
. (Microsoft/IBM/JPSoftware command interpreters have a /K
option that runs a command then drops into the prompt-read-execute loop. This would be the logical combination of -c
and -s
, but that was never done in the world of Unix shells.)
zsh
is only "interactive by default" if its stdin is a tty. You may want to run an interactive zsh when that is not the case. Just tryecho val | zsh -i
, then at the prompt enterread key; echo $key
. Got it? – Nov 12 '19 at 00:28echo val | zsh -i
– schily Nov 12 '19 at 14:54zsh
as a compliant shell. It is just too different from a real shell :-( – schily Nov 13 '19 at 10:26