I have an old application I've been using on Linux for a quite a while that I like very much, and I'd like to use it on macOS sometimes.

It's called hcm, and it lives at https://stromberg.dnsalias.org/~strombrg/hcm/

It's mostly about running commands on remote hosts (mostly X11 graphical applications, but some interactive shells), a little like putty, but with predefined hosts and commands.

I mostly use it to start terminal emulators that'll do a script(1)-like tty log locally, while giving interactive access to a remote host over ssh. Each new tab from a given terminal emulator gives a new tty logfile, and a new shell on the remote host again over ssh.

Is there a macOS terminal emulator, preferably an opensource one, that will respect $SHELL on each invocation? I gave iTerm2 and kitty a try, but they seem to ignore a test $SHELL. Alacritty wouldn't even run, with or without a test $SHELL, saying "Alacritty cannot be opened because the developer cannot be verified."

Suggestions please?


  • I got alacritty to run. Much the problem: $SHELL is ignored. alacritty has a -e option, but it's not tabbed.
    – dstromberg
    Apr 28, 2021 at 20:02
  • 1
    Something I don't see any other answers pointing out... your shell, be it a login shell or otherwise, could not care less about what you set $SHELL to. You have it exactly backwards. Shells set that variable, not the other way around. May 15, 2021 at 23:42
  • On Linux and almost all Unixes, $SHELL is set by the login process. Are you claiming macOS is an exception?
    – dstromberg
    May 16, 2021 at 23:55
  • I misspoke. Yes, login(1) sets $SHELL based on the user's record. But, nothing cares about $SHELL in specific other than where required by POSIX. $SHELL is a result. If hcm cares what it is set to, well, more power to it. But the shell you are typing commands into doesn't. May 17, 2021 at 2:48
  • Can‘t you just use Terminal with different profiles, or with aliases/shell functions to connect to remote hosts?
    – nohillside
    May 17, 2021 at 3:48

3 Answers 3


This answer is not using $SHELL, but achieves the same result and more easily.

iTerm2 profiles, as well as setting colour scheme, can use a specific shell for each profile.

As an example I have set the builtin Red Sands profile to bash and not the default login shell (zsh).

In the menu choose Profiles -> Open Profiles. enter image description here

Add profiles or modify them with "Edit Profiles...". Here is Red Sands with a custom shell.

enter image description here

Now return to the main iTerm2 window and, in the menu bar, choose Profiles -> Red Sands. This opens a new tab running bash.

enter image description here

Taking this a step further (you want to run ssh for remote access) you can run a command when starting the shell/tab. Here I have added an ssh command to my Red Sands profile.

enter image description here

So setup however many profiles you want for the different shells and actions you need.

Note: My iTerm2 expertise is at the beginner level. I am sure you can do much better than me and, if not, someone else will have a better answer.

  • This is pretty and novice-friendly, but it seems difficult to automate? Hopefully I'm wrong about that.
    – dstromberg
    Apr 28, 2021 at 16:15
  • @dstromberg Applescript (deprecated) or Python iterm2.com/python-api
    – Gilby
    Apr 28, 2021 at 21:55

It's looking like wezterm (https://github.com/wez/wezterm) might be what I'm looking for.

  • Sadly, wezterm has abandoned $SHELL. So I'm looking for this again.
    – dstromberg
    Dec 8, 2022 at 16:21

Maybe csshX (github).

Did a little searching around and that seems quite useful, although how close it is to what you were looking for, I don't know. It's in MacPorts.

$ csshX -help
    csshX [--login *username*] [--config *filename*] [ *[user@]host1[:port]*
    [*[user@]host2[:port]*] .. ]

    csshX [-h | -m | -v ]

    -l *username*, --login *username*
        Remote user to authenticate as for all hosts. This is overridden by

    -c *configfile*, --config *configfile*
        Alternative config file to use

    -h, --help
        Quick summary of program usage

    -m, --man
        Full program man page

    -v, --version
        Displays the version of csshX

    --screen *number or range*
        Sets the screen(s) on which to display the terminals, if you have
        multiple monitors. If the argument is passed a number, that screen
        will be used.

        If a range (of the format 1-2) is passed, a rectangle that fits
        within those displays will be chosen. Particularly odd arrangements
        of windows, such as "L" shapes will probably not work.

        Screens are numbered from 1.

    --space *number*
        Sets the space (if Spaces is enabled) on which to display the

        Default: *0* (current space)

    -x, --tile_x *number*
        (csshX only) The number of columns to use when tiling windows.

    -y, --tile_y *number*
        (csshX only) The number of rows to use when tiling windows. tile_x
        will be used if both are specified.

    --ssh *ssh command*
        Change the command that is run. May be useful if you use an
        alternative ssh binary or some wrapper script to connect to hosts.

    --ssh_args *ssh arguments*
        Sets a list of arguments to pass to the ssh binary when run. If
        there is more than one, they must be quoted or escaped to prevent
        csshX from interpreting them.

    --remote_command *command to run*
        Sets the command to run on the remote system after authenticating.
        If the command contains spaces, it should be quoted or escaped.

        To run different commands on different hosts, see the --hosts

    --hosts *hosts_file*
        Load a file containing a list of hostnames to connect to and,
        optionally, commands to run on each host. A single dash - can be
        used to read hosts data from standard input, for example, through a

        See HOSTS for the file format.

    --session_max *number*
        Set the maximum number of ssh Terminal sessions that can be opened
        during a single csshX session. By default csshX will not open more
        than 256 sessions. You must set this to something really high to get
        around that. (default: 256)

        Note that you will probably run out of Pseudo-TTYs before reaching
        256 terminal windows.

    --ping_test, --ping *number*
        To avoid opening connections to machines that are down, or not
        running sshd, this option will make csshX ping each host/port that
        is specified. This uses the Net::Ping module to perform a simple
        syn/ack check.

        Use of this option is highly recommended when subnet ranges are

    --ping_timeout *number*
        This sets the timeout used when the "ping_test" feature is enabled.

        Due to the implementation of Net::Ping syn/ack checks, this timeout
        applies once per destination port used. Also, if the number of hosts
        to ping is greater than the number of filehandles available pings
        will be batched, and the timeout will apply once per batch. You can
        set 'ulimit -n' to improve this performance.

        The value is in seconds. (default: 2)

    --sock *sockfile*
        Sets the Unix domain socket filename to be used for interprocess
        communication. This may be set by the user in the launcher session,
        possibly for security reasons.

        Sort the host windows, by hostname, before opening them.

    --slave_settings_set, --sss *string*
        Change the "settings set" for slave windows. See slave_settings_set
        below for an explanation of why you might do this.

    --master_settings_set, --mss *string*
        Change the "settings set" for master windows.

    -i, --interleave *number*
        (csshX only) Interleave the hosts that were passed in. Useful when
        multiple clusters are specified.

        For instance, if clusterA and clusterB each have 3 hosts, running
        csshX -tile_x 2 -interleave 3 clusterA clusterB

        will display as clusterA1 clusterB1 clusterA2 clusterB2 clusterA3

        as opposed to the default clusterA1 clusterA2 clusterA3 clusterB1
        clusterB2 clusterB3

    --debug *number*
        Sets the debug level. Number is optional and will default to 1 if

        Currently only one level of debug is supported. It will enable
        backtrace on fatal errors, and will keep terminal windows open after
        terminating (so you can see any errors).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .