
Show HN: A fix for the .bash_profile .bashrc .profile madness - jwecker
https://gist.github.com/3320963
======
drothlis
Ignoring .bash_profile vs .bash_login vs .profile, the bash man page
essentially says:

    
    
      login shell                     .profile
      interactive non-login shell     .bashrc
      run by the remote shell daemon  .bashrc
    

I wasn't sure exactly what counted as a "login" shell so I got each of my
.profile and .bashrc printing into a logfile. These are my observations on OS
X:

    
    
      log into OS X user account      (nothing)
      new terminal                    .profile
      run "bash" from that terminal   .bashrc
      run a shell script              (nothing)
      ssh hostname                    .profile
      ssh hostname somecommand        .bashrc
    

The behaviour of ssh seems (to me, at least) to contradict the bash man page.
Anyway this was all so complicated that I'll never remember it, so what I
ended up doing was:

* .profile does nothing other than source .bashrc

* In .bashrc anything that should be specific to interactive shells goes inside:
    
    
      if [ -n "${-//[^i]}" ]; then
        ...
      fi

~~~
lloeki
'login' is when called with _\--login_ flag (which login(1) does)

'interactive' is when called with _-i_ flag (which sshd does when reached by
ssh with no command passed, i.e if you do 'ssh ls' it should not call bashrc,
but should call bash_profile)

The _only_ thing needed is, in bash_profile:

    
    
        [[ $- == *i* ]] && source ~/.bashrc
    

because bash does not load bashrc when it is both an interactive _and_ a login
shell.

There is no madness, everything is explained in TFMs (bash and zshall, which I
coincidentally went through just yesterday)

FWIW here are my dotfiles, which are designed to relieve me of the true
madness of the startup scripts: the thousand-line hairy mess (bonus included:
mutualization between bash and zsh configs).

[0] <https://github.com/lloeki/dotfiles>

~~~
drothlis

      > 'interactive' is when called with -i flag (which sshd does when reached by
      > ssh with no command passed, i.e if you do 'ssh ls' it should not call
      > bashrc, but should call bash_profile)
    

Do you mean that an interactive shell should load .bash_profile? Because the
bash man page says: "When an interactive shell that is not a login shell is
started, bash reads and executes commands from ~/.bashrc".

Re. ssh, the bash man page says (at least on my system, with bash 4.2.37):
"Bash attempts to determine when it is being run with its standard input
connected to a network connection, as when executed by the remote shell
daemon, usually rshd, or the secure shell daemon sshd. If bash determines it
is being run in this fashion, it reads and executes commands from ~/.bashrc".

That last quote from the bash man page contradict my own experiments. Logging
into my OS X system with ssh loads .profile, not .bashrc. The same is true
logging into my Fedora 17 system with ssh. Perhaps sshd uses --login instead
of -i (or, on my systems, happens to be configured in that way).

Running a command via ssh ("ssh hostname somecommand") loads nothing when the
remote system is OS X, and loads .bashrc when the remote system is Fedora 17.

All in all, I will respectfully disagree that "there is no madness". Normally
I completely relate to the RTFM sentiment, and I have spent plenty of time in
the bash manual myself.

I remain unconvinced that there is a better solution than putting _everything_
into .bashrc (with the $- guard around stuff specific to interactive shells)
and doing nothing in .profile other than unconditionally sourcing .bashrc.

    
    
      >  [[ $- == *i* ]]
    

Thanks for the sane test syntax: It's much clearer than my ugly mess of almost
every symbol character I could find. :-)

~~~
lloeki
> Do you mean that an interactive shell should load .bash_profile?

No, ssh logs you in, so it is a login shell (and thus calls {bash_,z}profile),
but if you call 'ssh ls', it is not an interactive shell since you just ask
ssh to launch the 'ls' command remotely.

> Logging into my OS X system with ssh loads .profile

sshd starts login(1) (which shows the 'Last login:' line), which calls your
shell as login

~~~
drothlis
Ah, I understand now: The sentence I quoted from the bash man page refers to
ssh only when run with a command ("ssh hostname command"). Thanks!

------
delinka
Why did someone even decide that the program (the shell executable) needed to
behave differently depending on "login" vs. "interactive"? If I'm executing a
remote command (e.g. via ssh) I expect it to be working in the same
environment that I'd be working in interactively. Maybe I need some
environment variable to tell my script what "mode" we're in so that _it_ can
make some decision, but the shell not providing the same environment (aside
from interactive vs. non-interactive behavior) bugs the hell out of me.

~~~
gecko
Historically, there were a collection of commands that ran on login (e.g.,
checking for mail, giving a security report, etc.) that you wouldn't want
popping up when you were just firing up additional sessions with su or the
like. I'm not saying it's perfect, but there _is_ a logic to it.

------
gioele
I forked this file into a proper repo [1] and made some adjustments. Now the
script does not spawn external processes any longer, making the shell lighter
and faster to came up.

Enjoy. Patches are welcome.

[1] <https://github.com/gioele/bashrc_dispatch>

------
ColinWright
Some time ago I had to deal with Bash initialisation, so I wrote this:

<http://news.ycombinator.com/item?id=4369934>

------
bajsejohannes
This is great! I made a little tweak where you can change the locations of the
scripts: <https://gist.github.com/3323840>

I set PREFIX to ~/Dropbox/rcfiles/, and the scripts will follow wherever I go
:)

~~~
jwecker
Good call. Merged into the repo
<https://github.com/josephwecker/bashrc_dispatch> . Thanks!

------
pyre
The other thing not being mentioned is that sometimes logging into GNOME will
source .profile or .bash_profile (I forget which). This can be used to make
sure that environment variables are set at the top-level (to be inherited by
all sub-processes of the GNOME environment). Mostly this is useful for things
like $PATH (which programs like gmrun make use of).

~~~
sciurus
Yes, this is standard and useful behaviour. When I started using OS X, it
really confused me that 1) it didn't source .profile on login 2) it sourced
.profile (instead of .bashrc) whenever I opened a new terminal.

~~~
graywh
Yeah, many terminal emulators (like Terminal.app) are configured to run login
shells.

------
james2vegas
bash is the problem, the solution to this madness is to not use bash.

~~~
masklinn
Don't other shells have similar issues? ZSH sources .zshenv, .zprofile, .zshrc
and .zlogin (and corresponding /etc files) depending on the exact situation.

Only difference is that it does not source .profile unless it's in
compatibility mode (invoked as `sh`)

~~~
pyre
The other difference is that zsh has a .zshenv (and /etc/zshenv), while bash
only has $BASH_ENV, which only happen if it's set.

/etc/zshenv is _always_ sourced, and there's no way to stop that. On the other
hand, you can control if /etc/zshrc (etc) are sourced with:

    
    
      unsetopt GLOBAL_RCS
      IGNORE_SYSTEM_ZSH=1
    

But you would have to put it in .zshenv for it to get sourced at the right
time.

~~~
masklinn
Yeah, my point was simply that other advanced shells have as many files they
may source as bash (if not more), with similar naming conventions. So holding
that against bash and bash alone didn't make much sense.

The details and decision tree of zsh's initialization are rather well
documented already (if complex) and beyond the scope of making that point.

------
benatkin
Looks awesome!

Please use a github project (also called a repository) for this sort of thing
rather than a gist. There isn't a minimum size for a github project's codebase
and it's much better for code that is intended to be used by more than a
handful of people.

~~~
jwecker
Indeed. <https://github.com/josephwecker/bashrc_dispatch> \- thanks to gioele
for already doing some cleanups and speedups.

------
smegel
Solution looking for a problem. Not a particularly good one at that.

~~~
ghshephard
If I do a survey of 10 engineers who use Linux, and OS X every day - I'm
willing to wager that 9/10 of them wouldn't be able to explain what should be
in .bash_profile, .profile, and .bashrc. And I can almost guarantee you they
couldn't explain the difference between OS X and Linux. I know I couldn't. I
like the solution, actually:

    
    
      - .bashrc_all - always gets executed 
      - .bashrc_scripts - non-interactive bash execution
      - .bashrc_interactive - duh.
      - .bashrc_login - motd and friends (when you "login")
    

I'd have no problem remembering that. Why don't you think there is a problem,
and, why do you believe this is a poor solution to that (nonexistent) problem?

~~~
nodata
You would _never need to know the difference_ \- because you _can always read
the man page_.

~~~
moe
Which man-page exactly?

The fun part about .bashrc, .bash_profile, .profile, /etc/bash*, /etc/profile
etc. is that their semantics vary wildly between linux-distros. Not even
Ubuntu and Debian agree here.

You never quite know where, when and if .bashrc is sourced by the distro-
templates. A .bash_profile that unconditionally sources .bashrc will work fine
on one distro, on the next you will see it run multiple times, on yet another
you run into an infinite recursion.

His approach is the correct one and you'll find a similar script in just about
any sizable, heterogenous deployment.

~~~
nodata
man bash

The INVOCATION part. You find this part by searching for the word "profile".

