Bash scripting Guide
Example
1#!/usr/bin/env bash
2
3NAME="John"
4echo "Hello $NAME!"
5
Variables
1NAME="John"
2echo $NAME
3echo "$NAME"
4echo "${NAME}!"
5
String quotes
1NAME="John"
2echo "Hi $NAME" #=> Hi John
3echo 'Hi $NAME' #=> Hi $NAME
4
Shell execution
1echo "I'm in $(pwd)"
2echo "I'm in `pwd`"
3# Same
4
See Command substitution
Conditional execution
1git commit && git push
2git commit || echo "Commit failed"
3
Functions
1get_name() {
2 echo "John"
3}
4
5echo "You are $(get_name)"
6
See: Functions
Conditionals
1if [[ -z "$string" ]]; then
2 echo "String is empty"
3elif [[ -n "$string" ]]; then
4 echo "String is not empty"
5fi
6
Strict mode
1set -euo pipefail
2IFS=$'\n\t'
3
Brace expansion
1echo {A,B}.js
2
| Same as |
| Same as |
| Same as |
#Parameter expansions
Basics
1name="John"
2echo ${name}
3echo ${name/J/j} #=> "john" (substitution)
4echo ${name:0:2} #=> "Jo" (slicing)
5echo ${name::2} #=> "Jo" (slicing)
6echo ${name::-1} #=> "Joh" (slicing)
7echo ${name:(-1)} #=> "n" (slicing from right)
8echo ${name:(-2):1} #=> "h" (slicing from right)
9echo ${food:-Cake} #=> $food or "Cake"
10
11length=2
12echo ${name:0:length} #=> "Jo"
13
1STR="/path/to/foo.cpp"
2echo ${STR%.cpp} # /path/to/foo
3echo ${STR%.cpp}.o # /path/to/foo.o
4
5echo ${STR##*.} # cpp (extension)
6echo ${STR##*/} # foo.cpp (basepath)
7
8echo ${STR#*/} # path/to/foo.cpp
9echo ${STR##*/} # foo.cpp
10
11echo ${STR/foo/bar} # /path/to/bar.cpp
12
13STR="Hello world"
14echo ${STR:6:5} # "world"
15echo ${STR:-5:5} # "world"
16
17SRC="/path/to/foo.cpp"
18BASE=${SRC##*/} #=> "foo.cpp" (basepath)
19DIR=${SRC%$BASE} #=> "/path/to/" (dirpath)
20
Substitution
| Remove suffix |
| Remove prefix |
| Remove long suffix |
| Remove long prefix |
| Replace first match |
| Replace all |
| Replace suffix |
| Replace prefix |
Comments
1# Single line comment
2
3: '
4This is a
5multi line
6comment
7'
8
Substrings
| Substring (position, length) |
| Substring from the right |
Length
| Length of |
Manipulation
1STR="HELLO WORLD!"
2echo ${STR,} #=> "hELLO WORLD!" (lowercase 1st letter)
3echo ${STR,,} #=> "hello world!" (all lowercase)
4
5STR="hello world!"
6echo ${STR^} #=> "Hello world!" (uppercase 1st letter)
7echo ${STR^^} #=> "HELLO WORLD!" (all uppercase)
8
Default values
|
|
| Set |
|
|
| Show error message and exit if |
The :
is optional (eg, ${FOO=word}
works)
#Loops
Basic for loop
1for i in /etc/rc.*; do
2 echo $i
3done
4
C-like for loop
1for ((i = 0 ; i < 100 ; i++)); do
2 echo $i
3done
4
Ranges
1for i in {1..5}; do
2 echo "Welcome $i"
3done
4
With step size
1for i in {5..50..5}; do
2 echo "Welcome $i"
3done
4
Reading lines
1cat file.txt | while read line; do
2 echo $line
3done
4
Forever
1while true; do
2 ···
3done
4
#Functions
Defining functions
1myfunc() {
2 echo "hello $1"
3}
4
5# Same as above (alternate syntax)
6function myfunc() {
7 echo "hello $1"
8}
9
10myfunc "John"
11
Returning values
1myfunc() {
2 local myresult='some value'
3 echo $myresult
4}
5
6result="$(myfunc)"
7
Raising errors
1myfunc() {
2 return 1
3}
4
5if myfunc; then
6 echo "success"
7else
8 echo "failure"
9fi
10
Arguments
| Number of arguments |
| All arguments |
| All arguments, starting from first |
| First argument |
| Last argument of the previous command |
#Conditionals
Conditions
Note that [[
is actually a command/program that returns either 0
(true) or 1
(false). Any program that obeys the same logic (like all base utils, such as grep(1)
or ping(1)
) can be used as condition, see examples.
| Empty string |
| Not empty string |
| Equal |
| Not Equal |
| Equal |
| Not equal |
| Less than |
| Less than or equal |
| Greater than |
| Greater than or equal |
| Regexp |
| Numeric conditions |
| If OPTIONNAME is enabled |
| Not |
| And |
| Or |
File conditions
| Exists |
| Readable |
| Symlink |
| Directory |
| Writable |
| Size is > 0 bytes |
| File |
| Executable |
| 1 is more recent than 2 |
| 2 is more recent than 1 |
| Same files |
Example
1# String
2if [[ -z "$string" ]]; then
3 echo "String is empty"
4elif [[ -n "$string" ]]; then
5 echo "String is not empty"
6fi
7
8# Combinations
9if [[ X ]] && [[ Y ]]; then
10 ...
11fi
12
13# Equal
14if [[ "$A" == "$B" ]]
15
16# Regex
17if [[ "A" =~ . ]]
18
19if (( $a < $b )); then
20 echo "$a is smaller than $b"
21fi
22
23if [[ -e "file.txt" ]]; then
24 echo "file exists"
25fi
26
#Arrays
Defining arrays
1Fruits=('Apple' 'Banana' 'Orange')
2
3Fruits[0]="Apple"
4Fruits[1]="Banana"
5Fruits[2]="Orange"
6
Working with arrays
1echo ${Fruits[0]} # Element #0
2echo ${Fruits[@]} # All elements, space-separated
3echo ${#Fruits[@]} # Number of elements
4echo ${#Fruits} # String length of the 1st element
5echo ${#Fruits[3]} # String length of the Nth element
6echo ${Fruits[@]:3:2} # Range (from position 3, length 2)
7
Operations
1Fruits=("${Fruits[@]}" "Watermelon") # Push
2Fruits+=('Watermelon') # Also Push
3Fruits=( ${Fruits[@]/Ap*/} ) # Remove by regex match
4unset Fruits[2] # Remove one item
5Fruits=("${Fruits[@]}") # Duplicate
6Fruits=("${Fruits[@]}" "${Veggies[@]}") # Concatenate
7lines=(`cat "logfile"`) # Read from file
8
Iteration
1for i in "${arrayName[@]}"; do
2 echo $i
3done
4
#Dictionaries
Defining
1declare -A sounds
2
3sounds[dog]="bark"
4sounds[cow]="moo"
5sounds[bird]="tweet"
6sounds[wolf]="howl"
7
Declares sound
as a Dictionary object (aka associative array).
Working with dictionaries
1echo ${sounds[dog]} # Dog's sound
2echo ${sounds[@]} # All values
3echo ${!sounds[@]} # All keys
4echo ${#sounds[@]} # Number of elements
5unset sounds[dog] # Delete dog
6
Iteration
Iterate over values
1for val in "${sounds[@]}"; do
2 echo $val
3done
4
Iterate over keys
1for key in "${!sounds[@]}"; do
2 echo $key
3done
4
#Options
Options
1set -o noclobber # Avoid overlay files (echo "hi" > foo)
2set -o errexit # Used to exit upon error, avoiding cascading errors
3set -o pipefail # Unveils hidden failures
4set -o nounset # Exposes unset variables
5
Glob options
1shopt -s nullglob # Non-matching globs are removed ('*.foo' => '')
2shopt -s failglob # Non-matching globs throw errors
3shopt -s nocaseglob # Case insensitive globs
4shopt -s dotglob # Wildcards match dotfiles ("*.sh" => ".foo.sh")
5shopt -s globstar # Allow ** for recursive matches ('lib/**/*.rb' => 'lib/a/b/c.rb')
6
Set GLOBIGNORE
as a colon-separated list of patterns to be removed from glob matches.
#History
Commands
| Show history |
| Don’t execute expanded result immediately |
Expansions
| Expand last parameter of most recent command |
| Expand all parameters of most recent command |
| Expand |
| Expand |
| Expand most recent invocation of command |
Operations
| Execute last command again |
| Replace first occurrence of |
| Replace all occurrences of |
| Expand only basename from last parameter of most recent command |
| Expand only directory from last parameter of most recent command |
!!
and !$
can be replaced with any valid expansion.
Slices
| Expand only |
| Expand first argument from most recent command |
| Expand last token from most recent command |
| Expand range of tokens from most recent command |
| Expand |
!!
can be replaced with any valid expansion i.e. !cat
, !-2
, !42
, etc.
#Miscellaneous
Numeric calculations
1$((a + 200)) # Add 200 to $a
2
3$((RANDOM%=200)) # Random number 0..200
4
Subshells
1(cd somedir; echo "I'm now in $PWD")
2pwd # still in first directory
3
Redirection
1python hello.py > output.txt # stdout to (file)
2python hello.py >> output.txt # stdout to (file), append
3python hello.py 2> error.log # stderr to (file)
4python hello.py 2>&1 # stderr to stdout
5python hello.py 2>/dev/null # stderr to (null)
6python hello.py &>/dev/null # stdout and stderr to (null)
7
8python hello.py < foo.txt # feed foo.txt to stdin for python
9
Inspecting commands
1command -V cd
2#=> "cd is a function/alias/whatever"
3
Trap errors
1trap 'echo Error at about $LINENO' ERR
2
or
1traperr() {
2 echo "ERROR: ${BASH_SOURCE[1]} at about ${BASH_LINENO[0]}"
3}
4
5set -o errtrace
6trap traperr ERR
7
Case/switch
1case "$1" in
2 start | up)
3 vagrant up
4 ;;
5
6 *)
7 echo "Usage: $0 {start|stop|ssh}"
8 ;;
9esac
10
Source relative
1source "${0%/*}/../share/foo.sh"
2
printf
1printf "Hello %s, I'm %s" Sven Olga
2#=> "Hello Sven, I'm Olga
3
4printf "1 + 1 = %d" 2
5#=> "1 + 1 = 2"
6
7printf "This is how you print a float: %f" 2
8#=> "This is how you print a float: 2.000000"
9
Directory of script
1DIR="${0%/*}"
2
Getting options
1while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do case $1 in
2 -V | --version )
3 echo $version
4 exit
5 ;;
6 -s | --string )
7 shift; string=$1
8 ;;
9 -f | --flag )
10 flag=1
11 ;;
12esac; shift; done
13if [[ "$1" == '--' ]]; then shift; fi
14
Heredoc
1cat <<END
2hello world
3END
4
Reading input
1echo -n "Proceed? [y/n]: "
2read ans
3echo $ans
4
5read -n 1 ans # Just one character
6
Special variables
| Exit status of last task |
| PID of last background task |
| PID of shell |
| Filename of the shell script |
Go to previous directory
1pwd # /home/user/foo
2cd bar/
3pwd # /home/user/foo/bar
4cd -
5pwd # /home/user/foo
6
Check for command’s result
1if ping -c 1 google.com; then
2 echo "It appears you have a working internet connection"
3fi
4
Grep check
1if grep -q 'foo' ~/.bash_history; then
2 echo "You appear to have typed 'foo' in the past"
3fi
Nhận xét
Đăng nhận xét