- pipes send the result of one process to another ( send the output of one commend to the next command instead of displaying on the screen)
|
: pipe> >> < <<
: redirection
# wc: word count
ls | wc -l # ls the number of the folder or file in the current directory
# redirection 將結果輸出到list.txt
ls > list.txt
# add the information onto the end of the existing file
ls >> list.txt
# redirect the standard output with one & standard error with 2 輸出
# std output: 1, std err: 2
ls /notreal 1>output.txt 2>error.txt
# take information from a file
cat < list.txt
# use the command version of ```echo``` instead of the builtin version, we would write
command echo
![](https://i.imgur.com/CiK3QG6.png =300x)
echo
: print text to the standard output
~
: represents the user's $HOME environment variable
{}
: create sets or ranges
touch <new file>
: create a new file
# 不能有空格
echo /tmp/{1,2,4}/file.txt
# Output: /tmp/1/file.txt /tmp/2/file.txt /tmp/4/file.txt
echo /tmp/{1..4}/file.txt
# Output: /tmp/1/file.txt /tmp/2/file.txt /tmp/3/file.txt /tmp/4/file.txt
echo {00..100}
echo {00..30..3}
# Output: 00 03 06 09 12 15 18 21 24 27 30
echo {a..z..2}
# Output: a c e g i k m o q s u w y
echo {cat,dog}_{01..05}
# Output: cat_01 cat_02 cat_03 cat_04 cat_05 dog_01 dog_02 dog_03 dog_04 dog_05
${...}
: put the output of one command inside another (command substitution)
# retrieve and transform stored values
a="HelloWorld"
echo $a
# Output: HelloWorld
b=BashCommand
echo ${b}
# Output: BashCommand
char_list="abcdefghijk"
echo ${greeting:6:3} # start print at the 6th character and 3 character
man bash # check the paramter using "/" and input the keyword you want to search
greeting="hello there"
echo ${greeting/there/everybody} # replace there with everybody
# Output: hello everybody
echo ${greeting//e/_} # replace all using "//"
# Output: h_llo th_r_
echo ${greeting/e/_} # replace all using "//"
# Output: h_llo there
$(...)
: puts the output of one command inside another. Often used together with string manipulation tools. e.g. path, file size, IP address
uname -r # get the release version of the kernel
echo "The kernel is $(uname -r)."
# Output: The kernel is 3.0.4(0.338/5/3).
echo "Result: $(python3 -c 'print("Hello from Python!")' | tr [a-z] # transform
$((..))
: arithmetric expansion does math
echo $((2 + 2))
# Output: 4
Bash script run inside of a noninteractive shell
- Bash script
- text file that contains a series of commands
bash script.sh
- Executable bash script (general way)
- includes a sheband as the first line
#!/usr/bin/env bash
(tells the shell when the script is run & the path to whatever program should run the script) - make executable with
chmod +x myscript
- run locally with ./myscript or myscript if in the $PATH
- includes a sheband as the first line
- keep in mind of
declare
for remind you some situation e.g. read only./script.sh
: run the script on the right directorydeclare -p
: show all the variables that have been set in the current session
# script.sh
#!/usr/bin/env bash
myvar="Hello"
echo "The value of the myvar variable is: $myvar"
myvar="World"
echo "The value of the myvar variable is: $myvar"
declare -r myname="Eric" # read only (cannot change the variable)
echo "The value of the myname variable is: $myname"
myname="ASD"
echo "The value of the myname variable is: $myname"
declare -l lowerstring="This is some TEXT" # transform into lower case
echo "Lower string is: $lowerstring"
lowerstring="Let's have the LOWER CASE"
echo "Lower string is: $lowerstring"
declare -u upperstring="This is some TEXT" # transform into upper case
echo "Upper string is: $upperstring"
upperstring="Let's have the UPPER CASE"
echo "Upper string is: $upperstring"
# Output
The value of the myvar variable is: Hello
The value of the myvar variable is: World
The value of the myname variable is: Eric
./myscript.sh: line 14: myname: readonly variable
The value of the myname variable is: Eric
Lower string is: this is some text
Lower string is: let's have the lower case
Upper string is: THIS IS SOME TEXT
Upper string is: LET'S HAVE THE UPPER CASE
Supported Arithmetric Operations +, -, *, /, %, **
$((...))
: (arithmetic expansion) return the result of mathematical operations
((...))
: (arithmetic evaluation) perform calculation and change the value of variables
Bash can do integer math, but not decimal or fractional
To do more precise math, consider using bc
or awk
a=3
((a+=3))
echo $a
# Output: 6
((a++))
echo $a
# Output: 7
((a--))
echo $a
# Output: 6
a=$a+2 # bash treat this as a string not a number
# Output: 6+2
# To prevent bash treating the number as string, we use declare
declare -i b=3 # treat as integer
b=$b+3 # no black here
# Output: 6
# for more precise math
declare -i c=1
e=$(echo "scale=3; $c/$b" | bc) # scale: 位數
echo $e
# Output: 0.166
echo $RANDOM
# to get reduce range from 1 to 20
echo $((1 + RANDOM % 20)) # range from 1 to 20
[ ... ]
: an alias for test and is used to test, evaluate, expression (builtin function) 注意前後要空格
use help test
for more information
- 0: success
- 1 or other number: failure
$?
: read the value of the return status
# test whether a file exist
if test -f "$FILE"; then
echo "$FILE exists."
fi
# another way
if [ -f "$FILE" ]; then
echo "$FILE exists."
fi
# test string operator (=, !=, >, <)
# >: str1 sorts before str2 lexicographically
[ "cat" = "dog" ]; echo $?
[ "cat" = "cat" ]; echo $?
[ 4 -lt 5 ]; echo $? # less than
# Output: 0
[ 5 -ge 3]; echo $? # greater or equal to
# Output: 0
[ ! 5 -ge 3]; echo $? # not greater or equal to
# Output: 1
[[ ... ]]
: extended test
- same operations as test, and add a few other features
- can create a more complex logic (multiple conditions is allowed in one single test)
# whether my home directory is directory & whether the Bash binary exists
[[ -d ~ && -a /bin/bash ]]; echo $?
# Output: 0
[[ -d ~ && -a /bin/mash ]]; echo $?
# Output: 1
[[ -d ~ || -a /bin/bash ]]; echo $?
# Output: 0
[[ -d ~ ]] && echo ~ is a directory
# Output: /home/mobaxterm is a directory
[[ -d /bin/bash ]] && echo ~ is a directory
# since /bin/bash is not a directory, so doesn't output anything
# regular expression
[[ "cat" =~ c.* ]]; echo $?
# Output: 0
echo -e
: control special character
change color: e.g. \033[5mMyStIcAl sPhErE\033[0m
\t
: tab, \n
: new line
echo -e "Name\t\tNumber"; echo -e "Eric\t\t123"
# Output
#Name Number
#Eric 123
echo -e "This text\nbreaks over\nthree lines"
# Output
#This text
#breaks over
#three lines
printf "..."
: Output text using placeholders and formatting
printf "The results are: %d and %d\n" $((2+2)) $((4/1))
![](https://i.imgur.com/nOWmYCR.png =600x)
- support indexed and associative arrays
- remember only one-dimension array is supported in bash (no nested array)
# declare indexed array (use ```-a```)
declare -a snacks=("apple" "banna" "orange")
echo ${snacks[2]}
# Output: orange
snacks+=("mango")
echo ${snacks[@]} # @ output the whole array
snacks[6]="grapes"
for i in {0..6}; do echo "$i: ${snacks[$i]}"; done
# Output
#0: apple
#1: banna
#2: orange
#3: mango
#4:
#5:
#6: grapes
# declare associative array (use ```-A```)
declare -A office
office[city]="Hsinchu"
office["building name"]="ABC"
echo ${office["building name"]} is in ${office[city]}
- use some standard tools
df
: find storage utilizationfree
: find out the memory- use
awk
orsed
to extract text from output
#!/usr/bin/env bash
# Briefly summarize of system information
freespace=$(df -h / | awk 'NR==2 {print $4}')
freememory=$(free -h | awk 'NR==2 {print $4}')
printf "Current system report is summarized below\n"
# printf -v logdate "%(%Y-%m-%d)T"
printf "\tKernel Release:\t%s\n" $(uname -r)
printf "\tBash Version:\t%s\n" $BASH_VERSION
printf "\tFree Storage:\t%s\n" $freespace
printf "\tFree Memory:\t%s\n" $freememory
Output
Current system report is summarized below
./system_report.sh: line 7: /home/mobaxterm: is a directory
Kernel Release: 3.0.4(0.338/5/3)
Bash Version: 4.1.17(0)-release
Free Storage:
Free Memory:
if ...
then
...
fi
# if ... else if ... else
if ...; then
...
elif ...; then
...
else
...
fi
- while
echo "While Loop"
declare -i n=0
while (( n<10 ))
do
echo "n: $n"
(( n++ ))
done
- until
echo "Until Loop"
declare -i m=0
until (( m==3 ))
do
echo "m: $m"
(( m++ ))
done
- for
echo "For loop"
for i in {0..6}
do
echo $i
done
declare -a fruits=("apple" "banana" "cherry")
for i in ${fruits[@]}
do
echo $i
done
animal="dog"
case $animal in
cat) echo "Feline";;
dog|puppy) echo "Canine";;
*) echo "No match" # else
esac # case spell backward
Output:
Canine
#!/usr/bin/env bash
greet() {
echo "Hi there, $1. What a nice $2"
}
# argument start from $1
greet Eric morning
greet Andy evening
- $@: represent the list of arguments given to a function
- $FUNCNAME: represents the name of the function
display_result(){
declare -i i=1
for fruit in $@; do
echo "$i: ${fruit}"
(( i+=1 ))
done
}
display_result apple banana cherry grapes
- common usage for log file
- Write to files with output redirection operators (
>
and>>
)echo "abc" > output.txt
: overwrites the contentsecho "abc" >> output.txt
: append to the end of txt
- Read from files with input redirection (
<
) and read commandwhile read line; do echo $line; done < input.txt
- can read log files with regular expression
# f represents line
while read f
do echo " $f"
done < ~textfile.txt
- include quote viewer, a dice roll, or a card draw
- text that represent a string, filename, and so on
- are represented by numbered variables ($1, $2, and so on)
#!/usr/bin/env bash
# $0: represent the bash script
echo "The $0 script got the argument $1"
echo "Argument 2 is $2"
./myscript Banana Cherry
- loop for all arguments
for i in $@
do
echo $i
done
- allow us to pass information into a script from the CLI
- Are a combination of a dash and a letter (like
-u
or-p
) - are accessed using the getopts keyword
- can accept arguments of their own
- can be specified and used in any order
#!/usr/bin/env bash
while getopts u:p: option; dp
case $option in
u) user=$OPTARG;;
p) pass=$OPTARG;;
esac
echo "user: $user / pass: $pass"
./myscript -u Eric -p password123
- used to enable or disable certain features of a script
# ab (flags without colons after them that means I just wanna know whether these flags are used)
while getopts u:p:ab option; dp
case $option in
u) user=$OPTARG;;
p) pass=$OPTARG;;
a) echo "got the A flag";;
b) echo "got the B flag";;
esac
echo "user: $user / pass: $pass"
./myscript -u Eric -p password123 -a
./myscript -u Eric -p password123 -ab
- add
:
in front ofu:p:
while getopts :u:p:ab option; dp
case $option in
u) user=$OPTARG;;
p) pass=$OPTARG;;
a) echo "got the A flag";;
b) echo "got the B flag";;
?) echo "I don't know what $OPTARG is"
esac
echo "user: $user / pass: $pass"
- script often need input as they run
- the read keyword allows us to gather input, pausing the script unit input is provided
- input is stored in a variable
- allows user input
echo "What is your name?"
read name
echo "What is your password?"
read -s pass # secret (not show on the screen)
read -p "What's your favorite animal? " animal
echo "name: $name, pass: $pass, animal: $animal"
help read
select
echo "Which animal"
select animal in "cat" "dog" "bird" "fish"
do
echo "You selected $animal"
break
done
-
What if the user will simply ignore our request. e.g. just by pressing
Enter
ignoring input prompts. Might cause problem with empty information -
Sol1: give it a default value to prevent empty situation
#!/usr/bin/env bash
read -ep "Favorite color? " -i "Blue" favcolor
echo $favcolor
- Sol2: use condition to provide users the information
#!/usr/bin/env bash
# $#: the number of arguments provided at the command line
if (($# < 3)); then
echo "This command requires 3 arguments:"
echo "username, userid, and favorite number."
else
echo "username: $1"
echo "userid: $2"
echo "favorite number: $3"
fi
- Sol3: use loops to continue without specifying some kind of input
- It might be irritated for the users, so recommend to give it a default value
read -p "Favorite animal?" fav
while [[ -z $fav ]] # check whether it is empty -z
do
read -p "Cannot be empty! " fav
done
echo "$fav was selected."
# give defualt value cat [...]: default format
read -p "Favorite animal? [cat]" fav
while [[ -z $fav ]] # check whether it is empty -z
do
fav="cat" # set the value equal to whether the default would be
done
echo "$fav was selected."
=~
: regular expression
# {4}: 4 digits, ask
read -p "What year? [nnnn] " year
until [[ $year =~ [0-9]{4} ]]; do
read -p "A year, please! [nnnn] " year
done
echo "Selected year: $year"
- tips: read the errors carefully, observe line numbers in errors
- check quotes and escaping
- single quotes ('') and double quotes ("")
- check spacing in tests
[[$a-gt3]]
will fail but [[ $a -gt 3 ]] will work
- check closure of expansions and substitutions
- add
echo
statement to keep track of parameter flow - use the
true
andfalse
built-ins to troubleshoot logic (to guarantee a successful or failed exit code) - break down complex scripts into smaller parts
- Tell Bash to print out commands before it executes them
-
set -x
and turn it off, you can useset +x
- check the user's Bash version before running the script
$BASH_VERSION
[[ ! $BASH_VERSION -ge 4 ]] && echo "You'll need to update to Bash 4+." && exit
- check if the user has nonstandard tools your script uses
[[ ! -a $(which nmap) ]] && echo "This script use Nmap, which was not found on this system." && exit
- write the script so that it's compatible with the Bourne Shell (sh) instead of just Bash