寫在開頭
本文是閱讀《Linux命令行與shell腳本編程大全》時的一些筆記,主要是shell腳本的一些基本語法,
還有很多細節和高級內容沒有寫到。
筆者也是shell script菜鳥,如有不當歡迎指正。
shell腳本從入門到精通(初級)之入門篇
shell腳本從入門到精通(中級)之提高篇
目錄:
一、變量
二、數學計算
三、邏輯控制
四、輸入
五、輸出
六、函數
七、控制腳本
一、變量
1. 環境變量
#!/bin/bash
# 環境變量
echo "User Info:"
echo "user: $USER"
echo "UID : $EUID"
echo "home: $HOME"
echo “$HOSTNAME”
2. 用戶變量
變量命名規則:
- 由字母、數字和下划線組成
- 大小寫敏感
#!/bin/bash
# 用戶變量
var1=100
var2=hello
var3="hello world"
echo "$var1 $var2 $var3"
3. 特殊變量
變量 | 含義 |
---|---|
$0 | 當前腳本的文件名 |
$n | 傳遞給腳本或函數的參數。n 是一個數字 |
$# | 傳遞給腳本或函數的參數個數。 |
$* | 傳遞給腳本或函數的所有參數。 |
$@ | 傳遞給腳本或函數的所有參數 |
$? | 上個命令的退出狀態,或函數的返回值。 |
$$ | 當前Shell進程ID。對於 Shell 腳本,就是這些腳本所在的進程ID。 |
#!/bin/bash
echo "Total Number of Parameters : $#"
echo "File Name: $0"
echo "First Parameter : $1"
echo "First Parameter : $2"
echo "Quoted Values: $@"
echo "Quoted Values: $*"
echo "PID $$"
echo "$?"
$@ 與 $* 的區別
$* 和 $@ 都表示傳遞給函數或腳本的所有參數,不被雙引號(" ")包含時,都以"$1" "$2" … "$n" 的形式輸出所有參數。
但是當它們被雙引號(" ")包含時,"$*" 會將所有的參數作為一個整體,以"$1 $2 … $n"的形式輸出所有參數;"$@" 會將各個參數分開,以"$1" "$2" … "$n" 的形式輸出所有參數。
#!/bin/bash
echo ‘$@:’
for i in $@;do
echo $i
done
echo ‘"$@:"’
for i in "$@";do
echo $i
done
echo ‘$*:’
for i in $*;do
echo $i
done
echo ‘"$*:"’
for i in "$*";do
echo $i
done
結果:
$@:
1
2
3
"$*":
1
2
3
$@:
1
2
3
"$*":
1 2 3
4. ``與$()
把命令的執行結果賦給變量的兩種方式
#!/bin/bash
time=`date +%y%m%d`
time=$(date +%y%m%d)
二、數學計算
1. expr
#!/bin/bash
# 注意變量和=之間不能有空格,expr 和 = 之間要有一個空格
var= expr 1 + 2
echo $var
expr 極難用,一般不用
2. [] 與 (())
#!/bin/bash
#
var=$[ 1 + 2 ]
var=$(( 3 + 4 ))
3. bc
上述兩種只能計算整數,對於浮點數需要使用bc
在腳本中使用bc的格式:
variable=`echo "option; expression" |bc`
#!/bin/bash
var=`echo "scale=2;5/3" | bc`
echo $var
三、邏輯控制
1. if
1.1 if-then
格式:
if command
then
command
fi
例:
#!/bin/bash
if date
then
echo "command is succeed"
fi
1.2 if-then-else
格式:
if command
then
command
else
command
fi
例:
#!/bin/bash
# 查找系統中是否存在httpd用戶
if grep httpd /etc/passwd
then
echo "httpd is exist"
else
echo "httpd not find"
fi
1.3 if嵌套
格式:
if command
then
command
elif command
command
else
command
fi
2. test
功能:
- 數值比較
- 字符串比較
- 文件比較
格式:
test condition
或
[ command ] -- 更常用
2.1 數值比較
比較 | 描述 |
---|---|
-eq | 等於 |
-ge | 大於等於 |
-gt | 大於 |
-le | 小於等於 |
-lt | 小於 |
-ne | 不等於 |
例:
#!/bin/bash
date
if [ $? -eq 0 ];then
echo "command is succeed"
fi
# 或
if test date;then
echo "command is succeed"
fi
2.2 字符串比較
比較 | 描述 | 例 |
---|---|---|
str1 = str2 | 字符串是否相同 | |
str1 != str2 | 字符串是否不同 | |
str1 < str2 | str1是否比str2小 | |
str1 > str2 | str1是否比str2大 | [ b \> a ] && echo "true" (注意>需要轉義) |
-n str | 字符串長度非0為真 | [ -n "str" ] && echo "str is not null" |
-z str | 字符串長度為0為真 | [ -z "" ] && echo "str is null" |
2.3 文件比較
比較 | 描述 | 例 |
---|---|---|
-d file | 檢查file是否存在並是一個目錄 | [ -d /data ] && echo "exist" |
-f file | 檢查file是否存在並是一個目錄 | |
-e file | 檢查file是否存在 | |
-r file | 檢查file是否存在並可讀 | |
-s file | 檢查file是否存在並非空 | |
-w file | 檢查file是否存在並可寫 | |
-x file | 檢查file是否存在並可執行 | |
-O file | 檢查file是否存在並屬當前用戶所有 | |
-G file | 檢查file是否存在並且默認組與當前用戶相同 | |
file1 -nt file2 | file1是否比file2新 | |
file1 -ot file2 | file1是否比file2舊 |
2.4 復合條件
[ condition1 ] && [ condition2 ]
[ condition1 ] || [ condition2 ]
3. case
格式:
case variable in
pattern1 | pattern2) command1;;
pattern3) command2;;
*) default command;;
esca
例:
#!/bin/bash
read -p "input something: " var
case $var in
[0-9])
echo "number";;
[a-z])
echo "character";;
*)
echo "other";;
esac
4. for
4.1 bash中的for
格式:
for var in list
do
command
done
例1:查看服務狀態
#!/bin/bash
for service in apache2 mysqld zabbix-server zabbix-agent
do
status=$(systemctl status mysql | awk '/Active/ {print $2,$3}')
echo $service $status
done
例2:使用通配符
#!/bin/bash
# 注意在$file上加“”,否則如果出現帶空格的目錄名,腳本會出錯
for file in /tmp/*
do
if [ -d "$file" ]
then
echo "$file" is a directory
elif [ -f "$file" ]
then
echo "$file" is a file
fi
done
4.2 C 語言風格的for
例:
#!/bin/bash
for (( i=1; i<=10; i++ ))
do
echo $i
done
#!/bin/bash
# 單變量
for (( i=1; i<=10; i++ ))
do
echo $i
done
# 多變量
for (( i=1,j=10; i<=10; i++,j-- ))
do
echo $i $j
done
5. while
例:檢測站點狀態
#!/bin/bash
urls="
https://www.baidu.com
https://www.taobao.com
https://www.jd.com/abc
https://www.12306.cn/index/
192.168.1.111
"
for url in $urls;do
count=0
while [ $count -lt 3 ];do
STATUS=$(curl -I -m 10 -o /dev/null -s -w %{http_code} $url)
if [ $STATUS -eq 200 ];then
echo "$url OK"
break 1
fi
count=$(($count+1))
done
if [ $count -eq 3 ];then
echo "$url Error"
fi
done
6. until
#!/bin/bash
var=10
until [ $var -eq 0 ];do
var=$[$var-2]
echo $var
done
7. 控制循環
7.1 break
#!/bin/bash
# break 跳出當前循環
# break n 跳出n層循環
for (( i=0; i<10; i++ ));do
if [ $i -eq 5 ];then
break
fi
echo $i
done
7.2 continue
四、輸入
1. 命令行參數
例1:
#!/bin/bash
echo $1+$2=$[$1+$2]
./add.sh 3 4
3+4=7
例2:shift
#!/bin/bash
# 把變量的位置向左移
while [ -n "$1" ];do
echo $1
shift
done
2.getopts
格式:getopts optstring variable
optstring: 選項字母,如果字母要求有參數就加一個:,要去掉錯誤消息的話可以在optstring前加一個:
variable:保存當前參數
#!/bin/bash
# getopts的用法
# opt 會保存輸入的參數,如 r i
# OPTARG保存參數值
# 參數需要一個值就在參數后面加一個: 如i:
while getopts ri: opt
do
case "$opt" in
i) echo "install service $OPTARG";;
r) echo "remove all service";;
*) echo "Unknown option: $opt";;
esac
done
root@localhost:/# ./getopts.sh -i apache2
install service apache2
root@localhost:/# ./getopts.sh -r
remove all service
root@localhost:/# ./getopts.sh -a
./getopts.sh: illegal option -- a
Unknown option: ?
3. 獲得用戶輸入 read
3.1 普通用法
#!/bin/bash
read name
echo $name
3.2 指定提示符
#!/bin/bash
read -p "Enter your name: " name
echo "Hello $name "
3.3 指定超時時間
#!/bin/bash
if read -t 5 -p "Enter your name: " name
then
echo "Hello $name"
else
echo "TIME OUT"
fi
3.4 隱藏數據
#!/bin/bash
read -s -p "Enter passwd: " passwd
echo "$passwd"
3.5 限制輸入長度
#!/bin/bash
read -n1 -p "Do you want continue[Y/N]?" answer
echo
echo "$answer"
五、輸出
顯示腳本輸出的方法:
- 在顯示器上顯示
- 將輸出重定向到文件
描述符 | 縮寫 | 描述 |
---|---|---|
0 | STDIN | 標准輸入 |
1 | STDOUT | 標准輸出 |
2 | STDERR | 標准錯誤 |
1. 在腳本中重定向
1. 臨時重定向
使用場景:在腳本中生成錯誤消息
#!/bin/bash
echo "This is an error message" >&2
echo "This is normal output"
默認情況下Linux 會將STDERR定向到STDOUT
$./error.sh
This is an error message
This is normal output
在執行腳本的時候重定向STDERR,ERR文本就會被重定向
$ ./error.sh 2> error.log
This is normal output
$ cat error.log
This is an error message
2. 永久重定向
用exec命令告訴shell在執行腳本期間重定向某個特定文件描述符
#!/bin/bash
exec 2>errout
echo "This is error"
exec 1>testout
echo "testout"
echo "testout to errout" >&2
$ ./test.sh
This is error
$ cat errout
testout to errout
$ cat testout
testout
2. 記錄消息
tee : 將輸出一邊發送到顯示器一邊發送到日志文件
tee 默認會覆蓋原來的文件,可以使用-a追加
$ date | tee -a date.txt
Fri Nov 23 11:03:15 CST 2018
$ cat date.txt
Fri Nov 23 11:03:07 CST 2018
Fri Nov 23 11:03:15 CST 2018
六、函數
1.基本函數
#!/bin/bash
# 定義方式1
function foo {
echo "This is a func"
}
# 定義方式2
bar() {
echo "This is another func"
}
# 函數的調用
foo
bar
$ ./func.sh
This is a func
This is another func
2.返回值
- 默認退出狀態碼
#!/bin/bash
function foo {
echo "This is a func"
}
foo
echo "Exit status is $?"
- 使用return命令
#!/bin/bash
function foo {
echo "Hello world"
return 2
}
foo
echo "Exit status is $?"
./func.sh
Hello world
Exit status is 2
- 使用函數輸出
#!/bin/bash
function foo {
echo "Hello world"
}
foo
# 把函數的輸出賦值給變量
result=`foo`
echo "Exit status is $result"
$./func.sh
Hello world
Exit status is Hello world
3.變量
- 傳參
#!/bin/bash
function status {
systemctl status $1
}
status sshd
- 局部變量與全局變量
#!/bin/bash
# 定義全局變量
hostname="web"
function foo {
str="hello"
# 使用 local 定義局部變量
local user="http"
# 可以在函數內使用全局變量
echo "$hostname"
echo "$user"
}
foo
# 在函數中定義的局部變量不能在全局使用
echo "$str $user"
- 數組變量
如果將數組變量作為函數參數,函數只會取數組變量的第一個值
#!/bin/bash
function foo {
arr=$1
echo "The received array is ${arr[*]}"
}
myarr=(1 2 3 4 5)
foo $myarr
解決方法
#!/bin/bash
function foo {
arr=$@
echo "The received array is ${arr[*]}"
}
myarr=(1 2 3 4 5)
# 將該數組變量的值分解成單個的值,然后將這些值作為函數參數使用。
foo ${myarr[*]}
七、控制腳本
1.處理信號
1.1查看Linux信號
kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
在Linux編程時會遇到的最常見的Linux系統信號
信 號 | 值 | 描 述 | 觸發 |
---|---|---|---|
1 | SIGHUP | 掛起進程 | |
2 | SIGINT | 終止進程 | Ctrl + C |
3 | SIGQUIT | 停止進程 | |
9 | SIGKILL | 無條件終止進程 | |
15 | SIGTERM | 盡可能終止進程 | |
17 | SIGSTOP | 無條件停止進程,但不是終止進程 | |
18 | SIGTSTP | 停止或暫停進程,但不終止進程 | Ctrl+Z |
19 | SIGCONT | 繼續運行停止的進程 |
1.2 捕捉信號
格式:trap command signals
例
#!/bin/bash
trap "echo 'You Enter Ctrl + C'" SIGINT
for (( i=0; i<10; i++ ));do
echo $i
sleep 1
done
# ./signal.sh
0
1
^CYou Enter Ctrl + C
2
^CYou Enter Ctrl + C
2.腳本執行
- 腳本執行
bash file.sh
或
chmod +x file.sh
./file.sh - 后台運行腳本
./test.sh &
nohub ./test.sh &
附:退出狀態
可以使用$?查看上一個命令的退出狀態碼
狀態碼 | 含義 |
---|---|
0 | 命令成功結束 |
1 | 通用未知錯誤 |
2 | 誤用shell命令 |
126 | 命令不可執行 |
127 | 沒有找到命令 |
128+x | Linux信號x的嚴重錯誤 |
130 | 命令通過Ctrl+C終止 |
255 | 退出狀態碼越界 |