BASH tips and snippets

This page documents bash tips and snippets that I typically use in my shell scripts. I keep them together to be able to quickly check them if required.


Colours in prompt

In your .bashrc file:

export BLACK="\[\e[00;30m\]"
export BLUE="\[\e[00;34m\]"
export GREEN="\[\e[00;32m\]"
export CYAN="\[\e[00;36m\]"
export RED="\[\e[00;31m\]"
export PURPLE="\[\e[00;35m\]"
export BROWN="\[\e[00;33m\]"
export GRAY="\[\e[01;30m\]"
export L_GRAY="\[\e[00;37m\]"
export L_BLUE="\[\e[01;34m\]"
export L_GREEN="\[\e[01;32m\]"
export L_CYAN="\[\e[01;36m\]"
export L_RED="\[\e[01;31m\]"
export L_PURPLE="\[\e[01;35m\]"
export L_YELLOW="\[\e[01;33m\]"
export L_WHITE="\[\e[01;37m\]"
export NORMAL="\[\e[00m\]"
 
# Red prompt for root
export PS1="${L_RED}\u@\H:${BLUE} \w #${NORMAL} "
 
# For a normal user, use this instead:
#export PS1="${L_YELLOW}\u@\H:${BLUE} \w \$${NORMAL} "


Check if variable starts or ends with substring

# SH
if [ "${HOST:0:4}" = "user" ]; then ...; fi
 
# BASH
if [[ "$HOST" =~ ^user.* ]]; then ... ; fi
 
# BASH (without quotes, for * to expand):
if [[ $HOST == user* ]]; then ... ; fi 


Get the last argument given to the script

LASTARG="${@: -1}"


Remove the last argument given to the script

set -- "${@:1:$(($#-1))}"


Use colours in bash scripts, with --nocolor option

This code allows to use colours in your scripts, and disable them giving –nocolor

# Terminal colours
RED="\e[31m"
YELLOW="\e[33m"
NORMAL="\e[0m"
GREEN="\e[92m"
 
# Disable terminal colours if "--nocolor" flag is passed as last argument:
LASTARG="${@: -1}"
if [ "${LASTARG}" == "--nocolor" ];
then
    RED=""
    YELLOW=""
    NORMAL=""
    GREEN=""
fi
 
# Now you can safely write colors with:
echo -e "* File $FILE ${YELLOW}already patched${NORMAL} ${GREEN}[success]${NORMAL}\n"


Remove trailing bar in argument if exists

Sometimes our scripts accept paths as command line arguments. As we don't know if the user will add a trailing bar to the path (/tmp or /tmp/), we can use bash's % operator to remove it:

# Remove trailing / from $1 (if it ends with a /).
DESTDIR="${1%/}"
 
# Another example:
BACKUPDIR="${BACKUPDIR%/}"


Request a password with visual feedbackup

unset MYPWD
prompt="Please insert the password: "
while IFS= read -p "$prompt" -r -s -n 1 char
do
    if [[ $char == $'\0' ]]
    then
        break
    fi
    prompt='*'
    MYPWD+="$char"
done
echo


Trap CTRL+C and KILL to remove tempfiles

We should always remove temporary files created with mktemp. But if our script it's killed with CTRL+C or with kill, the temporary file won't be deleted. Bash provides a signal trapping mechanism to allow us to minimize this effect:

TEMPFILE=$(mktemp)
trap "rm -f ${TEMPFILE}; exit 1" INT TERM
 
   (your code...)
 
rm -f $TEMPFILE
trap - INT TERM

Note that there is a small race condition between the tempfile creation and the trap command (the file is created and the trap is not yet set).


Vi mode for bash (readline)

By setting "set -o vi" in our .bashrc file (or typing it directly in the terminal), we change from "emacs-mode" to "vi-mode" (check set -o). While in Vi mode, we can use the Vi keyboard commands to work in the terminal. Each command starts in insert mode but we can go to command mode by pressing ESC.

In your .bashrc:

export VISUAL=/usr/bin/vim
export EDITOR=/usr/bin/vim
set -o vi

Now, in command mode, the following Vi keys/commands will act as expected:

  • 0 and ^ ⇒ go to beginning of line.
  • $ ⇒ go to end of line.
  • j and k ⇒ up and down in the command history.
  • h and l ⇒ move cursor left or right.
  • yy ⇒ copy current line into the yank buffer.
  • p and P ⇒ paste the yank buffer after or before the cursor.
  • dw and cw ⇒ delete or change the current word (supports modifiers, like 2cw).
  • w and W ⇒ move to beginning of next word (uppercase ignores . and -).
  • b and B ⇒ move to beginning of previous word (uppercase ignores . and -).
  • e and E ⇒ move to end of next word (uppercase ignores . and -).
  • % ⇒ move between start / end of (), {} or []
  • i and a ⇒ insert or append text (current vs next cursor position).
  • I and A ⇒ insert at the beginning or end of the line (no need to move to it manually!)
  • x and X ⇒ delete current or previous character (DEL vs BACKSPACE).
  • D ⇒ delete from the current cursor position to End of Line.
  • C and C$ and S ⇒ change the text from current position to End of Line (like D + i)
  • . ⇒ repeat the last change.
  • u and U ⇒ undo last or ALL changes to the line.
  • s ⇒ delete char under cursor and enter insert mode.
  • r ⇒ replace char under cursor and keep command mode.
  • fx and Fx / tx and Tx ⇒ move to next/previous ocurrence of 'x' (included vs not included x)
  • NUMBER| ⇒ Move to char number NUMBER of the line.
  • CTRL+V + char ⇒ Insert char (ex: tab).
  • ma and `a ⇒ place mark and move to mark

Specially useful commands are included:

  • # ⇒ send current line to the history, commented
  • v ⇒ edit current line in $EDITOR (very useful for long commands!). Exit the editor by saving (:wq or :x) and the content of the edited file will be executed. To avoid executing anything, delete the command (dd) or comment it with #.

Searches also work like in vi:

  • /string and ?string ⇒ search history backwards/forward for the string (press ESC first, and note that the /string will appear on the script while typing!).
  • n and N ⇒ next or previous search result during a search


Recall last argument from the last command

Unfortunately, with the vi mode I lost the only "emacs" command that I typically used: ESC + . (to recover the last argument from the previous command). To solve this, we have a couple of alternatives:

First: you can use bash's !$ or $_ but it is not expanded until you execute the command and it's quite dangerous used that way.

Second: you can use Alt+. instead (which also works in emacs mode) by adding the following to your ~/.inputrc file:

set editing-mode vi
set keymap vi-insert

$if mode=vi
   "\e.":yank-last-arg
$endif

Then, edit your ~/.bashrc and after the set -o vi add:

if [[ $- =~ i ]] && [[ -f ~/.inputrc ]]; then
    bind -f ~/.inputrc
fi

If you don't mind losing Vi's dot (.) functionality, you can also add the following to your .inputrc file:

set keymap vi-command
$if mode=vi
    ".":insert-last-argument
$endif

Finally, another option would be to switch to command mode and press _ (underscore): ESC + _.


Block arrow keys in command mode

If you also want to "block" the arrow keys in command mode, add the following in your .inputrc file:

set keymap vi-command

$if mode=vi
   "\eOD":""
   "\e[D":""
   "\eOC":""
   "\e[C":""
   "\eOA":""
   "\e[A":""
   "\eOB":""
   "\e[B":""
$endif


Other emacs-mode typical shortcuts in vi mode

If we are used also to other emacs shortcuts like "Clear the Screen" (Ctrl+L) or "Home" / "End" (Ctrl+A and Ctrl+E) we can add them to our .inputrc file:

set editing-mode vi
set keymap vi-insert

$if mode=vi
(...)
   "\C-l":clear-screen
   "\C-a":beginning-of-line
   "\C-e":end-of-line
   "\C-w":backward-kill-word
$endif

set keymap vi-command
$if mode=vi
(...)
   "\C-l":clear-screen
   "\C-a":beginning-of-line
   "\C-e":end-of-line
   "\C-w":backward-kill-word
$endif


<Volver a la sección de GNU/Linux>