Contents
Interesting bash aliases and functions:
https://github.com/Bash-it/bash-it: Community bash "framework"
Google's shell script style guide. Also good is Bash 3 Boilerplate
Neat features
Edit a command in $EDITOR
Ctrl-x Ctrl-e will edit a command in $EDITOR before execution.
With set -o vi in bash/readline, v in command mode will do the same.
-v for testing set variables, while handling undefined ones
-v: True if the shell variable varname is set (has been assigned a value).
bash >=4.2 supports -v for testing for *set* variables while handling empty ones, also no specials needs when running under 'set -u':
also:
If you use bash, you should really use ]] instead of [ ]. It can do everything [ can (and then some, like ERE and glob matches), but is a shell keyword, which improves ergonomics quite a bit. See [ $x = 0 ] vs. [[ $x = 0 after unset x, for example.
Special Variables
- $0 - The filename of the current script. Do not use!
-
$n - The Nth argument passed to script was invoked or function was called. E.g. $1, etc.
-
$# - The number of argument passed to script or function.
- $@ - All arguments passed to script or function.
- $* - All arguments passed to script or function.
- $? - The exit status of the last command executed.
- $$ - The process ID of the current shell. For shell scripts, this is the process ID under which they are executing.
-
$! - The process number of the last background command.
-
$- - current options set for the shell
- $_ - most recent parameter, or the absolute path of the command used to start the current shell
See Special Parameters from the Bash Reference Manual.
"Strict Mode"
Tools
Use shellcheck with timonwong.shellcheck plugin.
Use bash-language-server.
Gotchas
Take care editing bash scripts: Bash processes scripts as they execute, so saving over a script that is running can have side effects.
Types and variables
Can use declare (conventional r/w variable), local (scoped to function only), and readonly, along with modifiers (e.g. -a), to declare variables.
E.g. readonly -a declares a read-only array.
Arrays
1 # Define
2 arr=("one" "two")
3 declare -a arr=("one" "two")
4
5 # Append
6 arr+=("three")
7
8 # Merge all items, space delimited
9 echo "${arr[@]}"
10 # one two three
11
12 # Join by delimiter
13 echo $(IFS=, ; echo "${arr[*]}")
14 # one,two,three
15
16 # Indexes of the array (i.e. Python's enumerate)
17 echo "${!arr[@]}"
18 # 0 1 2
19
20 # Array length
21 echo "${#arr[@]}"
22 # 3
23
24 # Remove an element
25 unset arr[3]
26
27 # Display item by position
28 echo "${arr[1]}"
29 # two
30
31 # Loop over each individual items
32 # If inside a quoted string
33 # '@' returns each individual item
34 for i in "${arr[@]}"; do
35 echo "$i"
36 done
37 # one
38 # two
39 # three
40
41 # If inside a quoted string
42 # '*' will merge together by IFS
43 for i in "${arr[*]}"; do
44 echo "$i"
45 done
46 # one two three
47
48 # Copy an array
49 declare -a arr2=("${arr[@]}")
50
51 # Combine two arrays
52 declare -a arr3=("${arr1[@]}" "${arr2[@]}")
53
54 # Randomly shuffle an array
55 shuf -e "${arr[@]}"
56 shuffled=(shuf -e "${arr[@]}")
57
58 # Delete an entire array
59 unset arr arr1 arr2 arr3
60
61 # Subset of an array
62 # ${arr[@]:$start} # from start to end of array
63 # ${arr[@]::$count} # first count-many elements from beginning
64 # ${arr[@]:$start,$count} # count-many elements from start
65 echo "${arr[@]:1:1}"
66
67 # Check if array is empty
68 if [[ ${#arr[@]} -eq 0 ]]; then ... fi
69
70 # Check if array is not empty
71 if [[ ${#arr[@]} -gt 0 ]]; then ... fi
72
73 # Check item "one" is in array
74 [[ ${arr[*]} =~ ^|[[:space:]])"one"($|[[:space:]]) ]] && echo "exists" || echo "doesn't exist"
75 # exists
76
77 # Check if partial match is in array
78 [[ ${arr[*]} =~ 'on' ]] && echo "exists" || echo "doesn't exist"
79 # exists
80
81 # Read into an array from a file, safely. Preserves spaces, special characters, etc.
82 # bash 4.0
83 mapfile -t inputarr < file.txt
84
85 # Read into an array from a command, safely. Preserve spaces, special characters, etc.
86 # -t removes trailing spaces
87 readarray -t inputarr < <(seq 5)
88 declare -p inputarr
89 # declare -a inputarr=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5")
90
Associative arrays/hash tables
declare -A ht
ht["one"]=1
ht["two"]=2
# Print values, all one line
echo "${ht[@]}"
# 2 1
# Print keys, all one line
echo "${!ht[@]}"
# two one
# Iterate over keys and values
for k in "${!ht[@]}"; do
echo "$k=${ht[$k]}"
done
# two=2
# one=1
# Hash length
echo "${#ht[@]}"
# Remove a key
unset ht["one"]
echo "${ht[@]}"
# 2
# Check if key is in hash
[[ -z "${ht["one"]}" ]] && echo "exists" || echo "doesn't exist"
Escaping characters
tl;dr: bash (TODO: check which version) lets you just use the character via copy-and-paste. Make sure to be using a Unicode encoding, e.g. LANG="en_US.UTF-8".
Use special `$"..." quoting to allow bash to read ANSI C and Unicode escapes. E.g.
# ANSI escape
VAR=$'\xe2\x98\xa2'
# Unicode escape
VAR=$'\u2622'
See also: ANSI-C quoting from the Bash Reference Manual.
Never Remember
Process Substition is the name for <(command) syntax.
Redirect stderr to stdout with 2>&1, e.g.:
Some-Command 2>&1 > log.txt