tech-userlevel archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: interactive shell detection in shrc



At Mon, 30 Sep 2024 22:52:18 +0200, tlaronde%kergis.com@localhost wrote:
Subject: Re: interactive shell detection in shrc
>
> But, if I'm not mistaken, the discussion was about testing if a shell
> is interactive, that is, "inheriting" whatever has been set and
> testing it.

Indeed, so by tradition a Unix shell session is started by one of any
number of mechanisms, including from "xterm -ls", and has a $SHELL
assigned and invoked, and then keeps a constant value for $SHELL until
that session ends.  Everything outside those parameters is out of scope,
such as manually running some other shell program.

This thread started to discuss detecting an interactive shell in the
likes of ~/.shrc, i.e. in the script file referenced by the expansion of
$ENV, specifically for NetBSD sh(1).

This is of particular importance for NetBSD's sh(1) (and also NetBSD
ksh(1)) because it(they) always source the expansion of $ENV even for
non-interactive shells.

$ENV is particularly useful for customising interactive sub-shells that
one might start from within some other application.  My favourite
example, and the method I use for testing this condition, is the "shell"
command in mail(1), or the ":sh" command in vi(1).

However for for non-interactive shells started by other programs, such
as make(1), i.e. typically via system(3), one usually wants to avoid
customisations!  For example that's the difference between mail(1)'s
"shell" and "|" commands -- the first is interactive, the second is not.

That's because such customisations may both impact performance of, and
possibly also "corrupt" the expected behaviour of, the shell in such a
way as to interfere with the proper and expected execution of
non-interactive programs.

Of course, as kre suggested, by always sourcing the result of $ENV
expansion NetBSD sh(1) offers the option of purposefully modifying the
behaviour of non-interactive shells, perhaps so that one can work around
bugs, or test new features, etc.

Note there is also the concept of a shell instance being a "login
shell", which generally amongst unixy shells suitable for login session
also affects which files it might source by default upon invocation,
such as /etc/profile and ~/.profile.  Traditionally this was all one
might desire for shell customisation.  However it restricted the ability
to make use of certain features in more featureful shells such as ksh
which required setup on a per-invocation basis (e.g. for things like
command aliases which cannot be exported via the environment), thus the
invention of $ENV and the eventual restriction in POSIX of only sourcing
$ENV for interactive (sub)shells such as those started from mail(1) or
vi(1).  I'll also note in passing that the need for interactive
sub-shells has been greatly reduced to almost nil by the advent of job
control as well as the proliferation of multi-session tools such as
windowing environments or the likes of tmux(1) and screen(1).

If I'm not mistaken NetBSD's sh(1) and ksh(1) (and pkgsrc pdksh(1) some
other pdksh derivatives, but not mksh(1)) are pretty much alone in
always expanding and sourcing $ENV (or some default file) even when
invoked non-interactively.

Bash(1) has $BASH_ENV, which is always sourced, but bash(1) also
supports $ENV in the POSIX manner for interactive-only shells.

In my vague memory AT&T Ksh once always sourced $ENV (thus why pdksh(1)
does as well), but ksh93(1) from pkgsrc/shells/ast-ksh is now documented
to have the POSIX behaviour.

Schily's bosh(1) (and obviously his pbosh(1)) also are POSIX compliant.

NetBSD's sh(1) is also pretty much alone in not having a default
filename to be used if $ENV is not set (and not null(empty)).  NetBSD
ksh(1), pdksh(1), and even mksh(1) if I understand it correctly, _do_ on
the other hand have default files which are used if $ENV is not set (and
not null, i.e. empty).

So, to sum up, for NetBSD sh(1) and ksh(1) specifically the following in
a script file referenced by the expansion of $ENV is sufficient:

	case "$-" in *i*) ;; *) return ;; esac

However for those of us who use other shells, either because they are
not (yet) compatible with POSIX Issue 8 (which is when the mandate for
including 'i' in $- first appears), or because of the second part of the
following rule in POSIX, we require a wee bit more.

	If the -i option is present, or if there are no operands and the
	shell's standard input and standard error are attached to a
	terminal, the shell is considered to be interactive.

To handle the second part of this rule we must compare $0 and $SHELL, as
the presence of any operands upon invocation will have changed $0 to
something other than $SHELL.

Also because of a long-standing "oversight" in NetBSD sh(1) some shells
we might ignore the stderr part of the test and just consider stdin (as
that's really all that matters for "interactivity", right?).

So this is a more portable, compatible, and hopefully sufficient test:

	sh_is_interactive=false
	case "$-" in
	*i*)
		sh_is_interactive=true
		;;
	esac
	if [ -t 0 -a "$0" = "$SHELL" ]; then
		sh_is_interactive=true
	fi
	if $sh_is_interactive; then
		: OK
	else
		return
	fi

It is need only in files referenced by $ENV, or any sourced by default
if $ENV is not set, by shells that always source those files even for
non-interactive shells.  On stock NetBSD this is just sh(1) and ksh(1),
but it may also include older AT&T Ksh, other pdksh(1), etc.  It would
matter for the Heirloom Shell, but since it doesn't support $ENV at all,
it does not actually matter for it.

--
					Greg A. Woods <gwoods%acm.org@localhost>

Kelowna, BC     +1 250 762-7675           RoboHack <woods%robohack.ca@localhost>
Planix, Inc. <woods%planix.com@localhost>     Avoncote Farms <woods%avoncote.ca@localhost>

Attachment: pgpnqO3gnSbfz.pgp
Description: OpenPGP Digital Signature



Home | Main Index | Thread Index | Old Index