Shell脚本——提前设计可执行语句,用来完成特定任务的文件。(命令的堆积)
——解释型程序;顺序、批量执行
常见脚本语言:Bash Shell/Python/Perl/Ruby/JSP/PHP/ASP/CGI/JavaScript……
规范Shell脚本的一般组成 :
#!环境声明 //使用哪种解释器,作者信息等。sha-bang调用标记
#注释文本 //一般脚本超过10行,默认要进行注释(注释信息可包括步骤、思路、用途、变量含义等)
可执行代码
脚本的创建过程
Ø 1)明确任务需求:按自然语言拆分小步骤;按顺序整理好(先做什么,后做什么)
Ø 2)编写代码文件:每一个步骤怎么实现;转换成命令行保存到脚本文件
Ø 3)测试并完善:运行脚本,并根据结果排除错误;代码优化、用户友好处理
Shell脚本的执行方式:
方法一:作为“命令字”:指定脚本文件的路径,前提是有x权限
需要权限x(chmod赋予);chmod +x 脚本文件路径
绝对路径:#/脚本文件路径
相对路径:#cd 脚本文件路径 #./脚本文件
方法二:作为“参数”:不需要脚本文件有x权限:
会启动子进程(HISTSIZE=2):bash 脚本文件路径 或sh 脚本文件路径
不会启动子进程:. 脚本文件路径 或 source 脚本文件路径
调试Shell脚本,主要途径:直接观察执行中的输出、报错信息
通过sh -x开启调试模式
在可能出错的地方设置echo
###########################################################################
示例:编写一个面世问候 /root/helloworld.sh 脚本
1.编写脚本代码 # vim /root/helloworld.sh
echo “Hello World!!” //可执行代码
# ls -l /root/helloworld.sh
-rw-r--r--. 1 root root 76 8月 29 10:45 /root/helloworld.sh
2.添加x执行权限 # chmod a+x 1.sh #赋予所有人执行权限
# ls -l /root/helloworld.sh
-rwxr-xr-x. 1 root root 76 8月 29 10:45 /root/helloworld.sh
3.运行脚本测试 # /root/helloworld.sh
如果是在当前路径运行此文件程序 # ./helloworld.sh ###########################################################################
示例:依次输出以下系统信息(红帽系统版本、内核版本、当前主机名)
编写脚本代码: #vim /root/sysinfo.sh
#!/bin/bash
cat /etc/redhat-release
uname -r
hostname
脚本运行及调试:(所有人)添加执行权限 #chmod +x /root/sysinfo.sh
独立运行 #/root/sysinfo.sh
通过sh执行脚本代码,结合-x选项,可以调试脚本代码(此操作不需要再单独对文件添加执行权限) #sh -x /root/sysinfo.sh
###############################################################################
示例:快速为新装的客户机配置YUM仓库;快速为新装的客户机搭好vsftpd服务
#!/bin/bash
echo "准备配置yum源..."
rm -rf /etc/yum.repos.d/*.repo //清理配置目录
echo "
[rh7dvd]
name=rh7dvd
baseurl=http://192.168.4.254/rh7dvd/
enabled=1
gpgcheck=0
" > /etc/yum.repos.d/rh7dvd.repo //新建仓库配置文件
echo "配置完成。软件包数量如下:"
yum repolist | tail -1
echo "准备安装软件..."
yum -y install vsftpd &> /dev/null //装包,忽略输出
systemctl start vsftpd &> /dev/null
systemctl enable vsftpd > /dev/null
echo "安装完成"
echo "启动服务完成"
###############################################################################
使用变量,增加脚本的适应环境的能力
变量:以固定的名称存放的可能会变化的值 可用生活中的容器(杯子)来比喻,用固定的字符存储变化的数据。
Ø 方便以固定名称重复使用某个值
Ø 提高对任务需求、运行环境变化的适应能力
定义/赋值变量
Ø 变量名=变量值
设置变量时的注意事项
– 若指定的变量名已存在,相当于为此变量重新赋值
– 变量名相同时候赋值的生效
– 等号两边不要有空格
– 变量名由字母/数字/下划线组成,区分大小写
– 变量名不能以数字开头,不要使用关键字和特殊字符
查看/引用变量
Ø 引用变量值:$变量名
Ø 查看变量值:echo $变量名、 echo ${变量名}
未定义的变量为空值 以{}界定易混淆名称,一般用于嵌套
a=11, $ab为空,调用要使用${a}b
echo $USER 输出用户名
取消变量
Ø 退出定义变量的Shell环境时,变量会自动失效
Ø 手动取消:unset 变量名…
/dev/null是Linux中专门用来存放,不要的数据或输出信息的设备。
SHELL变量--shell的变量是弱类型的变量
###############################################################################
示例:创建用户脚本,用户名利用变量user实现
1)编写脚本代码# vim /root/user.sh
#!/bin/bash
read -p '请输入您要创建的用户名:' user #为user变量赋值
read -p '请输入您要设置的密码:' pass #为pass变量赋值
useradd=$user #调用user变量的值
echo 用户$user创建成功
echo $pass |passwd --stdin $user &> /dev/null #调用pass变量的值
echo 用户$user密码设置成功
2)添加x执行权限 # chmod +x /root/user.sh
3)测试 /root/user.sh 脚本
###############################################################################
变量的种类
根据存储类型分类
Ø 整数型、浮点型、双精度浮点型、字符型……
Ø Shell脚本语言对存储类型要求较松散
根据变量的用途不同区分
类型 |
说明 |
环境变量 |
变量名一般都大写,由系统维护,用来设置用户/系统环境,只有个别变量用户可以直接更改 |
位置变量 |
bash内置,存储执行脚本时提供的命令行参数 |
预定义变量 |
bash内置,一类有特殊用途的变量,可直接调用,但不能直接赋值或修改 |
自定义变量 |
由用户自主设置、修改及使用 |
环境变量——写在profile的变量
配置文件:/etc/profile、~/.bash_profile
相关操作:env:列出所有的环境变量
set:列出所有变量
常见的环境变量:PWD(当前工作目录变量)、PATH(命令搜索路径变量)、USER(用户名变量)、LOGNAME、UID(用户ID号);SHELL(当前使用的shell变量)、HOME(用户家目录变量)、PS1(命令提示符)、PS2……
PATH变量应用:在Linux执行大多数命令时,都要去PATH变量值当中去寻找该命令的程序文件,能够找到就执行,不能找到就告诉你找不到该命令
+++++++++++++++++++++++++++++++++++++++++++++
把自己编写的脚本,设置成执行的时候,像执行系统命令一样执行。
#echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
#mkdir /root/bin
#cp /shell/checkip.sh /root/bin/checkip
#chmod +x /root/bin/checkip
#checkip 172.40.50.117
或用软链接方式将脚本放入PATH路径下 ln -s ./a root/bin/a
+++++++++++++++++
题:执行一条命令出现“命令未找到”的原因?
1、输入有误; 2、该命令未安装; 3、该命令没有在PATH路径下。
位置变量
使用位置变量可以取得在执行脚本时提供的命令行参数:
表示为 $n,n为序号 $1、$2、.. .. ${10}、${11}、.. ..
用于免交互传递值(适用于脚本熟练者)
示例:# vim 2.sh
#!/bin/bash
head -$1 $2
cat -n $2 | head -$1
[root@server0 ~]# chmod +x 2.sh
[root@server0 ~]# ./2.sh 3 /etc/passwd #输出/etc/passwd前3行,并标示行号
快速添加用户,并设置好登录密码
#!/bin/bash
echo "一共提供了$#个参数"
useradd=$1 2> /tmp/err/log
echo 用户$1创建成功
echo $2 |passwd --stdin $1 &> /dev/null
echo 用户$1密码设置成功
# /root/user.sh nsd01 123456
或
#!/bain/bash
for i in {1..10}
do
useradd user$i 2>>user.log
echo "123456" |passwd --stdin user$i > /dev/null
done
问题:已存在用户不修改密码,使用if判断
#chmod +x test1.sh
++++++++++++++++++++++++
预定义变量:用来保存脚本程序的执行信息
直接使用这些变量;不能直接为这些变量赋值
变量名 |
含义 |
$0 |
当前所在的进程或脚本名 |
$? |
查看上一个命令退出后的状态值,0表示正常,其他值异常 |
$# |
统计已加载的位置变量的个数 |
$* |
输出所有位置变量的值 |
$$ |
返回当前进程的PID号 |
$($#-1):倒数第二个参数
例1:# cat /etc/redhat-release
# echo $? #判断上一条命令,是否成功执行,
0代表上一个命令执行成功,非0代表上一个命令执行失败
#vim test4.sh
#!/bin/bash
echo $0 显示脚本的名称
echo $1 显示第一个参数
echo $2 显示第二个参数
echo $3 显示第三个参数
echo $* 显示所有参数
echo $# 统计参数有多少个
# chmod +x test4.sh
# ./test4.sh a b c 1 8 7
###############################################################################
扩展赋值操作
区分三种定界符
定界符 |
作用 |
|
双引号“” |
允许扩展,以$引用其他变量,引用整体,支持特殊符号(整体中有调用参数时不影响调用结果) |
echo a b c != echo "a b c" echo "abc" == echo abc |
单引号‘’ |
禁用扩展,即便$也视为普通字符, 引用整体,屏蔽特殊含义,原样输出,不用于调取参数 |
echo 'a b c' == echo "a b c" #echo "\$a" == echo '$a' echo '13$a' != echo "13$a" |
反撇号`` |
引用命令,提取命令的执行结果,只在双引号下使用才能显示其含义
|
每周五,把/var/log备份一次 crontab -e 01 03 * * 5 tar -czf log-`date +%Y%m%d`.tar.gz /var/log |
$() |
与反撇号``等效,但$()更方便嵌套使用,将命令的输出结果作为参数
|
|
read 产生交互(降低脚本的使用难度),写脚本时定义变量不赋值,运行脚本时,从键盘读入用户输入的变量值,完成赋值
格式:read [-p '屏幕输出的提示信息'] 赋值的变量名 -p可选,-t可指定超时秒数
终端显示控制:stty -echo:关闭终端输出(无显示)
stty echo:恢复终端输出(显示)
修改制定用户的密码,用户名和密码由用户输入
#vim test6.sh //创建一个测试脚本
#!/bin/bash
read -p "请输入用户名:" user //读取用户名
stty -echo //关闭回显
read -p "请输入密码:" pass //读取密码
stty echo //恢复回显
echo "" //恢复回显后补一个空行
useradd $name 2> /tmp/err/log
echo $pass |passwd --stdin $user
+++++++++++++++++++++++++++++
rpm -q httpd || yum -y install httpd > /dev/null
service httpdd start ; chkconfig httpd on
+++++++++++++++++++++++++++++++++++++
变量的作用范围
局部变量:(仅当前有效的变量)
新定义的变量默认只在当前Shell环境中有效
无法在子Shell环境中使用
全局变量:(当前及所有子进程都有效的变量)
全局变量在当前Shell及子Shell环境中均有效
使用export可将局部变量声明为全局变量
export 局部变量名[=变量值]…:为局部变量添加全局属性
export -n 全局变量名…:取消指定变量的全局属性
永久修改全局变量: /etc/profile
示例:
# SCHOOL=”Tarena IT Group.”
# sh //进入子Shell环境
# echo $SCHOOL //无此变量,输出空行
sh-4.2# exit //返回原Shell环境
# export SCHOOL //声明为全局变量
# sh //再次进入子Shell
sh-4.2# echo $SCHOOL //可成功使用该变量
Tarena IT Group.
数值运算
整数运算
四则运算符号: + - * /
取余数运算(求模) num1%num2
计算命令: $[] $(()) expr let bc
$[]算式替换:使用$[]或$(())表达式
格式:$[整数1 运算符 整数2 …]
乘法操作*无需转义,运算符两侧可以无空格
引用变量可省略$符号;计算结果替换表达式本身,可结合echo命令输出。
echo $[ num1 运算符 num2 ]
echo $((num1 运算符 num2))
expr运算工具:计算指定的表达式,并输出结果
格式:expr 整数1 运算符 整数2…
参与运算的整数值与运算操作符之间需要以空格分开,引用变量时必须加$符号。
类型 |
运算符 |
示例(X、Y为整数) |
加法 |
+ |
expr $X + $Y |
减法 |
- |
expr $X - $Y |
乘法 |
\* |
expr $X \* $Y 备注:乘法操作应用\*转义,避免被作为Shell通配符 |
除法 |
/ |
expr $X / $Y |
取余数 |
% |
expr $X % $Y |
取10以内的随机数:# expr $RANDOM % 10 $RANDOM产生随机数
let命令格式:let 变量名++let 变量名-- 结合echo命令查看结果
自加运算 ++ 自己加1之后把计算结果赋值给自己
自减运算 -- 自己减1之后把计算结果赋值给自己
可以自定义自加或自减的步长:+= 、 -=
简写表达式 |
完整表达式 |
示例(x=3) |
i++ |
i=i+1 |
let x++ echo $x |
i-- |
i=i-1 |
let x+=4 echo $x |
i+=2 |
i=i+2 |
|
i-=2 |
i=i-2 |
|
i*=2 |
i=i*2 |
|
i/=2 |
i=i/2 |
|
i%=2 |
i=i%2 |
|
# let y=X+22 # echo $y
浮点数运算 bc ——
交互式:# bc //按quit退出交互计算器
非交互式:将需要运算的表达式通过管道操作交给bc运算。注意,小数位的长度可采用scale=N限制,除此以外也受参与运算的数值的小数位影响。
# echo "scale=2;3/4" | bc
# echo "1+2" | bc
bc支持逻辑运算> < <= == !=...
# echo "1>3" |bc 对是1,错是0
小数运算
整数运算的局限性:
Bash内建机制仅支持整数值运算:expr命令、$[]算式替换不支持小数运算
使用bc实现小数运算
多数Linux系统默认安装此工具:支持高精度的数值运算
直接运行bc可进入交互式运算界面,quit退出
设置scale=n可给小数位
结合管道向bc发送表达式:多个表达式以分号分隔
通过echo命令+管道传递要计算的表达式
小数值的比较
基本用法:echo “数值1 比较符 数值2” |bc
如果表达式成立,则返回的计算结果为1,否则返回0
常见比较操作:>、>=、<、<=、==、!=
x=`echo "1.23 + 2.67" | bc` echo $x
条件测试
Shell脚本的智能化:使Shell脚本获得识别能力?
为命令的执行提供最直接的识别依据
文件或目录的读/写等状态
数值的大小
字符串是否匹配
多条件组合
表达式:值1 符号 值2
Shell测试的依据
命令行/程序的退出状态值$?
Ø 值为0,表示执行成功
Ø 值不为0,还未执行异常或失败
在脚本中自定义退出状态值
Ø 以退出之前最后一条命令的$?作为脚本的退出状态值
Ø 也可以自行替换,添加exit整数
几种可用的测试方式
Ø 正常的命令行
Ø test -选项 参数……
Ø [ 测试表达式 ]
表达式两边至少要留一个空格。条件测试操作本身不显示出任何信息。测试的条件是否成立主要体现在命令执行后的返回状态(即 $?),所以可以在测试后查看变量$?的值来做出判断,或者结合&&、||等逻辑操作显示出结果(或作其他操作) 。
创建一个以当日日期结尾的文件
# mkdir mydir-$(date +%F)
# mkdir $(hostname)-$(date +%F)
# tar -czf log-`date +%Y%m%d`.tar.gz 文件或目录 (按当前日期)打包文件或目录
字符比较 |
操作符 |
含义 |
示例 |
[操作符 字符串] |
-z |
检查变量的值是否未设置(空值) |
[ -z "$var" ] && echo "空值" || echo "非空值" 未对变量赋值,则为空 [ ! -z $var1 ]测试var1是否为非空 |
-n |
字符串的值不为空 (相当于!-z) |
[ -n "$var" ] 判断$var变量是否有值 |
|
[字符串1 操作符 字符串2] |
== |
两个字符串相同 |
[ $USER == 'root' ] echo $? |
!= |
两个字符串不相同(取反) |
|
|
整数值比较 |
操作符 |
含义 |
示例 |
[整数值1 操作符 整数值2]
注:参与比较的必须是整数(可以调用变量),比较非整数值时会出错 |
-eq |
等于(equal) |
[ $X -eq 20 ] && echo "相等" || echo "不相等" |
-ne |
不等于(not equal) |
|
|
-ge |
大于或等于 (greater or equal) |
[ $X -ge 10 ] && echo "大于或等于" || echo "否" |
|
-le |
小于或等于 (less or equal) |
检查登录用户数是否不超过5个:[ $(who | wc -l) -gt 5 ] && echo "超过了" || echo "没超过" [ $X -le 10 ] && echo "小于或等于" || echo "否" |
|
-gt |
大于(greater than) |
[ $X -gt 10 ] && echo "大于" || echo "否" |
|
-lt |
小于(less than) |
[ $X -lt 10 ] && echo "小于" || echo "否" |
|
文件状态测试常用选项 |
操作符 |
含义 |
示例 |
[操作符 文件或目录] |
-e |
判断对象(文件或目录)是否存在(exist),若存在则结果为真 |
[ -e "/usr/src/" ] && echo "存在" || echo "不存在" |
-d |
判断对象是否为目录(directory),是则为真 |
[ -d "/usr/src/" ] && echo "是目录" || echo "不是目录" |
|
-f |
判断对象是否为一般文件(file),是则为真 |
[ -f "/usr/src/" ] && echo "是文件" || echo "不是文件" |
|
-r |
判断对象(文件或目录)是否有可读(Read)权限,是则为真 |
此测试中,无论文件是否设置r/w权限,root都可读;x权限取决于文件本身、文件系统级的控制,root或普通用户都适用。 1)复制一个文件做测试: # cp install.log /tmp/rtest.txt 2)去掉需要测试的权限: # chmod -r|w|x /tmp/rtest.txt 3)测试root与普通用户: # [ -r "/tmp/rtest.txt" ] && echo "可读" || echo "不可读" |
|
-w |
判断对象(文件或目录)是否有可写(Write)权限,是则为真 |
||
-x |
判断对象(文件或目录)是否有可执行(eXcute)权限,是则为真 |
||
-s |
判断文件大小非0时为真 |
|
|
|
-c |
判断文件为字符特殊文件为真 |
|
|
-b |
判断文件为块特殊文件为真 |
|
|
-t |
当文件描述符(默认为1)指定的设备为终端时为真 |
|
需要注意的一些小细节:
1) [ 、] 、==、!= 符号左右都需要有空格
2)引号、分号这些都是英文
3)字符串最好用双引号括起来
#!/bin/bash
login_num=`who |wc -l` #反引号
[ $login_num -gt 5 ]&&mail -s warning root <警告文件路径
制定计划任务
#crontab -e
*/15 * * * * /var/tmp/test8.sh
监控脚本:ps aux 、free、uptime
内存,磁盘,CPU,进程数量
使用uptime查看(相当于top的最上边一行),先查看电脑CPU是几核的,1核的满负载是1,2核的满负载是2
示例:每分钟判断电脑的进程数量,大于X,就报警
#!/bin/bash
num=`ps aux |wc -l`
[ $num -gt 100 ] && mail -s warning root < /var/err/log
chmod +x 4.sh
crontab -e
* * * * * /root/4.sh
示例:判断电脑apache启动了没有,没有启动就启动它
#!/bin/bash
netstat -ntulp |grep httpd &>/dev/null
[ $? -ne 0 ] && systemctl start httpd
[ -d /abc ]是否存在目录 [ ! -d /abc ]是否不存在目录
[ -z ]是否为空 [ ! -z ]是否为非空
示例:判断/media/是否有abc目录,如果没有自动创建并touch一个文件a.txt,有则touch一个文件a.txt
#!/bin/bash
dir=”/media/abc”
[ -d $dir ] || mkdir $dir
touch $dir/a.txt
或 [ ! -d $dir ] && mkdir $dir
touch $dir/a.txt
组合多个条件
主要用法:命令1 操作符 命令2 ……
[条件1] 操作符 [条件2]……
逻辑分隔操作 |
操作符 |
含义 |
示例 |
逻辑与 |
&& |
多个判断条件必须同时成立,结果才为真。 |
[ $UID == root ] && [ -e /etc/passwd ] [ -e "/studir2" ] && echo "ok" |
逻辑或 |
|| |
多个判断条件时某一个判断条件成立,结果为真 |
检查变量X的值是否小于10或者小于30:[[ $X -lt 10 || $X -lt 30 ]] && echo "YES" |
逻辑非 |
! |
取反 |
test -z "" test ! -z "" echo $? |
# A&&B 仅A命令成功,才执行B命令
# A||B 仅A不成功,才执行B命令
# A;B;C 执行A,执行B,执行C,没有逻辑关系
多个条件组合时,可以使用 [[ .. ]] 界定:[[ $X -gt 10 && $X -lt 30 ]] && echo "YES"