Bash
Hello Summary
# variable default value if unset
v=${1:-world}
echo "hello ${v}" # hello world (if no args)
# arrays
l=("hello")
l+=("world")
echo "${#l[@]}" # 2
echo "${l[*]}" # hello world
# conditions
if [ 5 -ge 0 ] && [ 10 -ge 0 ]; then # can also be written [[ ... && ...]]
echo "both positives" # both positive
fi
# 'function' and 'tricky::' are optional
function tricky::variable() {
a=5
local b=10
}
echo "a=${a}, b=${b}" # a=,b=
tricky::variable # call function
echo "a=${a}, b=${b}" # a=5,b=
# function (can be used as a command)
run_ls() {
local directory="$1"
ls ${directory}
}
# redirect the standard and error output
run_ls "/tmp" >/dev/null 2>&1 && echo "success" || echo "failed" # success
run_ls "/nonexistingdir/lala" >/dev/null 2>&1 \
&& echo "success" \
|| echo "failed" # failed
# in case you want to stop the script, you can add (set -e) and do something like
# ... || (echo "failed" && exit 1)
# loops
for i in $(seq 1 5); do echo -n "${i}, "; done # 1, 2, 3, 4, 5,
while true; do echo "test"; sleep 1s; break; done # test
# case
case ${option} in
-l|--list)
ls
;;
*)
echo "default" # default
;;
esac
Completion
Here's an intersting example with the command ''pass''
# completion file for bash
# Copyright (C) 2012 Jason A. Donenfeld `<Jason@zx2c4.com>` and
# Brian Mattern `<rephorm@rephorm.com>`. All Rights Reserved.
# This file is licensed under the GPLv2+. Please see COPYING for more information.
_pass_complete_entries () {
prefix="${PASSWORD_STORE_DIR:-$HOME/.password-store/}"
suffix=".gpg"
autoexpand=${1:-0}
local IFS=$'\n'
local items=($(compgen -f $prefix$cur))
for item in ${items[@]}; do
if [[ $item == $prefix.* ]]; then
continue
fi
# if there is a unique match, and it is a directory with one entry
# autocomplete the subentry as well (recursively)
if [[ ${#items[@]} -eq 1 && $autoexpand -eq 1 ]]; then
while [[ -d $item ]]; do
local subitems=($(compgen -f "$item/"))
if [[ ${#subitems[@]} -eq 1 ]]; then
item="${subitems[0]}"
else
break
fi
done
fi
# append / to directories
[[ -d $item ]] && item="$item/"
item="${item%$suffix}"
COMPREPLY+=("${item#$prefix}")
done
}
_pass_complete_keys () {
local IFS=$'\n'
local GPG=gpg2
which $GPG >/dev/null || GPG=gpg
# Extract names and email addresses from gpg --list-keys
local keys="$($GPG --list-secret-keys --with-colons | cut -d : -f 10 | sort -u | sed '/^$/d')"
COMPREPLY+=($(compgen -W "${keys}" -- ${cur}))
}
_pass()
{
COMPREPLY=()
local cur="${COMP_WORDS[COMP_CWORD]}"
local commands="init ls show insert generate edit rm git help version"
if [[ $COMP_CWORD -gt 1 ]]; then
case "${COMP_WORDS[1]}" in
init)
COMPREPLY+=($(compgen -W "-e --reencrypt" -- ${cur}))
_pass_complete_keys
;;
ls|list|edit)
_pass_complete_entries
;;
show|-*)
COMPREPLY+=($(compgen -W "-c --clip" -- ${cur}))
_pass_complete_entries 1
;;
insert)
COMPREPLY+=($(compgen -W "-e --echo -m --multiline -f --force" -- ${cur}))
_pass_complete_entries
;;
generate)
COMPREPLY+=($(compgen -W "-n --no-symbols -c --clip -f --force" -- ${cur}))
_pass_complete_entries
;;
rm|remove|delete)
COMPREPLY+=($(compgen -W "-r --recursive -f --force" -- ${cur}))
_pass_complete_entries
;;
git)
COMPREPLY+=($(compgen -W "init push pull config log reflog" -- ${cur}))
;;
esac
else
COMPREPLY+=($(compgen -W "${commands}" -- ${cur}))
_pass_complete_entries 1
fi
}
complete -o filenames -o nospace -F _pass pass
Environment
To retrieve environment variable:
env
{bash}
Note: Could also use set
but it return the register functions too.
Constant
readonly var=value
Arithmetic
n1=1
n2=1
# $(( expression ))
a=$(( n1+n2 )) # 2
b=$(( n1/n2 )) # 1
c=$(( n1-n2 )) # 0
See more on official doc
Shell Parameter Expansion
Example :
bash --version # GNU bash, version 4.2.25(1)-release (x86_64-pc-linux-gnu)
test="n=5=6=7"
echo ${test} # n=5=6=7
echo ${test#*=} # 5=6=7
echo ${test#*=*=} # 6=7
echo ${test##*=} # 7
echo ${test%=*} # n=5=6
echo ${test%=*=*} # n=5
echo ${test%%=*} # n
Bash Regex
Example with host variable which start by "user." and continue by whatever or start by host1.
if [[ "$HOST" =~ ^user.*|^host1 ]]; then
echo "yes"
fi
FAQ / Tips
Debug bash script
bash -x script
Note: You could also add ''-v'' option
Or directly in script :
#!/bin/bash
### Turn on debug mode ###
set -x
# Run shell commands
echo "Hello $(LOGNAME)"
echo "Today is $(date)"
echo "Users currently on the machine, and their processes:"
w
### Turn OFF debug mode ###
set +x
Note: set
permit also to change option of shell (see more).
Breakpoint
bash -c "$SHELL"
Slugify
echo $str | iconv -t ascii//TRANSLIT | sed -r s/[~\^]+//g | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | tr A-Z a-z
Use bash completion from another command
function _mycmd(){
local current choice
current=${COMP_WORDS[COMP_CWORD]}
case "$COMP_CWORD" in
1)
choice="opt1 opt2"
COMPREPLY=( $( compgen -W '$choice' -- $current ) )
;;
2)
COMPREPLY=( $(compgen -W 'the_other_command' -- $current) )
;;
*)
# idea based on sudo bash completion
local PATH=$PATH:/sbin:/usr/sbin:/usr/local/sbin
_command_offset 2
return
;;
esac
}
complete -F _mycmd mycmd
Note/Todo: the command will look like mycmd opt1 the_other_command ...
. I did not find a way to get rid of the_other_command
to make it works
Inspired from:
lock a file (multiple access)
# lock
exec 3>filename # open a file handle; this part will always succeed
flock -x 3 # lock the file handle; this part will block
# release
exec 3>&- # close the file handle
equivalent code in a block
{
flock -x 3
...other stuff here...
} 3>filename
with new bash version, no need to fd number:
# this requires a very new bash -- 4.2 or so.
exec {lock_fd}>filename # open filename, store FD number in lock_fd
flock -x "$lock_fd" # pass that FD number to flock
exec {lock_fd}>&- # later: release the lock