Setting up an enriched, system-wide prompt again raised the question where to put the definition code. Spreading the snippet all over the home-located profile-(
.bash_profile
) and resource(.bashrc
)-files is a bad idea, tedious to maintain, you know, don’t ever think about it.
Changing the global profile- and resource-files (/etc/profile , /etc/bashrc
) won’t be of any much more brain either, since yet another system update may cause conflicts with the maintainers version. Just reading along the web, I found that there is some mechanism integrated with /etc/profile
to source homebrewed definitions from a file within /etc/profile.d
. Great, this is what I want, but, as far as I remember correctly, a profile will only be sourced once, upon login, in a login shell, right?!
Oohhm, still can’t recite that poem on command. On login, we source the environment (profile). We won’t do that for sub-shells because the environment will be derived. However, since a login shell is also an interactive shell usually, we still addionally source the aliase- und function-resources as well… And so on and forth in bare theory, which has been depicted nicely in man bash, in the invocation section in particular. Read this now for reference!
On the other hand, living system may not stay with the initial ideas very long, in practice a question may have a multiple of answers (or the other way round). So I parsed and instrumented the profile- and resource-loading process to see what really happens under the bonnt. See what I’ve found.
Let’s do some wording first. We have two types of shells essentailly, login and interactive, as well as their counterparts. Again, refer the the bash
man page on this. The interesting thing is though, some shells may be both login and interactive or login and non-interactive etc. Ok, this is just two-dimensional, a matrix representation of possible cases will fit this accordingly for the remainder of the article.
ls | nls | is | nis | |
---|---|---|---|---|
ls | – | 0 | a | b |
nls | – | c | 0/d | |
is | – | 0 | ||
nis | – |
We conclude easily that the antagonists ls/nls and is/nis can’t go togehter, they’re 0 and 1. Anythings else is possible, for the exception of nls/nis, which may be shown but will have no effect in regard to profiles and resources, discussed here.
The following section gives examples of the shell type combinations from above. Please note, that an echo ''
has been coded at the very top of /etc/profile , /etc/bashrc , .bash_profile , .bashrc
and /etc/profile.d/local.sh
, respectively. The latter file being my extension script to set the enriched prompt (and some aliases) as mentioned above. The global files also print the current value of $PS1
, an obviously important marker in shell invocation load logic, as we’ll see further down. .bash_profile
just sources .bashrc
which in turn sources /etc/bashrc
, that’s the initial setup from the home skeleton.
Case a) login and interactive
Using username "risco". Last login: Mon Sep 24 21:19:21 2018 /etc/profile (ps1: \s-\v\$ ) /etc/profile.d/local.sh (ps1: \s-\v\$ ) .bash_profile .bashrc /etc/bashrc (ps1: \[\e[1;33m\][\u@\H(\l|\A) \[\e[0;37m\]../\W\[\e[1;33m\]]\[\e[0;37m\]\$\[\e[0m\] )
Ok, it’s a login shell, so profiles served first. $PS1
already set on the call to /etc/profile
. /etc/profile
also calls /etc/profile.d/local.sh
to reset the prompt. .bash_profile
enters into .bashrc
and /etc/bashrc
for an interactive shell. Nice.
Case b) login and non-interactive
[root@10.0.2.15(0|21:52) ../~] # su -l risco -c "alias | grep which" /etc/profile (ps1: ) .bash_profile .bashrc /etc/bashrc (ps1: ) alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
Login shell, profiles loaded, ok. Notably first, $PS1
has not been and will not be set furthermore. That’s ok for a non-interactive shell. .bashrc
and /etc/bashrc
will still be sourced due to .bash_profile
. That’s against the rules, right. However, inspecting /etc/bashrc
shows if-then logic based on the existence of $PS1
. Seems, as if the theory leak, bridging .bash_profile
and .bashrc
, gets kind of fixed up here. /etc/bashrc
right has another if-then block apparently, only getting active for non-login shells (shopt
, see below). And last but by no means least, the non-login shell if-then block of /etc/bashrc
does in fact feature the same (not even truly the same) local extension script loader as /etc/profile
… That’s kind of wired, my dear round iron stove!!!!! So has /etc/profile.d/local.sh
been loaded actually? There’s no echo output… It has, by /etc/profile
. Since the local extension script loader in /etc/profile
and, as we’ve found now, /etc/bashrc
redirects to /dev/null
for non-interactive shells, there’s no visible feedback. This is why I did the alias grep
on which, an alias setting executed with /etc/profile.d/which.sh
.
Case c) interactive
[risco@10.0.2.15(0|21:56) ../~] $ bash .bashrc /etc/bashrc (ps1: \[\e[1;33m\][\u@\H(\l|\A) \[\e[0;37m\]../\W\[\e[1;33m\]] \[\e[0;37m\]\$\[\e[0m\] ) /etc/profile.d/local.sh (ps1: \[\e[1;33m\][\u@\H(\l|\A) \[\e[0;37m\]../\W\[\e[1;33m\]] \[\e[0;37m\]\$\[\e[0m\] )
Ok, no profiles, not a login shell. So only resources and the extension script by the non-login shell if-then block of /etc/bashrc
.
Case d) non-login and non-interactive
[risco@10.0.2.15(0|22:02) ../~] $ bash -c "alias | grep which" n/a
As mentioned above, this is only sort of a mentionable combination. Its possible but nothing gets loaded here.
Case forced
Please note nevertheless, that bash
may be forced to act as a certain shell type by setting the -i
or -l
options like this.
[risco@10.0.2.15(0|22:02) ../~] $ bash -i -c "alias | grep which" .bashrc /etc/bashrc (ps1: \[\e[1;33m\][\u@\H(\l|\A) \[\e[0;37m\]../\W\[\e[1;33m\]] \[\e[0;37m\]\$\[\e[0m\] ) /etc/profile.d/local.sh (ps1: \[\e[1;33m\][\u@\H(\l|\A) \[\e[0;37m\]../\W\[\e[1;33m\]] \[\e[0;37m\]\$\[\e[0m\] ) alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
[risco@10.0.2.15(0|22:02) ../~] $ bash -l -c "alias | grep which" /etc/profile (ps1: ) .bash_profile .bashrc /etc/bashrc (ps1: ) alias which='alias | /usr/bin/which --tty-only --read-alias --show-dot --show-tilde'
So, what’s the lessons learned? Profile loading with login shells does work as theory suggests, almost. Resource loading takes place anytime, due to the .bash_rofile
to .bashrc
and /etc/bashrc
shortcut. /etc/bashrc
then has if-then logic to act separately upon interactive and non-login shells only. This is what they use to determine an interactive and a non-login shell, respectively.
[root@10.0.2.15(0|22:11) ../~] # if [[ "$PS1" ]] ; then echo "1" ; fi 1 [root@10.0.2.15(2|18:19) ../~] # if (! shopt -q login_shell) ; then echo "1" ; fi 1
What else? To me, some extension script in /etc/profile.d
, yet to set an enriched and recolored prompt, like this time, shall evenly employ if-then logic to identify the shell type, as practiced on this linux flavour, to make shure the current setting fits into the given context. This is what i came up with.
#!/bin/bash # reset the prompt for (initial) login-shells only if ( shopt -q login_shell ) ; then BASE_COLOR='[1;33m' # light yellow # have a red prompt for root if [[ `id -u` == 0 ]] ; then BASE_COLOR='[1;31m' # light red fi # have the full hostname with \H for an ip only export PROMPT_DIRTRIM=3 export BASE_COLOR export PS1='\[\e$BASE_COLOR\][\u@\H \l|\A \[\e[0;37m\]\w\[\e$BASE_COLOR\]] \$\[\e[0m\] ' # set the invoked editor export EDITOR=mcedit fi # apply to interactive shells, recall aliases for sub-shells if [[ "$PS1" ]] ; then # alias assignment shall be set for interactive shells alias ll='ls -alh --color=auto' alias lt='ll -t' alias mce='mcedit' fi
Have fun, Peter