第一部分:指定解释器(必写)
# 表示以下内容会被bash解析
#!/bin/bash
注意:如果将解释器路径写死在脚本里,可能在某些系统里就会存在找不到解释器的兼容性问题,所以可以写成:
#!/bin/env bash
注释使用 #
第二部分 :具体的代码逻辑
第一个脚本: hello.sh
#!/bin/env bash
# 输出hello,world
echo "hello,world"
echo "hello,world"
echo "hello,world"
脚本的执行方式:
# 第一种 使用source
source hello.sh
# 第二种,给脚本添加执行权限,直接执行
chmod +x hello.sh
./hello.sh
变量名区分大小写,变量一般使用大写
# 定义变量,=两边不能有空格
A=hello
# 对于有空格的字符串,需要使用""
A="hello world"
# 使用变量 $VAR ${VAR}
echo $A
echo ${A}
# 取消变量
unset A
${VAR:start:end} # 左闭右开
[root@localhost ~] NUM=123456
[root@localhost ~] echo ${NUM:0:2}
12
[root@localhost ~] echo ${NUM::2}
12
[root@localhost ~] echo ${NUM:2:-1}
345
VAR=`expression` 或
VAR=$(expression)
# 例如:
[root@localhost ~] HOSTNAME=`hostname`
[root@localhost ~] echo $HOSTNAME
localhost.localdomain
[root@localhost ~] CURNEL_VERSION=$(uname -r)
[root@localhost ~] echo $CURNEL_VERSION
3.10.0-1160.el7.x86_64
read [options] 变量名
options:
-p 定义提示用户的信息
-n 定义字符数(限制变量长度)
-s 不显示(不显示用户输入的内容),比如在输入密码时不会显示
-t 定义超时时间 单位:s
# 例如
[root@localhost ~] read -p "请输入姓名:" NAME
请输入姓名:kunkun
[root@localhost ~] echo $NAME
kunkun
read FILE_CONTENT < FILE_NAME
# 例如:
mgh@ubuntu:~/shell_script$ read -p "请输入IP所在文件:" IP_FILE
请输入IP所在文件:ip.txt
mgh@ubuntu:~/shell_script$ read IP < $IP_FILE
mgh@ubuntu:~/shell_script$ echo $IP
192.168.226.128
declare options VAR=VAL
options:
-i 定义整型 declare -i NUM=123
-r 定义只读变量 declare -r STR="hello"
-a 定义普通数组;查看普通数组
-A 定义关联数组;查看关联数组
-x 将环境变量导出 declare -x A=123 ==> export A=123
(一)、本地变量
本地变量:当前用户自定义的变量。当前进程中有效,其它进程及当前进程的子进程无效。
(二)、环境变量
环境变量:当前进程有效,并且能够被子进程调用
(三)、全局变量
全局变量:全局所有用户和程序都能调用,且继承,新建的用户也默认能调用
解读相关配置文件:
文件名 | 说明 | 备注 |
---|---|---|
$HOME/.bashrc | 当前用户的bash信息,用户登录时读取 | 定义别名、umask、函数等 |
$HOME/.bash_profile | 当前用户的环境变量,用户登录时读取 | |
$HOME/.bash_logout | 当前用户退出当前shell时最后读取 | 定义用户退出时执行的程序 |
/etc/bashrc | 全局的bash信息,所有用户都生效 | |
/etc/profile | 全局环境变量信息 | 系统和所有用户都生效 |
说明:以上文件修改后,都需要重新source让其生效或者退出重新登录
用户登录系统读取相关文件的顺序:
(四)、系统变量
系统变量(内置bash中变量):shell本身已经固定好的了它的名字和作用
内置变量 | 含义 |
---|---|
$? | 上一条命令执行后返回的状态;状态为0表示正常,非0表示异常或错误 |
$0 | 当前执行的程序或脚本名 |
$# | 脚本后面接的参数的个数 |
$* | 脚本后面所有参数,参数当成一个整体输出,每一个变量参数之间以空格隔开 |
$@ | 脚本后面所有参数,参数是独立的,也是全部输出 |
$1~$9 | 脚本后面的位置参数,$1表示第一个位置的参数,以此类推 |
${10}~${n} | 扩展位置参数,第10个位置变量必须用{}括起来(2位数字以上括起来) |
$$ | 当前所在进程的进程号(当前终端) |
$! | 后台运行的最后一个进程号(当前终端) |
!$ | 调用最后一条命令历史中的参数 |
# $? 可以在执行安装程序命令后,使用$?来查看是否执行成功
mgh@ubuntu:~/shell_script$ echo $?
0
mgh@ubuntu:~/shell_script$ lll
Command 'lll' not found, did you mean:
mgh@ubuntu:~/shell_script$ echo $?
127
# var.sh:
#!/bin/env bash
echo "\$0 = $0"
echo "\$# = $#"
echo "\$* = $*"
echo "\$@ = $@"
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
# ./var.sh sing jump rap basketball
mgh@ubuntu:~/shell_script$ ./var.sh sing jump rap basketball
$0 = ./var.sh # 脚本名称
$# = 4 # 参数个数
$* = sing jump rap basketball
$@ = sing jump rap basketball
$1 = sing
$2 = jump
$3 = rap
dirname:获取一个路径下的目录
basename:获取一个路径下的文件
root@ubuntu:/home/mgh/shell_script# A=/root/Desktop/shell/mem.txt
root@ubuntu:/home/mgh/shell_script# dirname $A
/root/Desktop/shell
root@ubuntu:/home/mgh/shell_script# basename $A
mem.txt
”%“表示从右往左去掉一个/key/
”%%“表示从右往左最大去掉/key/
”#“表示从左往右去掉一个/key/
”##“表示从左往右最大去掉/key/
# 例如
root@ubuntu:/home/mgh/shell_script# url=www.taobao.com
root@ubuntu:/home/mgh/shell_script# echo ${#url} # 获取变量长度
14
root@ubuntu:/home/mgh/shell_script# echo ${url#*.}
taobao.com
root@ubuntu:/home/mgh/shell_script# echo ${url##*.}
com
root@ubuntu:/home/mgh/shell_script# echo ${url%.*}
www.taobao
root@ubuntu:/home/mgh/shell_script# echo ${url%%.*}
www
变量替换:
替换:/ 和 //
# 例如
root@ubuntu:/home/mgh/shell_script# url=www.taobao.com
root@ubuntu:/home/mgh/shell_script# echo ${url/o/i}
www.taibao.com
root@ubuntu:/home/mgh/shell_script# echo ${url//o/i}
www.taibai.cim
算术运算:默认情况下,shell就只能支持简单的整数运算
运算内容:加(+)、减(-)、乘(*)、除(/)、求余(%)、乘方(**)
表达式 | 举例 |
---|---|
$(( )) | echo $((1+1)) |
$[ ] | echo $[10-5] |
expr | expr 10 / 5 运算符两边必须加空格,*需要转义\* |
let | n=1;let n+=1;let n++ |
语法:
格式1:test 条件表达式
格式2:[ 条件表达式 ] # 中括号两边需要有空格
格式3:[[ 条件表达式 ]] 支持正则 # 中括号两边需要有空格
判断文件类型:
判断参数 | 含义 |
---|---|
-e | 判断文件是否存在(任何类型文件) |
-f | 判断文件是否存在且是一个普通文件 |
-d | 判断文件是否存在且是一个目录 |
-s | 判断文件是否存在且是一个非空文件 |
mgh@ubuntu:~/shell_script$ touch hello.c
mgh@ubuntu:~/shell_script$ test -f hello.c # 判断文件是否是普通文件
mgh@ubuntu:~/shell_script$ echo $? # 获取上一条命令的返回状态 0为正常
0
mgh@ubuntu:~/shell_script$ [ -f hello.c ]
mgh@ubuntu:~/shell_script$ echo $?
0
判断整数:
判断参数 | 含义 |
---|---|
-eq | 相等 equal |
-ne | 不相等 not equal |
-gt | 大于 greater than |
-lt | 小于 less than |
-ge | 大于等于 greater equal |
-le | 小于等于 less equal |
判断字符串:
判断参数 | 含义 |
---|---|
-z | 判断字符串是否为空 |
-n | 判断是否为非空字符串 |
string1 = string2 | 判断字符串是否相等 ,两边需要加空格 |
string1 != string2 | 判断字符串是否不相等,两边需要加空格 |
mgh@ubuntu:~/shell_script$ STR="hello"
mgh@ubuntu:~/shell_script$ test "$STR"="hello";echo $?
0
类C风格判断:
mgh@ubuntu:~/shell_script$ ((1==1));echo $?
0
mgh@ubuntu:~/shell_script$ ((1>2));echo $?
1
F:表示false,为假
T:表示true,为真
if [ condition ];then
command
command
fi
if [[ condition ]];then
command
command
fi
if test condition;then
command
command
fi
[ condition ] && command
if … else
if [ condition ];then
command1
else
command2
fi
[ condition ] && command1 || command2
if … else if …else
if [ condition1 ];then
command1
elif [ condition2 ];then
command2
...
else
command3
fi
判断两台主机是否ping的通
mgh@ubuntu:~/shell_script$ ping -c1 www.baidu.com # -c 参数表示次数
PING www.a.shifen.com (112.80.248.76) 56(84) bytes of data.
64 比特,来自 112.80.248.76 (112.80.248.76): icmp_seq=1 ttl=128 时间=11.1 毫秒
--- www.a.shifen.com ping 统计 ---
已发送 1 个包, 已接收 1 个包, 0% 包丢失, 耗时 0 毫秒
rtt min/avg/max/mdev = 11.063/11.063/11.063/0.000 ms
mgh@ubuntu:~/shell_script$ echo $?
0
# 代码:ping_test.sh
#!/bin/env bash
if [ -z $1 ];then # -z 判断字符串是否为空
echo "error, please input ip or host"
else
ping -c1 $1 > /dev/null # 将输出结果重定向到黑洞中,也就是不显示结果
if [ $? -eq 0 ];then
echo "ping $1 ok"
else
echo "ping $1 fail"
fi
fi
# 执行结果
mgh@ubuntu:~/shell_script$ ./ping_test.sh
error, please input ip or host
mgh@ubuntu:~/shell_script$ ./ping_test.sh www.baidu.com
ping www.baidu.com ok
判断进程是否存在
ps -ef | grep docker | grep -v "grep" # -v 参数 过滤字段
代码:
#!/bin/env bash
if [ -z $1 ];then
echo "error, please input process name"
else
ps -ef | grep $1 | grep -v "grep" | grep -v $0 > /dev/null # 要排除grep进程和当前shell进程
[ $? -eq 0 ] && echo "$1进程存在" || echo "$1进程不存在"
fi
(1)、列表循环
语法格式:
for variable in {list}
do
command
command
...
done
或者
for variable in a b c
do
command
command
...
done
# 例如:for_test.sh
#!/bin/env bash
for i in {1..5}
do
echo "i=$i"
done
# 执行结果:
mgh@ubuntu:~/shell_script$ ./for_test.sh
i=1
i=2
i=3
i=4
i=5
{1..10} 表示从1到10
{1..100..2} 表示1 3 5 7 9... ,2表示步长
seq 可以生成序列
# 例如
mgh@ubuntu:~/shell_script$ seq 3
1
2
3
mgh@ubuntu:~/shell_script$ for i in $(seq 5);do echo $i;done
1
2
3
4
5
(二)、不带列表循环
执行时由用户指定,也就是执行脚本时带的参数,有多少个参数执行多少次
for variable
do
command
command
...
done
# 例如 for_test2.sh
#!/bin/bash
for i
do
echo "hello,world"
done
# 执行结果
mgh@ubuntu:~/shell_script$ ./for_test2.sh a b c
hello,world
hello,world
hello,world
(三)、类C风格
for((expr1;expr2;expr3))
do
command
command
...
done
# 例如
for((i=0;i<5;i++))
do
echo $i
done
while expression
do
command
...
done
# 例如:
while [ 1 -eq 1 ] 或者 ((1==1))
do
command
command
done
案例:脚本同步系统时间
需求:
思路:
代码
#!/bin/bash
NTP_SERVER=203.107.6.88
count=0
while true
do
ntpdate $NTP_SERVER > /dev/null
if [ $? -ne 0 ];then # 执行失败
echo "system update time failed" | mail -s "check system time " root@localhost
else
let count++
if [ $[$count%100] -eq 0 ];then
echo "system update time failed" | mail -s "check system time " root@localhost
fi
fi
sleep 30
done
exit 退出整个程序
break 结束当前循环
continue 忽略本次循环的代码,直接进入下一次循环
shift 脚本输入的参数向左移一位 也可以移多位:shift 2
系统变量:RANDOM,默认会产生0~32767的随机整数
mgh@ubuntu:~/shell_script$ echo $RANDOM
14073
mgh@ubuntu:~/shell_script$ echo $RANDOM
8299
mgh@ubuntu:~/shell_script$ echo $RANDOM
31547
# 产生0~100的随机数
mgh@ubuntu:~/shell_script$ echo $[$RANDOM%101]
77
# 产生10~100的随机数
mgh@ubuntu:~/shell_script$ echo $[$RANDOM%91+10]
93
需求:
写一个脚本,生成一个phonenum.txt文件,随机产生139开头的手机号1000个,每行一个
代码1:
#!/bin/env bash
FILE_PATH="phone.txt"
for i in {1..1000}
do
let phonenum=139$[$RANDOM%9000+1000]$[$RANDOM%9000+1000] # $[$RANDOM%9000+1000]产生1000~9999
echo $phonenum >> $FILE_PATH
done
代码2:
#!/bin/env bash
FILE_PATH="phone.txt"
for i in {1..1000}
do
phonenum=139
for i in {1..8}
do
let n=$[$RANDOM%10] # 一次生成一位,最后拼接
let phonenum=$phonenum$n
done
echo $phonenum >> $FILE_PATH
done
需求:
在上面的1000个手机号码里抽取5个号码作为幸运观众,显示这五个幸运观众
但只显示头3位和尾4位,中间的用*代替
代码:
#!/bin/env bash
# 生成5个0~1000的随机数,数子对应的行的电话就是幸运观众的号码
FILE_PATH="phone.txt"
for i in {1..5}
do
line=`wc -l $FILE_PATH | cut -d ' ' -f1` # 获取文件行数
let luck_line=$[$RANDOM%$line+1]
luck_num=`head -$luck_line $FILE_PATH | tail -1`
echo "第$i位幸运观众的号码:"
echo "139****${luck_num:7:4}"
sed -i "/$luck_num/d" $FILE_PATH # 从文件中删除该行
done
数组分类:
arr[index]=value
# 例如
arr[0]=v1
arr[1]=v2
arr=(val1 val2 val3 ...)
# 例如
namearr=(zhangsan lisi wangwu)
# 将命令的执行结果赋给数组
files=(`ls /root`)
${arr[index]}
echo ${arr[0]} 获取第一个元素
echo ${arr[*]} 获取所有元素
echo ${#arr[*]} 获取所有元素个数
echo ${!arr[@]} 获取元素的索引下标
declare -a 定义普通数组或查看所有普通数组
declare -A 定义关联数组查看所有关联数组
# 需要先声明才能使用
declare -A arr
arr[key]=val
# 例如
inf[name]=zhangsan
inf[age]=lisi
arr=([name]=zhangsan [age]=18 [addr]=hangzhou)
语法结构:
case var in
pattern 1) # 模式1,用|分割多个模式,相当于or
command1 # 执行的语句
;; # 两个分号表示命令结束
pattern 2)
command2
;;
pattern 3)
command3
;;
*) # default,默认执行
command4
;;
esac # 语句结束
案例:给脚本传不同的参数,做不同的事
#!/bin/env bash
case $1 in
start|S)
echo "server is running"
;;
stop|T)
echo "server is stopping"
;;
reload|R)
echo "server is reloading"
;;
esac
# 方法一
函数名()
{
函数体
}
# 方法二
function 函数名()
{
函数体
}
# 定义函数文件 func1.sh
hello()
{
echo "hello,world"
}
# 在终端中执行
source func1.sh
hello
# 在脚本文件中调用本文件定义的函数,直接使用函数名
hello
# 在脚本中调用其它文件定义的函数,也需要先source
source /etc/func.sh
hello
# 函数传参,直接跟在函数后
hello 1 2 3
# 函数中使用 $1 $2 ...来使用参数