If your login shell can't be executed on some machine, then you can't log into it over SSH, or by most other methods for that matter. The SSH server always executes your login shell. If you pass a command on the
ssh command line then the login shell is executed with
-c and the command string¹ as arguments; otherwise the login shell is executed as a login shell with no argument.
If there was a way to bypass the login shell, that would be a security hole. An account can be configured as a restricted account by making its login shell a program that only performs one specific task; for example, the login shell could be
git-shell to allow only access to a git repository, or
To log in to that machine, you'll need to either arrange for
/bin/zsh to be present, or change your login shell to something that is present.
What I recommend in a heterogeneous environment like this is to stick to
/bin/sh as your login shell, because it's present everywhere. Set the
SHELL environment variable to
/bin/zsh if it's present, that way you'll get zsh as an interactive shell.
if [ -x /bin/zsh ]; then
While you're at it, this lets you avoid hard-coding the path to
if SHELL=$(command -v zsh); then
To get zsh to run automatically for a text mode login, invoke it from your
.profile. If you want to use
.zprofile to set things up, make it a login shell (but then you won't get the same environment on machines where zsh isn't present, so I don't recommend this). Do this only if this is an interactive login, not when your
.profile is executed by a script, during GUI mode login, etc.
if case $- in *i*) true;; *) false;; esac && # interactive shell
[ -z "$ZSH_VERSION" ] && # not running zsh yet
type zsh >/dev/null 2>/dev/null; then # zsh is present
¹ The SSH client concatenates its non-option arguments with spaces in between, and sends the resulting string through the connection. The SSH protocols defines the command as a string, not a list of strings.