1.shell 腳本語言的基本用法
1.1shell 腳本創建
1.格式要求:首行shebang機制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
2.添加執行權限,在命令行上指定腳本的絕對或者相對路徑,也可以運行腳本解釋器直接運行腳本
1.2腳本的注釋規范
1、第一行一般為調用使用的語言
2、程序名,避免更改文件名為無法找到正確的文件
3、版本號
4、更改后的時間
5、作者相關信息
6、該程序的作用,及注意事項
7、最后是各版本的更新簡要說明
1.3腳本的執行方式
[root@centos8 data]# cat shell.sh
#!/bin/bash
ls
[root@centos8 data]# chmod +x shell.sh
#相對路徑執行
[root@centos8 data]# ./shell.sh
a.txt b.txt linxu.txt message passwd shell.sh systeminfo.sh test vimrc
#bash執行
[root@centos8 data]# bash shell.sh
a.txt b.txt linxu.txt message passwd shell.sh systeminfo.sh test vimrc
#執行遠程主機的shell腳本
[root@centos8 data]# #curl -s http://10.0.0.8/hello.sh|bash
[root@centos8 ~]#wget -qO - http://www.wangxiaochun.com/testdir/hello.sh |bash
1.4shell腳本調試
腳本常見的3種錯誤:
- 語法錯誤,后續的命令不繼續執行
- 命令錯誤,默認后續的命令還會繼續執行,用bash -n 無法檢查出來 ,可以使用 bash -x 進行觀察
- 邏輯錯誤,只能使用 bash -x 進行觀察
bash命令
bash -n:只檢測腳本中的語法錯誤,但無法檢查出命令錯誤,但不真正執行腳本
bash -x:調試並執行腳本
1.5變量
1.5.1 shell變量類型
內置變量,如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE
用戶自定義變量
1.5.2 變量的定義和引用
- 普通變量:生效范圍為當前shell進程;對當前shell之外的其它shell進程,包括當前shell的子shell 進程均無效
- 環境變量:生效范圍為當前shell進程及其子進程
- 本地變量:生效范圍為當前shell進程中某代碼片斷,通常指函數
變量賦值
#變量的賦值可以是以下多種形式
直接字串:name='root'
變量引用:name="$USER"
命令引用:name=`COMMAND` 或者 name=$(COMMAND)
變量引用
$name
${name}
范例
[root@centos8 data]# name='xiaoming'
[root@centos8 data]# echo $name
xiaoming
[root@centos8 data]# user='yonghu'
[root@centos8 data]# name="$user"
[root@centos8 data]# echo $name
yonghu
[root@centos8 data]# name=`ls -l /data/`
[root@centos8 data]# echo $name
total 8 -rw-r--r-- 1 root root 403 Jan 2 17:39 systeminfo.sh -rw-r--r-- 1 root root 110 Jan 3 10:17 test
[root@centos8 data]# echo "$name"
total 8
-rw-r--r-- 1 root root 403 Jan 2 17:39 systeminfo.sh
-rw-r--r-- 1 root root 110 Jan 3 10:17 test
范例:變量追加值
[root@centos8 data]# echo $user
yonghu
[root@centos8 data]# user+=:xiaoming
[root@centos8 data]# echo $user
yonghu:xiaoming
顯示和刪除變量
#set查看所有變量
#unset <name>刪除指定變量
#使用上述方式設置變量是臨時保存於內存當中,當退出終端就會銷毀
1.5.3 環境變量
說明
- 子進程(包括孫子進程)可以繼承父進程的變量,但是無法讓父進程使用子進程的變量
- 一旦子進程修改從父進程繼承的變量,將會新的值傳遞給孫子進程
- 一般只在系統配置文件中使用,在腳本中較少使用
環境變量的聲明和賦值:
#聲明並賦值
export name=VALUE
declare -x name=VALUE
#或者分兩步實現
name=VALUE
export name
變量引用:
$name
${name}
顯示所有環境變量:
env
printenv
export
declare -x
刪除變量
unset
bash的內建環境變量
PATH
SHELL
USER
UID
HOME
PWD
SHLVL #shell的嵌套層數,即深度
LANG
MAIL
HOSTNAME
HISTSIZE
_ #下划線 表示前一命令的最后一個參數
1.5.4 只讀變量
只能聲明定義,但后續不能修改和刪除,即常量;退出當前終端可以銷毀聲明的只讀變量
聲明只讀變量:
readonly name
declare -r name
查看只讀變量
readonly [-p]
declare -r
1.5.5 變量位置
在bash shell中內置的變量, 在腳本代碼中調用通過命令行傳遞給腳本的參數
$1, $2, ... 對應第1個、第2個等參數,shift [n]換位置
$0 命令本身,包括路徑
$* 傳遞給腳本的所有參數,全部參數合為一個字符串
$@ 傳遞給腳本的所有參數,每個參數為獨立字符串
$# 傳遞給腳本的參數的個數
#清空所有位置變量
set --
范例
[root@centos8 data]# cat arg.sh
#!/bin/bash
echo "arg1 is $1"
echo "arg1 is $2"
echo "arg1 is $3"
echo "the number of arg is $#"
echo "all args is $*"
echo "all args is $@"
echo "the script name is `basename $0`"
[root@centos8 data]# bash arg.sh {1..5}
arg1 is 1
arg1 is 2
arg1 is 3
the number of arg is 5
all args is 1 2 3 4 5
all args is 1 2 3 4 5
the script name is arg.sh
rm命令修改
#用戶使用rm命令時不刪除文件而是移動到/tmp文件夾
[root@centos8 data]# cat rm.sh
COLOR="echo -e \E[1;31m"
END="\E[0m"
DIR=/tmp/`date +%F_%H-%M-%S`
mkdir $DIR
mv $* $DIR
$COLOR move $* to $DIR $END
1.5.6 退出狀態碼
$?
$?的值為0,代表成功
$?的值是1到255 #代表失敗
注意:
- 如果未給腳本指定退出狀態碼,整個腳本的退出狀態碼取決於腳本中執行的最后一條命令的狀態碼
1.5.7 腳本安全和 set
$- 變量
[root@centos8 data]# echo $-
himBHs
h:hashall,打開選項后,Shell 會將命令所在的路徑hash下來,避免每次都要查詢。通過set +h將h選 項關閉
i:interactive-comments,包含這個選項說明當前的 shell 是一個交互式的 shell。所謂的交互式shell, 在腳本中,i選項是關閉的 m:monitor,打開監控模式,就可以通過Job control來控制進程的停止、繼續,后台或者前台執行等
B:braceexpand,大括號擴展
H:history,H選項打開,可以展開歷史列表中的命令,可以通過!感嘆號來完成,例如“!!”返回上最近的 一個歷史命令,“!n”返回第 n 個歷史命令
set 命令
-u 在擴展一個沒有設置的變量時,顯示錯誤信息, 等同set -o nounset
-e 如果一個命令返回一個非0退出狀態值(失敗)就退出, 等同set -o errexit
-o option 顯示,打開或者關閉選項
顯示選項:set -o
打開選項:set -o 選項
關閉選項:set +o 選項
1.6 printf
printf:格式化要輸出的數據
printf FORMAT [ARGUMENT]...
常用格式替換符
-
%s 字符串
-
%f 浮點格式
-
%b 相對應的參數中包含轉義字符時,可以使用此替換符進行替換,對應的轉義字符會被轉 義
-
%c ASCII字符,即顯示對應參數的第一個字符
-
%d,%i 十進制整數
-
%o 八進制值
-
%u 不帶正負號的十進制值
-
%x 十六進制值(a-f)
-
%X 十六進制值(A-F)
-
%% 表示%本身
-
%s 中的數字代表此替換符中的輸出字符寬度,不足補空格,默認是右對齊,%-10s表示10個字 符寬,- 表示左對齊
常用轉義字符
- \a 警告字符,通常為ASCII的BEL字符
- \n 換行
- \r 回車
- \t 水平制表符
- \v 垂直制表符
范例
[root@centos8 data]# printf "%d\n" 123 321 123
123
321
123
[root@centos8 data]# printf "(%s) " 1 2 3
(1) (2) (3) [root@centos8 data]#
[root@centos8 data]# printf "(%s) " 1 2 3;echo
(1) (2) (3)
[root@centos8 data]# printf "(%s) " 1 2 3;echo ""
(1) (2) (3)
[root@centos8 data]# printf "%10s" name;echo
name
1.7算術運算
shell 支持算術運算,但只支持整數,不支持小數
shell運算符
+
-
*
/
% 取模,即取余數,示例:9%4=1,5%3=2
** 乘方
實現算數運算的方式
1. let var=算數表達式
2. ((var=算數表達式))
3. var=$[算數表達式]
4. var=$((算數表達式))
5. delare -i var = 數值
6. echo '算術表達式' | bc
7. var=$(expr arg1 arg2 arg3 ...)
范例
#生成 1 - 50 之間隨機數
[root@centos8 data]# echo $[RANDOM%50+1]
12
增強型賦值:
+= i+=10 相當於 i=i+10
-= i-=j 相當於 i=i-j
*=
/=
%=
++ i++,++i 相當於 i=i+1
-- i--,--i 相當於 i=i-1
范例
#i++和++i
[root@centos8 ~]#unset i j ; i=1; let j=i++; echo "i=$i,j=$j"
i=2,j=1
[root@centos8 ~]#unset i j ; i=1; let j=++i; echo "i=$i,j=$j"
i=2,j=2
#使用計算器實現浮點運算
[root@centos8 data]# echo "scale=3;20/3" | bc
6.666
1.8邏輯運算
與:&:和0相與,結果為0,和1相與,結果保留原值
1 與 1 = 1
1 與 0 = 0
0 與 1 = 0
0 與 0 = 0
或:|:和1相或結果為1,和0相或,結果保留原值
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
! 1 = 0 ! true
! 0 = 1 ! false
異或:^
相同為假,不同為真。
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 1 = 1
0 ^ 0 = 0
范例
#注意區分邏輯運算的二進制值與$?的返回值含義剛好相反
#利用異或可以實現兩個變量的數值交換
[root@centos8 data]# x=30;y=20;x=$[x^y];y=$[x^y];x=$[y^x];echo "x=$x y=$y"
x=20 y=30
短路運算
-
短路與
CMD1 短路與 CMD2
第一個CMD1結果為假 (0),總的結果必定為0,因此不需要執行CMD2
-
短路或
CMD1 短路或 CMD2
第一個CMD1結果為真 (1),總的結果必定為1,因此不需要執行CMD2
1.9條件測試命令
若真,則狀態碼變量 $? 返回0
若假,則狀態碼變量 $? 返回1
條件測試命令:
- test EXPRESSION
- [ EXPRESSION ] #和test 等價,建議使用 [ ]
- [[ EXPRESSION ]]
查看幫助
[root@centos8 data]# help [
[root@centos8 data]# help test
選項
-a FILE True if file exists.
-b FILE True if file is block special.
-c FILE True if file is character special.
-d FILE True if file is a directory.
-e FILE True if file exists.
-f FILE True if file exists and is a regular file.
-g FILE True if file is set-group-id.
-h FILE True if file is a symbolic link.
-L FILE True if file is a symbolic link.
-k FILE True if file has its `sticky' bit set.
-p FILE True if file is a named pipe.
-r FILE True if file is readable by you.
-s FILE True if file exists and is not empty.
-S FILE True if file is a socket.
-t FD True if FD is opened on a terminal.
-u FILE True if the file is set-user-id.
-w FILE True if the file is writable by you.
-x FILE True if the file is executable by you.
-O FILE True if the file is effectively owned by you.
-G FILE True if the file is effectively owned by your group.
-N FILE True if the file has been modified since it was last read.
String operators:
-z STRING True if string is empty.
-n STRING
STRING True if string is not empty.
STRING1 = STRING2
True if the strings are equal.
STRING1 != STRING2
True if the strings are not equal.
STRING1 < STRING2
True if STRING1 sorts before STRING2 lexicographically.
STRING1 > STRING2
True if STRING1 sorts after STRING2 lexicographically.
Other operators:
#變量測試
-o OPTION True if the shell option OPTION is enabled.
-v VAR True if the shell variable VAR is set.
-R VAR True if the shell variable VAR is set and is a name
reference.
! EXPR True if expr is false.
EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
#數值測試
arg1 OP arg2 Arithmetic tests. OP is one of -eq, -ne,
-lt, -le, -gt, or -ge.
Arithmetic binary operators return true if ARG1 is equal, not-equal,
less-than, less-than-or-equal, greater-than, or greater-than-or-equal
than ARG2.
1.9.1變量測試
范例
#判斷 NAME 變量是否定義
[ -v NAME ]
#判斷 NAME 變量是否定義並且是否引用
[ -R NAME ]
[root@centos8 data]# echo $name
[root@centos8 data]# [ -v name ]
[root@centos8 data]# echo $?
1
[root@centos8 data]# name="xiaoming"
[root@centos8 data]# [ -v name ]
[root@centos8 data]# echo $?
0
[root@centos8 data]# [ -R name ]
[root@centos8 data]# echo $?
1
[root@centos8 data]# [ -v name ]
[root@centos8 data]# echo $?
0
1.9.1數值測試
范例
[root@centos8 data]# i=10;j=20;
[root@centos8 data]# [ i -eq j ]
-bash: [: i: integer expression expected
[root@centos8 data]# [ $i -eq $j ]
[root@centos8 data]# echo $?
1
1.9.2字符串測試
范例
[root@centos8 data]# echo $name
[root@centos8 data]# [ -z $name ]
[root@centos8 data]# echo $?
0
[root@centos8 data]# unset name
[root@centos8 data]# [ -z $name ]
[root@centos8 data]# echo $?
0
[root@centos8 data]# name=xiaoming
[root@centos8 data]# [ -z "$name" ]
[root@centos8 data]# echo $?
1
[ ]用法
[[]] 用法,建議,當使用正則表達式或通配符使用,一般情況使用 [ ]
== 左側字符串是否和右側的PATTERN相同
注意:此表達式用於[[ ]]中,PATTERN為通配符
=~ 左側字符串是否能夠被右側的正則表達式的PATTERN所匹配
注意: 此表達式用於[[ ]]中;擴展的正則表達式
范例
[root@centos8 data]# NAME=linux
[root@centos8 data]# [[ "$NAME" == linu\* ]]
[root@centos8 data]# echo $?
1
[root@centos8 data]# [[ "$NAME" == linu* ]]
[root@centos8 data]# echo $?
0
#結論:[[ == ]] == 右側的 * 做為通配符,不要加“”,只想做為*, 需要加“” 或轉義
1.9.3 文件測試
范例
[root@centos8 data]# [ -a /data/test ]
[root@centos8 data]# echo $?
0
[root@centos8 data]# ![ -a /data/test ]
[ -a /data/test ] -a /data/test ]
-bash: [: too many arguments
[root@centos8 data]# ! [ -a /data/test ]
[root@centos8 data]# echo $?
1
[root@centos8 data]# [ -w /etc/shadow ]
[root@centos8 data]# echo $?
0
1.10 () 和 {}
(CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以將多個命令組合在一起,批量執行。使用man bash可以查看幫助
區別
( list ) 會開啟子shell,並且list中變量賦值及內部命令執行后,將不再影響后續的環境
{ list; } 不會啟子shell, 在當前shell中運行,會影響當前shell環境
范例
[root@centos8 data]# echo $BASHPID
164220
[root@centos8 data]# ( echo $BASHPID;sleep 100)
164450
sshd(976)───sshd(164206)───sshd(164219)───bash(164220)─┬─bash(164449)───sleep(164450)
[root@centos8 data]# echo $BASHPID
164220
[root@centos8 data]# { echo $BASHPID;}
16422
1.11 組合測試條件
1.11.1 第一種方式
[ EXPRESSION1 -a EXPRESSION2 ] 並且,EXPRESSION1和EXPRESSION2都是真,結果才為真
[ EXPRESSION1 -o EXPRESSION2 ] 或者,EXPRESSION1和EXPRESSION2只要有一個真,結果就為 真
[ ! EXPRESSION ] 取反
范例
[root@centos8 data]# FILE=/data/test
[root@centos8 data]# [ -f $FILE -a -x $FILE ]
[root@centos8 data]# echo $?
1
[root@centos8 data]# chmod +x /data/test
[root@centos8 data]# ll
total 20
-rw-r--r-- 1 root root 0 Jan 9 22:28 a.log
-rw-r--r-- 1 root root 181 Jan 4 18:29 arg.sh
-rw-r--r-- 1 root root 1 Jan 6 19:17 lx.txt
-rw-r--r-- 1 root root 116 Jan 5 15:50 rm.sh
-rw-r--r-- 1 root root 403 Jan 2 17:39 systeminfo.sh
-rwxr-xr-x 1 root root 110 Jan 3 10:17 test
[root@centos8 data]# [ -f $FILE -a -x $FILE ]
[root@centos8 data]# echo $?
0
1.11.2 第二種方式
COMMAND1 && COMMAND2 並且,短路與,代表條件性的AND THEN 如果COMMAND1 成功,將執行COMMAND2,否則,將不執行COMMAND2
COMMAND1 || COMMAND2 或者,短路或,代表條件性的OR ELSE 如果COMMAND1 成功,將不執行COMMAND2,否則,將執行COMMAND2
! COMMAND #非,取反
范例
[root@centos8 data]# test "A" = "B" && echo "string are equal"
[root@centos8 data]# test "A" = "A" && echo "string are equal"
string are equal
[root@centos8 data]# ll
-rw-r--r-- 1 root root 110 Jan 3 10:17 test.sh
[root@centos8 data]# [ -f $FILE ] && [[ $FILE =~ \.sh$ ]] && chmod +x $FILE
[root@centos8 data]# ll
-rwxr-xr-x 1 root root 110 Jan 3 10:17 test.sh
&&與||組合使用
[root@centos8 data]# NAME=user;id $NAME && echo "$NAME is exist" || echo "$NAME is not exist"
uid=1001(user) gid=1001(user) groups=1001(user)
user is exist
#注意:如果&& 和 || 混合使用,&& 要在前,|| 放在后。否則會出現邏輯錯誤
1.12 read命令
read:使用read來把輸入值分配給一個或多個shell變量,read從標准輸入中讀取值,給每個單詞分配一個變 量,所有剩余單詞都被分配給最后一個變量,如果變量名沒有指定,默認標准輸入的值賦值給系統內置 變量REPLY
read [options] [name ...]
常用選項
- -p 指定要顯示的提示
- -s 靜默輸入,一般用於密碼
- -n N 指定輸入的字符長度N
- -d '字符' 輸入結束符
- -t N TIMEOUT為N秒
范例
[root@centos8 data]# read
username
[root@centos8 data]# echo $REPLY
username
[root@centos8 data]# read NAME SEX
xiaoming boy
[root@centos8 data]# echo $NAME $SEX
xiaoming boy
[root@centos8 data]# read -p "please input your name:" NAME
please input your name:daxiong
[root@centos8 data]# echo $NAME
daxiong
面試題 read和輸入重定向
[root@centos8 data]# echo 1 2 | read x y;echo $x $y
[root@centos8 data]# echo 1 2 | (read x y;echo $x $y)
1 2
#Each command in a pipeline is executed as a separate process (i.e., in a subshell)
#管道符把每個命令執行都在獨立的子進程中,所以第一種情況父進程無法讀取子進程的變量
2 bash的配置文件
2.1 配置文件分類
全局生效配置文件
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
個人用戶生效配置文件
~/.bash_profile
~/.bashrc
2.2 配置文件加載順序
2.2.1 交互式登錄順序
- 直接通過終端輸入賬號密碼登錄
- 使用 su - UserName 切換的用戶
配置文件加載順序
/etc/profile.d/*.sh
/etc/bashrc
/etc/profile
/etc/bashrc #此文件執行兩次
.bashrc
.bash_profile
2.2.2 非交互式順序
- su UserName
- 圖形界面下打開的終端
- 執行腳本
- 任何其他的bash實例
配置文件加載順序
/etc/profile.d/*.sh
/etc/bashrc
.bashrc
2.3 配置文件分類
2.3.1 Profile類
為交互式登錄的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
個人:~/.bash_profile
用途:
1)用於定義環境變量
2)運行腳本或命令
2.3.2 Bashrc類
為非交互式和交互式登錄的shell提供配置
全局:/etc/bashrc
個人:~/.bashrc
用途:
1)定義命令別名和函數
2)定義本地變量
2.3.3 配置文件生效方式
- 重新啟動shell進程
- source|. 配置文件
3.流程控制
3.1 條件判斷if
格式
if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
范例
#BMI指數
[root@centos8 script]# cat bmi.sh
#!/bin/bash
#
#********************************************************************
#Author: yuanyuan
#QQ: 1136132605
#Date: 2021-01-11
#FileName: bmi.sh
#URL: http://www.bestvae.cn
#Description: The test script
#Copyright (C): 2021 All rights reserved
#********************************************************************
read -p "請輸入身高(單位m)" HIGH
if [[ ! "$HIGH" =~ ^[0-2]\.?[0-9]{,2}$ ]];then
echo "輸入身高錯誤"
exit 1
fi
read -p "請輸入體重(單位kg)" WEIGHT
if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then
echo "輸入體重錯誤"
exit 1
fi
BMI=`echo $WEIGHT/$HIGH^2|bc`
if [ "$BMI" -le 18 ];then
echo "你太瘦了多吃點"
elif [ "$BMI" -lt 24 ];then
echo "你的身材很棒"
else
echo "你太胖了,加強運動"
fi
3.2 條件判斷 case 語句
格式
case: case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
Execute commands based on pattern matching.
#case支持全局通配符
*: 任意長度任意字符
?: 任意單個字符
[]:指定范圍內的任意單個字符
|: 或,如 a或b
范例
#運維表
#!/bin/bash
echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF
1)備份數據庫
2)清理日志
3)軟件升級
4)軟件回退
5)刪庫跑路
EOF
echo -en '\E[0m'
read -p "請輸入上面的數字:" MENU
if [[ ! "$MENU" =~ ^[1-5]$ ]];then
echo "你的輸入有誤"
exit 1
fi
case $MENU in
1)
echo "備份成功";;
2)
echo "清理日志";;
3)
echo "清理日志";;
4)
echo "清理日志";;
5)
echo "清理日志";;
esac
練習
1、編寫腳本 createuser.sh,實現如下功能:使用一個用戶名做為參數,如果指定參數的用戶存在,就 顯示其存在,否則添加之。並設置初始密碼為123456,顯示添加的用戶的id號等信息,在此新用戶第一 次登錄時,會提示用戶立即改密碼,如果沒有參數,就提示:請輸入用戶名
[root@centos8 script]# cat creatuser.sh
#!/bin/bash
if [ -z "$1" ];then
echo "請輸入用戶名字格式為$0 username"
exit 1
else
getent passwd $1 > /dev/null
if [ "$?" -eq 0 ];then
echo "$1 is exist"
echo `getent passwd $1`
exit 1
else
useradd $1
echo '123456' | passwd --stdin $1 > /dev/null
passwd -e $1 > /dev/null
echo -e "$1用戶創建成功初始密碼為123456\n`getent passwd $1`"
fi
fi
2、編寫生成腳本基本格式的腳本,包括作者,聯系方式,版本,時間,描述等
[root@centos8 script]# cat /root/.vimrc
set ts=4
set expandtab
set ignorecase
set cursorline
set autoindent
set showcmd
autocmd BufNewFile *.sh exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1,"#!/bin/bash")
call setline(2,"#")
call setline(3,"#********************************************************************")
call setline(4,"#Author: yuanyuan")
call setline(5,"#QQ: 1136132605")
call setline(6,"#Date: ".strftime("%Y-%m-%d"))
call setline(7,"#FileName: ".expand("%"))
call setline(8,"#URL: http://www.bestvae.cn")
call setline(9,"#Description: The test script")
call setline(10,"#Copyright (C): ".strftime("%Y")." All rights reserved")
call setline(11,"#********************************************************************")
call setline(12,"")
endif
endfunc
autocmd BufNewFile * normal G
3.3 循環
3.3.2 for循環
格式:
#格式1
for NAME [in WORDS ... ] ; do COMMANDS; done
#格式2
for (( 控制變量初始化;條件判斷表達式;控制變量的修正表達式 )); do COMMANDS; done
循環列表生成方式:
-
直接給出列表
-
整數列表
{start..end}
$(seq [start [step]] end)
-
返回列表的命令
$(COMMAND)
-
使用*.sh
-
變量引用,如:$@,$*
范例:
#使用兩種方式計算1+...+100的結果
[root@centos8 ~]$sum=0;seq -s+ 100 | bc
5050
[root@centos8 ~]$sum=0;for i in {1..100};do sum=$[i+sum];done;echo $sum
5050
#九九乘法表
[root@centos8 script]$vim for_9.sh
for i in {1..9};do
for j in `seq $i`;do
echo -e "${j}x${i}=$[i*j]\t\c"
done
echo
done
#使用另一種方式輸出等腰三角形
[root@centos8 script]$vim for_trian.sh
read -p "輸入等腰三角形的行數:" line
for ((i=1;i<=line;i++));do
for ((k=0;k<=line-i;k++));do
echo -e ' \c'
done
for ((j=1;j<=2*i-1;j++));do
echo -e '*\c'
done
echo
done
3.3.2 while循環
格式:
while COMMANDS; do COMMANDS; done
while CONDITION; do
循環體
done
#進入條件:CONDITION為true
#當循環次數不確定時建議使用while
3.3.3 until 循環
格式:
until COMMANDS; do COMMANDS; done
until CONDITION; do
循環體
done
#進入條件: CONDITION 為false
#退出條件: CONDITION 為true
3.3.4 循環控制語句 continue
continue [N]:提前結束第N層的本輪循環,而直接進入下一輪判斷;最內層為第1層
范例 :
[root@centos8 script]$vim continue.sh
for ((i=0;i<=10;i++));do
for ((j=0;j<10;j++));do
[ $j -eq 5 ] && continue 2
echo -e $j'\c'
done
echo ----------------------
done
[root@centos8 script]$bash continue.sh
0123401234012340123401234012340123401234012340123401234
3.3.5 循環控制語句 break
break [N]:提前結束第N層整個循環,最內層為第1層
范例:
[root@centos8 script]$vim continue.sh
for ((i=0;i<=10;i++));do
for ((j=0;j<10;j++));do
[ $j -eq 6 ] && break 2
echo -e $j'\c'
done
echo ----------------------
done
[root@centos8 script]$bash continue.sh
012345
3.3.6 循環控制 shift 命令
shift [n] 用於將參量列表 list 左移指定次數,缺省為左移一次。
范例:
[root@centos8 script]$cat doit.sh
#!/bin/bash
until [ -z "$1" ]
do
echo "$1"
shift
done
echo
[root@centos8 script]$bash doit.sh 1 2 3 4 5
1
2
3
4
5
3.3.7 select 循環與菜單
格式:
select NAME [in WORDS ... ;] do COMMANDS; done
select variable in list ;do
循環體命令
done
- select 循環主要用於創建菜單,按數字順序排列的菜單項顯示在標准錯誤上,並顯示 PS3 提示符,等待用戶輸入
- 用戶輸入被保存在內置變量 REPLY 中
- select 是個無限循環,因此要用 break 命令退出循環,或用 exit 命令終止腳本。也可以按 ctrl+c 退出循環
范例:
[root@centos8 script]$cat select.sh
#!/bin/bash
sum=0
PS3="請輸入1-6"
select menu in 北京烤鴨 佛跳牆 小龍蝦 羊蠍子 火鍋 點菜結束 ;do
case $REPLY in
1)
echo $menu 價格是100
let sum+=100
;;
2)
echo $menu 價格是88
let sum+=88
;;
3)
echo $menu 價格是10
let sum+=10
;;
4)
echo $menu 價格是190
let sum+=190
;;
5)
echo $menu 價格是17
let sum+=17
;;
6)
break;;
esac
done
echo "$sum"
4 函數
Shell程序在子Shell中運行,而Shell函數在當前Shell中運行。因此在當前Shell中,函數可對shell中變量 進行修改
4.1 函數管理
4.1.1 定義函數
#語法一:
function name {
COMMANDS ;
}
#語法二:
function name () {
COMMANDS ;
}
#語法三:
func_name (){
COMMANDS ;
}
4.1.2 查看函數
#顯示已定義函數名稱及定義
delcare -f
#顯示已定義函數名稱
delcare -F
4.1.3 刪除函數
unset func_name
4.2 函數調用
4.2.1 交互式環境函數調用
范例:
[root@centos8 script]$dir() { ls -l;}
[root@centos8 script]$dir
total 44
-rw-r--r-- 1 root root 482 Mar 4 11:39 continue.sh
[root@centos8 script]$declare -f dir
dir ()
{
ls --color=auto -l
}
4.2.2 腳本中定義及使用
注意:函數使用前需先定義
[root@centos8 script]$bash function.sh
hello
[root@centos8 script]$cat function.sh
#!/bin/bash
function hello(){
echo "hello"
}
hello
4.2.3 調用函數文件
格式:
. filename 或 source filename
范例:
[root@centos8 script]$. function
[root@centos8 script]$hello
hello
[root@centos8 script]$cat function
#!/bin/bash
function hello(){
echo "hello"
}
4.3 環境函數
定義:
export -f function_name
declare -xf function_name
查看:
export -f
declare -xf
4.4 函數參數
- 傳遞參數給函數:在函數名后面以空白分隔給定參數列表即可
- 在函數體中當中,可使用$1, $2, ...調用這些參數;還可以使用$@, $*, $#等特殊變量,函數名稱存儲於$FUNCNAME變量中
4.5 函數中變量有效范圍
- 普通變量:只在當前shell進程有效,為執行腳本會啟動專用子shell進程;因此,本地變量的作用 范圍是當前shell腳本程序文件,包括腳本中的函數
- 環境變量:當前shell和子shell有效
- 本地變量:函數的生命周期;函數結束時變量被自動銷毀
由於普通變量和局部變量會沖突,建議在函數中只使用本地變量
[root@centos8 script]$name=hello
[root@centos8 script]$echo $name
hello
[root@centos8 script]$. function
[root@centos8 script]$hello
[root@centos8 script]$echo $name
tom
[root@centos8 script]$cat function
#!/bin/bash
function hello(){
name=tom
}
在函數中定義本地變量的方法
local NAME=VALUE
5 其他腳本工具
5.1 trap
捕捉系統信號並且執行相應動作
-
trap '觸發指令' 信號
進程收到系統發出的指定信號后,將執行自定義指令,而不會執行原操作
-
trap '' 信號
忽略信號的操作
-
trap '-' 信號
恢復原信號的操作
-
trap -p
列出自定義信號操作
-
trap finish EXIT
當腳本退出時,執行finish函數
-
signal_spec可以是簡寫,信號數字、信號全稱(int、2、SIGINT)
范例:
#捕捉到ctrl+c時什么也不執行,使用kill -L查看信號
[root@centos8 script]$cat trap.sh
#!/bin/bash
trap '' int quit
for((i=0;i<=10;i++))
do
sleep 1
echo $i
done
5.2 mktemp
創建並顯示臨時文件或目錄,主要用於避免文件命名沖突
格式:
mktemp [OPTION]... [TEMPLATE]
注意:TEMPLATE: filenameXXX,X至少要出現三個而且必須是大寫
常見選項:
- -d 創建目錄
- -p 指定創建臨時文件的目錄
范例:
[root@centos8 test]$mktemp testXXX
testDji
[root@centos8 test]$mktemp --tmpdir= testXXX
/tmp/testle5
#rm -rf命令的實現
[root@centos8 script]$cat rm_rf.sh
#!/bin/bash
DIR=`mktemp -d /tmp/$(date +%F_%H-%M-%S)XXX`
mv $* $DIR
echo $* is move to $DIR
5.3 install
install 功能相當於cp,chmod,chown,chgrp 等相關工具的集合
格式:
install [OPTION]... [-T] SOURCE DEST 單文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...創建空目錄
選項:
- -m MODE.默認755
- -o OWNER
- -g GROUP
- -d DIRNAME 目錄
5.4 expect
用於實現處理交互命令,實現自動完成交互
格式:
expect [選項] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
選項:
- -c:從命令執行expect腳本
- -d:輸出調試信息
expect中相關命令:
- spawn 啟動新的進程
- expect 從進程接收字符串
- send 用於向進程發送字符串
- interact 允許用戶交互
- exp_continue 匹配多個字符串在執行動作后加此命令
[root@centos8 script]$expect -c 'expect "\n" {send "pressed enter\n"}'
pressed enter
[root@centos8 script]$expect -dc 'expect "\n" {send "pressed enter\n"}'
expect version 5.45.4
expect: does "" (spawn_id exp0) match glob pattern "\n"? no
expect: does "\n" (spawn_id exp0) match glob pattern "\n"? yes
expect: set expect_out(0,string) "\n"
expect: set expect_out(spawn_id) "exp0"
expect: set expect_out(buffer) "\n"
send: sending "pressed enter\n" to { exp0 pressed enter
}
argv[0] = expect argv[1] = -dc argv[2] = expect "\n" {send "pressed enter\n"}
set argc 0
set argv0 "expect"
set argv ""
單分支語法:
passwdexpect1.5> expect "hi" {send "hello\n"}
hi
hello
多分支語法:
#語法1
[root@centos8 test]#expect
expect1.1> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n"
} "bye" { send "Good bye\n" }
hehe
Hehe yourself
#語法2
expect {
"hi" { send "You said hi\n"}
"hehe" { send "Hehe yourself\n"}
"bye" { send " Good bye\n"}
}
定義變量:
#set 變量名 值
set ip 10.0.0.7
位置參數:
#!/usr/bin/expect
set ip [lindex $argv 0]
6 數組
6.1 聲明數組
#普通數組可以不事先聲明,直接使用
declare -a ARRAY_NAME
#關聯數組必須先聲明,再使用:關聯數組就是自定義索引格式的數組
declare -A ARRAY_NAME
6.2 數組賦值
1)一次賦值一個元素
ARRAY_NAME[INDEX]=VALUE
2)一次賦值多個元素
ARRAY_NAME=(val1 val2 val3)
范例:
name=("tom" "xiaoming" "xiaohong")
num=({1..10})
file=(*.sh)
3)交互式賦值
read -a ARRAY
6.3 查看所有數組
顯示所有數組
declear -a
6.4 引用數組
引用數組元素
${ARRAY-NAME[INDEX]}
范例:
[root@centos8 script]$declare -a title
[root@centos8 script]$title=({1..3})
[root@centos8 script]$echo ${title}
1
引用數組所有元素
${ARRAY_NAME[*]}
${ARRAY_NAME[@]}
數組中元素的個數
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
6.5 刪除數組
刪除數組中的某元素
unset ARRAY[INDEX]
刪除整個數組
unset ARRAY
練習:
1.分別使用shell和expect實現遠程登陸主機
#expect語法實現(允許用戶交互,不會自動退出)
[root@centos8 script]# cat expect1.sh
#!/usr/bin/expect
set ip [lindex $argv 0]
set user [lindex $argv 1]
set passwd [lindex $argv 2]
spawn ssh $user@$ip
expect {
"yes/no" {send "yes\n";exp_continue}
"password" {send "$passwd\n"}
}
interact
#shell語法實現(此方式會在到達timeout時間后自動退出,默認timeout時間為10s)
[root@centos8 script]# cat expect.sh
#/bin/bash
ip=10.0.0.203
user=root
passwd=123321
expect << EOF
set timeout 2
spawn ssh $user@$ip
expect {
"yes/no" {send "yes\n";exp_continue}
"password" {send "$passwd\n"}
}
expect eof
EOF
2.生成10個隨機數保存於數組中,並找出其中最大值和最小值
[root@centos8 script]$cat random.sh
#!/bin/bash
declare -i max min
declare -a random
for((i=0;i<10;i++));do
random[$i]=$RANDOM
if [ $i -eq 0 ];then
min=${random[0]}
max=${random[0]}
else
[ ${random[$i]} -gt $max ] && max=${random[$i]}
[ ${random[$i]} -lt $min ] && min=${random[$i]}
fi
done
echo "all num are ${random[*]}"
echo "max num is $max"
echo "min num is $min"
3.輸入若干個數存入數組中,采用冒泡算法進行排序
[root@centos8 script]$cat sort.sh
#!/bin/bash
declare -a num
num=(1 10 6 5 8 7 9 4 3 2)
echo 調換順序之前數組順序 ${num[*]}
for((j=9;j>0;j--));do
for ((i=0;i<j;i++));do
if [ ${num[$i]} -gt ${num[$[i+1]]} ];then
b=${num[$i]}
num[$i]=${num[$[i+1]]}
num[$[i+1]]=$b
fi
done
done
echo 調換順序之后數組順序 ${num[*]}