- Package:
- base-files
- Source:
- base-files
- Description:
- Debian base system miscellaneous files
- Submitter:
- Christoph Anton Mitterer
- Date:
- 2021-06-10 20:33:03 UTC
- Severity:
- normal
Hi.
(CCing Georgios M. Zarkadas from #632887 for his comments on (3)
below, if any.)
Currently /usr/share/base-files/profile has:
if [ "${PS1-}" ]; then
if [ "${BASH-}" ] && [ "$BASH" != "/bin/sh" ]; then
and /usr/share/base-files/dot.profile has:
if [ "$BASH" ]; then
I think there are several issues with that, respectively possible
improvements:
1) I'd say, both should do the same (i.e. /etc/profile and ~/.profile),
so that should be aligned.
2) /etc/bash.bashrc already checks whether run interactively, so
if it's just for whether /etc/bash.bashrc shall be sourced or not, the
`if [ "${PS1-}" ]; then` is not really needed (other than avoiding the
sourcing).
But even then, using PS1 is IMO not optimal.
The variable may exist in a non-interactive shell (e.g. when exported
in a calling parent shell).
The following:
if [ -n "${-##*i*}" ] || [ -z "${-}" ]; then
return
fi
uses the shell options which, I guess, one can assume are always right.
The above code should be POSIXly correct and unlike some constructs
with `case` avoid the hypothetical issue of a future option "I" being
mistaken for "i", when bash's nocasematch option would be on.
3) The check for /bin/sh:
- Shouldn't that also be in .profile?
- Shouddn't it also cover the sourcing of /etc/profile.d/*.sh
(for the same reasons)?
- The check itself is IMO problematic:
bash's manual talks about the "sh-like-behaviour" when invoked as
`sh`, not necessarily as `/bin/sh`.
Also this would break, should sh ever go to /usr/bin/sh as part of
the usr-merge.
So, wouldn't it be better to check e.g.:
if [ -z "${0##*/sh}" ] || [ "$0" = sh ] ; then
#it's run as sh
?
I think $0 can never be empty, so there's no need to check that it's
actually set to something.
4) $BASH might be bogus.
Just export BASH, and then run dash...
Well, it's no our business to prevent users from shooting themselves,
at least not to every extent.
But one could do:
if [ -n "${BASH-}" ] && { [ "$(ps -p $$ -o exe=)" = /usr/bin/bash ] || [ "$(ps -p $$ -o exe=)" = /bin/bash ]; }; then
That has the short-circuit that if BASH is not there, don't source bashrc.
So there should be nearly no speed impact for shell scripts.
But if it's there, the above would also check whether the executable
is actually /usr/bin/bash or /bin/bash.
The above is not fully POSIX-compatible (POSIX ps has no "exe").
Calling ps twice is a bit ugly... but I see no other way than using
a helper variable. Maybe the `i` can be used, which is already used below
when sourcing /etc/profile.d/*.sh .
Also, ps isn't guaranteed to be there (procps isn't essential).
So either one would need to check for that... or e.g. use /proc/$$/exe
Cheers,
Chris.
Oh and I forgot: Mine also doesn't set the PATH. This seems to be done already by someone else (PAM?) and again, it would only work for login-shells but not for e.g. desktop sessions or shells spawned in there.
Taking that one back,... /etc/profile.d/*.sh is obviously meant to me
like /etc/profile, which would also get sourced when invoked as `sh`.
I myself use now the following for /etc/profile and .profile (just with
/etc/bash.bashrc replaced with ~/.bashrc:
-----------------------------------------------------------------------
#check whether any internal variables used in this script have already been set and unset them
if [ -n "${profile_p+is_set}" ]; then
printf 'Warning: The variable `profile_p` had already been set when executing `%s` and will be unset/overwritten.\n' '~/.profile' >&2
unset -v profile_p
fi
#source `/etc/bash.bashrc`
if [ -z "${-##*i*}" ] && [ -n "$-" ]; then
#when executed in an interactive (login) shell …
# The existence of a non-empty variable `BASH` is merely an indicator that the
# shell might be bash. This serves as a rapid test.
# However, its non-existence guarantees that the shell isn’t bash.
if [ -n "${BASH-}" ] && { [ -n "${0##*/sh}" ] && [ "$0" != 'sh' ]; }; then
#… that is (presumably) bash and not invoked as `sh` and …
# `ps`, which is part of the non-essential package `procps`, may not be
# available. `realpath`, which is part of the essential package `coreutils`, is
# guaranteed to be available.
if { [ -x /bin/ps ] && { profile_p="$( /bin/ps -o exe= -p $$ 2>/dev/null )" || profile_p=''; [ "${profile_p}" = /usr/bin/bash ] || [ "${profile_p}" = /bin/bash ]; }; } || \
{ [ -L /proc/$$/exe ] && { profile_p="$( /usr/bin/realpath --canonicalize-existing /proc/$$/exe 2>/dev/null )" || profile_p=''; [ "${profile_p}" = /usr/bin/bash ] || [ "${profile_p}" = /bin/bash ]; }; }; then
#… it’s actually bash
if [ -f /etc/bash.bashrc ]; then
. /etc/bash.bashrc
fi
fi
fi
fi
#source `/etc/profile.d/*.sh`
if [ -d /etc/profile.d ]; then
for profile_p in /etc/profile.d/*.sh; do
if [ -f "${profile_p}" ]; then
. "${profile_p}"
fi
done
fi
#cleanups
unset -v profile_p
-----------------------------------------------------------------------
That also no longer sets PS1 as /etc/profile does right now. This would
anyway just get used in login shells and just for non-bash... and it
seems e.g. dash sets #/$ automatically.
But of course one could just add the `else` back above.
It also uses -f instead of -r when sourcing /etc/profile.d . I think it
makes more sense to check whether these are regular files and actually
give and error if they're not readable.
Maybe one can just drop the {...} that uses ps to find out which binary
is used by the shell - ps also needs /proc.
Also, this might not work for HURD/kFreeBSD ... so either just not
check it at all, or one might need to add some alternative way for
those.
Well, pick whatever you like :-D
Cheers,
Chris.