什么是shell脚本,其实,shell脚本就是利用shell的功能所写的一个程序,这个程序是使用纯文本文件,将一些shell的语法与命令(包含外部命令)写在里面,搭配正则表达式、管道命令与数据流重定向等功能等这些命令的组合起来,以达到我们所想要的目的。
程序编程风格有两种:
过程式:以指令为中心,数据服务于指令。
对象式:以数据为中心,指令服务于数据。
过程式编程有 顺序执行、循环执行、选择执行。
shel程序就是过程式式的解释器,提供编程能力,然后解释执行。
shell脚本编写要注意一下事项:
1、命令的执行时由上而下、从左到右执行的。
2、命令、参数间的多个空白会被忽略。
3、如果读取到空白符(Enter),就尝试执行该命令。
4、如果一行内容太多可以用\[Enter]换行编写
5、"#"作为注释,任何加在#后面的数据将会被忽略为注释。
创建脚本
1、第一行必须用#!/bin/bash来声明这个脚本的shell名称
2、第二行一下可以用#来注释shell脚本的内容、功能、信息版本和作者和联系方式、建立日期、历史记录等良好习惯。有助于将来的拍错。 好记性不如烂笔头!
3、建议将一些重要的环境变量设置好,如此一来,就可以直接使用外部命令,而不必写绝对路径。
4、然后就将主要的程序写好输出
可以下用下面的命令来校验脚本
bash -n /path/to/some_script
检测脚本中的语法错误
bash -x /path/to/some_script
调试执行
bash变量
那么什么变量呢?额,简单的说变量就是用一个简单特定的字符来代替另一个比较复杂或者是容易变动的数据。
变量类型
变量类型的作用有
1、数据存储格式
2、参与的运算
3、表示的数据范围
变量的类型有:字符型、数值型。 数值型分为:整型和浮点型
变量对于编程语言来讲分为强类型和弱类型
强类型:定义变量时必须指定类型、参与运算必须符合类型要求;调用未声明变量会产生错误
弱类型:无须指定类型,默认均为字符型;参与运算会自动进行隐式类型转换;变量无须事先定义可直接调用。 shell为弱类型
变量命名法则:
1、不能使程序中的保留字:例如if, for;
2、只能使用数字、字母及下划线,且不能以数字开头
3、见名知义
4、统一命名规则:驼峰命名法
变量与变量的内容以一个等号"="来连接
例如 first_Name=Alan
等号两边不能直接接空格符。若使用空格符可以用双引号即弱引用( " )或单引号即强引用( ' )将变量结合起来
当时双引号内的特殊符号如"$",可以保留原本的特性
var="lang is $LANG" 则echo显示为"lang is en_US.UTF-8
单引号内的特殊符号仅为纯文本字符如
var='lang is $LANG' 则echo显示为"lang is $LANG"
可用转义字符"\"将特殊符号变成一般符号如($、\、空格符、!等)
根据变量的生效范围可分为
本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
环境变量:生效范围为当前shell进程及其子进程
局部变量:生效范围为当前shell进程中某代码片断(通常指函数)
位置变量:$1, $2, …来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数
特殊变量:$?, $0, $*, $@, $#
本地变量赋值:name=‘value’,
可以使用引用value:
(1) 可以是直接字串; name=“root"
(2) 变量引用:name="$USER"
(3) 命令引用:name=`COMMAND`, name=$(COMMAND)
变量引用:${name}, $name 也可以累加
例如: PATH=$PATH:~/bin: 累加~/bin到原有的PATH变量中
[root@localhost bin]# echo $PATH
/bin:/sbin:/usr/bin:/usr/sbin:usr/local/bin:/usr/local/sbin:/root/bin:/root/bin:
"":弱引用,其中的变量引用会被替换为变量值
'':强引用,其中的变量引用不会被替换为变量值,而保持原字符串
单引号和双引号最大不同之处在于双引号仍然可以保有变量的内容,但单引号内仅能是一般字符,而不会有特殊符号。
显示已定义的所有变量(包括环境变量):set
删除变量:unset name
环境变量
变量声明、赋值:
export name=VALUE
export也可以把本地变量转换环境变量
declare -x name=VALUE
变量引用:$name, ${name}
显示所有环境变量:
export
env
printenv
删除:unset name
bash有许多内建的环境变量:PATH, SHELL, HOME等用大写字母来定义环境变量。
只读变量:只能声时,但不能修改和删除
readonlyname
declare -r name
位置变量:在脚本代码中调用通过命令行传递给脚本的参数
$1, $2, …:对应第1、第2等参数,shift [n]换位置
$0: 命令本身
$*: 传递给脚本的所有参数,全部参数合为一个字符串
$@: 传递给脚本的所有参数,每个参数为独立字符串
$#: 传递给脚本的参数的个数
$@ $* 只在被双引号包起来的时候才会有差异
bash中的算术运算:help let
+, -, *, /, %取模(取余), **(乘方)
实现算术运算:
(1) let var=算术表达式
(2) var=$[算术表达式]
(3) var=$((算术表达式))
(4) var=$(expr arg1 arg2 arg3 …)
(5) declare –ivar= 数值
(6) echo ‘算术表达式’ | bc
增强型赋值:
+=, -=, *=, /=, %=
let varOPERvalue
例如:let count+=3
自加3后自赋值
自增,自减:
let var+=1
let var++
let var-=1
let var–
逻辑运算又称布尔运算 布尔用数学方法研究逻辑问题,成功地建立了逻辑演算。他用等式表示判断,把推理看作等式的变换。
这种变换的有效性不依赖人们对符号的解释,只依赖于符号的组合规律 。 true作1 false作0 与运算用& 或运算用||
与运算:
1 && 1 = 1 真
1 && 0 = 0 假
0 && 1 = 0 假
0 && 0 = 0 假
或运算:
1 || 1 = 1 真
1 || 0 = 1 真
0 || 0 = 1 真
0 || 0 = 0 假
非:!
! 1 = 0
! 0 = 1
短路运算:
短路与:
第一个为0,结果必定为0;
第一个为1,第二个必须要参与运算;
短路或:
第一个为1,结果必定为1;
第一个为0,第二个必须要参与运算;
异或:^
异或的两个值,相同为假,不同为真
进程使用退出状态来报告成功或失败
0 代表成功,1-255代表失败
$? 变量保存最近的命令退出状态
例如:
ping-c1-W1hostdown&>/dev/null
echo$?
bash自定义退出状态码
exit [n]:自定义退出状态码;
注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
判断某需求是否满足,需要由测试机制来实现;
专用的测试表达式需要由测试命令辅助完成测试过程;
评估布尔声明,以便用在条件性执行中
•若真,则返回0
•若假,则返回1
测试命令:
•test EXPRESSION
•[ EXPRESSION ]
•[[ EXPRESSION ]]
注意:EXPRESSION前后必须有空白字符
根据退出状态而定,命令可以有条件地运行
&& 代表条件性的AND THEN
|| 代表条件性的OR ELSE
例如:ls /dev && echo "This is device file"
ls /tmp/cpwd|| mkdir /tmp/cpwd
ls /dev/abc && echo "exist" || echo "no exist"
test命令是shell环境中测试条件表达式的实用工具
-b<文件>:如果文件为一个块特殊文件,则为真;
-c<文件>:如果文件为一个字符特殊文件,则为真;
-d<文件>:如果文件为一个目录,则为真;(常用)
-e<文件>:如果文件存在,则为真;(常用)
-f<文件>:如果文件为一个普通文件,则为真;(常用)
-g<文件>:如果设置了文件的SGID位,则为真;
-G<文件>:如果文件存在且归该组所有,则为真;
-k<文件>:如果设置了文件的粘着位,则为真;
-O<文件>:如果文件存在并且归该用户所有,则为真;
-p<文件>:如果文件为一个命名管道,则为真;
-r<文件>:如果文件可读,则为真;
-s<文件>:如果文件的长度不为零,则为真;
-S<文件>:如果文件为一个套接字特殊文件,则为真;
-u<文件>:如果设置了文件的SUID位,则为真;
-w<文件>:如果文件可写,则为真;
-x<文件>:如果文件可执行,则为真。
两种文件之间的比较 如 test file1 -nt file2
-nt file1 -nt file2 判断file1是否比file2新(newer than)
-ot file1 -ot file2 判断file1是否比file2旧(older than)
-er file1 -er file2 判断file1和file2为同一文件 (inode相同的文件)
两个整数之间的判断,例如 test file -eq file1
-eq 两数值相等(equal)
-ne 两数值不相等(not equal)
-gt file1大于file2(greater than)
-lt file1小于fiel2(less than)
-ge file1大于等于file2(greater than or equal)
-le file1小于等于file2(less than or equal)
判断字符串的数据
字符串测试:
==:是否等于;
>: ascii码是否大于ascii码
<: 是否小于
!=: 是否不等于
=~: 左侧字符串是否能够被右侧的PATTERN所匹配
注意: 此表达式一般用于[[ ]]中;
test -z string 判断字符串是否为0,若string为空字符串,则为真
test -n string 判断字符串是否为非0,若string为空字符串,则为假
组合测试条件
第一种方式:
COMMAND1 && COMMAND2 并且
COMMAND1 || COMMAND2 或者
! COMMAND 非
如:[ -e FILE ] && [ -r FILE ]
第二种方式:
EXPRESSION1 -a EXPRESSION2 并且
EXPRESSION1 -o EXPRESSION2 或者
! EXPRESSION
必须使用测试命令进行;
[ -z “$HOSTNAME” -o $HOSTNAME "=="localhost.localdomain" ] && hsotname.alan
[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
read命令从键盘读取变量的值,通常用在shell脚本中与用户进行交互的场合。该命令可以一次读取多个变量的值,变量和输入的值都需要使用空格隔开。在read命令后面,如果没有指定变量名,读取的数据将被自动赋值给特定的变量REPLY
-p:指定读取值时的提示符; -t:指定读取值时等待的时间(秒)。
read -p “Enter a filename:“ FILE
作业
1、编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。
[root@localhost work]# cat systeminfo.sh #!/bin/bash # #编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息, 包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。 I_p=$(ifconfig|grep -A 1 '^e.*[^[:space:]]\+'|tr -s ' '|cut -d' ' -f3|tr -sc [[:digit:].] ' ') C_pu=$(lscpu|grep 'Model name'|tr -s ' '|cut -d' ' -f3-) M_em=$(grep 'MemTotal' /proc/meminfo) H_ard=$(fdisk -l|grep '/dev/sda\>'|tr -s ' '|cut -d' ' -f3-4) echo -e "hostname:\033[31m ${hostname} \033[0m" echo -e "ipaddress is:\033[31m${I_p} \033[0m" echo -e "OS is:\033[31m$(cat /etc/redhat-release)\033[0m" echo -e "kernet is:\033[31m$(uname -r)\033[0m" echo -e "CPU tpye is:\033[31m${C_pu}\033[0m" echo -e "Mem size is\033[31m${M_em}\033[0m" echo -e "hardwqre size is:\033[31m${H_ard}\033[0m" [root@localhost work]# ./systeminfo.sh hostname: ipaddress is: 10.1.36.6 192.168.200.6 OS is:CentOS release 6.8 (Final) kernet is:2.6.32-642.el6.x86_64 CPU tpye is:Intel(R) Celeron(R) CPU N2940 @ 1.83GHz Mem size isMemTotal: 1906276 kB hardwqre size is:214.7 GB
2、编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中
#!/bin/bash #编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中 back_up=$(cp -r /etc /root/etc`date +%F`) echo -e "backup /etc directory:\033[31m${back_up}\033[0m" [root@localhost work]# ./backup.sh #脚本执行 backup /etc directory: [root@localhost work]# ll -d /etc/ /root/etc2016-08-14 #查看备份是否成功 drwxr-xr-x. 102 root root 12288 Aug 16 13:33 /etc/ drwxr-xr-x 102 root root 12288 Aug 14 19:36 /root/etc2016-08-14
3、编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值
[root@localhost work]# cat disk.sh #!/bin/bash # #编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值 disk_d=$(df |tr -s ' '|cut -d" " -f5|tr -d "%"|sort -n|tail -1) echo "disk:${disk_d}" [root@localhost work]# ./disk.sh #脚本执行 disk:22
4、编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序
[root@localhost work]# cat link.sh #!/bin/bash #编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序 L_ink=$(netstat -nt|tr -s ":" ' '|cut -d" " -f4|grep '[[:digit:].]'|sort -nr|uniq -c) echo "ipddress is:${L_ink}" [root@localhost work]# ./link.sh #脚本执行 ipddress is: 2 192.168.200.6
5、写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的ID之和
[root@localhost work]# cat sumid.sh #!/bin/bash #写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的ID之和 u_id1=$(head -10 /etc/passwd|tail -1|cut -d: -f3) u_id2=$(head -20 /etc/passwd|tail -1|cut -d: -f3) let sum=$u_id1+$u_id2 echo "$sum" [root@localhost work]# ./sumid.sh #脚本执行结果 42
6、写一个脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和
[root@localhost work]# cat sumspace.sh #!/bin/bash #写一个脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和 space1=$(grep "^[[:space:]]*$" $1 |wc -l) space2=$(grep "^[[:space:]]*$" $2 |wc -l) let space=$space1+$space2 echo "$space" [root@localhost work]# ./sumspace.sh /etc/fstab /etc/rc.d/init.d/functions#执行脚本 107
7、写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件
[root@localhost work]# cat sumfile.sh #!/bin/bash #写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件 one=`ls -A /etc|wc -l` two=`ls -A /var|wc -l` three=`ls -A /usr|wc -l` let all=$one+$two+$three echo "all line is$all" [root@localhost work]# ./sumfile.sh #脚本执行 all line is264
7、写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
[root@localhost work]# cat argsnum.sh #!/bin/bash #写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1, #则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1, #则显示第一个参数所指向的文件中的空白行数 rgum=`[ $# -le 1 ] && echo "At least one path" && exit 0 || grep '^[[:space:]]*$' $1 |wc -l` echo "$rgum" [root@localhost work]# sh argsnum.sh /etc/fstab #一个参数 At least one path [root@localhost work]# sh argsnum.sh /etc/fstab /etc/passwd #2个参数 1
原创文章,作者:ladsdm,如若转载,请注明出处:http://www.178linux.com/36561