DEV Community

Kristof Bruyninckx
Kristof Bruyninckx

Posted on • Edited on

Back to bash: Customization

This post explores a number of different ways in which your bash experience can be customized and, even more so, the underlying mechanism. It's good to be aware of this, as tooling will often either modify your bash startup scripts, or ask you to make the changes.

Customize your bash experience with... bash!

Generally, you use bash itself to customize your own bash experience, since every shell you start will begin by running bash code from a couple of startup scripts.

This means that you can customize things like:

  • define aliases and functions
  • Modify shell options through set and shopt.
  • Alter environment variables
  • ...

Aliases

When invoked these are replaced by the underlying command. For example, i quite often do ls -lrtah to list all (a) file details (l) sorted by reversed modification time (rt) in a human readable format (h). I could define an alias as such:

alias lsd="ls -lrtah" 
Enter fullscreen mode Exit fullscreen mode

Now i just need to remember lsd. If i would have a more complex case that required several commands, i could go for a shell function instead.

Shell options

As an example, i can set shopt -s globstar to make ls **/*.txt list files within sub directories.

Alter environment

A very common operation is to modify PATH so that your executables are searched in new locations.

export PATH="/opt/some_dir/:$PATH"
Enter fullscreen mode Exit fullscreen mode

Using export ensures sub processes have access to the variable as well, for example spawning a shell from within a shell will still 'see' the PATH variable.

The loading process

Depending on what kind of shell you start, bash attempts to run the following files 1.

  • Interactive login shell: ~/.bash_profile, ~/.bash_login or ~/profile. These are tried in order as specified, the first one that is found is loaded.
  • interactive non-login shell: ~/.bashrc

Since i refer to these files a lot, i'll simply call them the profile and bashrc files from now on.

you will probably only directly use login shells when using ssh to access a remote machine. On a GUI based OS, the login shell is typically hidden away, but all shells you launch will be subprocesses of the login shell, taking over its execution environment. Those new shells will only load your bashrc file. Herein lies an important factor in choosing which file to modify. Profile scripts are only executed once upon logging in, while bashrc is ran for every interactive shell you spawn. If you add something to your profile script, you'll need to re-login for this to take effect, as opposed to just spawning a new shell when modifying bashrc. There are alternatives to both undesirable options though, as discussed in the next section.

It's worth noting that on my ubuntu 24.04, the profile script will also run bashrc (you can verify this on your own setup) as such:

if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
        . "$HOME/.bashrc"
    fi  
fi
Enter fullscreen mode Exit fullscreen mode

This is not guaranteed though.

Tricks to prevent having to start a new shell

As an example, suppose we have just installed pyenv. The installation process contains instructions to update both your profile and bashrc files. Suppose you've updated your bashrc file, the changes don't take effect for the current shell you were using for the installation. Having to start a new shell can be a real pain, which can be circumvented in a couple of ways.

Spawning a new shell (worse)

You can use bash inside your current shell to spawn a new shell, but the old shell remains open as shown by printing out the process tree. The new shell gets the old shell as its parent.

$ pstree -s $$
systemd───systemd───gnome-terminal-───bash───bash───pstree
Enter fullscreen mode Exit fullscreen mode

A very annoying problem with this is that you loose your recent shell history because that only gets written once the old shell is closed 2. I've found this to be quite disruptive.

Sourcing startup scripts

Manually telling bash to re-execute the startup script (source ~/.bashrc or source ~/.profile) does the job. All that's important here is sourcing the script.

Be aware that newly spawned shells will still not see the changes made to profile unless you manually source the profile script each time. This is because they are started from the hidden login shell provided by your OS, which is now out-of-date with your profile script.

An important caveat related to that out-of-date login shell here is that if you remove something from your profile script, such as an export, it will still be there even if you source your profile script, since the export is still taken over from the existing out-of-date login shell.

This is a great option, but it's still useful to know about the others and their downsides.

Replacing your shell

You can use exec $BASH, $BASH being a variable that is automatically set and pointing to your bash executable, usually /bin/bash. This essentially replaces the current bash process with a newly spawned bash (loading bashrc as usual). You might also loose some things, such as function definitions and variables that were not exported, this is shown in the example below.

$ TEST="test"
$ echo $TEST
test
$ exec $BASH
$ echo $TEST

$ export TEST="test"
$ echo $TEST
test
$ exec $BASH
$ echo $TEST
test
Enter fullscreen mode Exit fullscreen mode

It depends on what you were doing, but for me this typically isn't a big deal. This behavior makes sense as we are spawning a new shell so only what was explicitly exported is taken over.

Note that if you want to reload the profile script, you can run exec bash -l, giving you a login shell, however any other shell you spawn will still be out-of-date, for the same reason as mentioned in the section above. Logins shells will also run ~/.bash_logout upon exiting.


  1. It should be noted that there are also system-wide variants that apply first, for every user (/etc/profile and /etc/bash.bashrc). 

  2. Under the assumption that you did not modify bash to write history immediately, which is possible using PROMPT_COMMAND 

Top comments (0)