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