【前言】
在Linux的shell环境下,有时候我们需要用使用一些东西来自动的帮我们实现一些繁琐的功能,从而减少我们自身的工作量,这个东西就是脚本。先来认识一下什么是脚本?
脚本语言是为了缩短传统电脑语言编译连结的过程而创建的编程语言。
当然,对于目前阶段的小编来说,编译一个脚本来实现电脑程序还是遥遥不可及的,注意本篇文章标题里的“入门”二字,如果路过的大神们想看点有技术含量的,就不要从本文中找了哈~~
好了,言归正传~接下来正式来接触一下脚本~
脚本的创建方式并非有什么特殊化。上篇文章已经介绍了vim编辑器的使用,写脚本的话直接使用vim来创建就可以了。脚本的特殊化只在于文件里写的内容,并且一个脚本需要拥有执行权限,不然它只是一个普通文件。
【shebang】
首先,敲命令创建文件:vim filename
我们需要首先在文件的开头写一个命令,什么呢?就是这个:
这一行命令有一个统称,叫做”! shebang“ 关于这一符号,我们先来看一下维基百科的解释:
通过维基的解释,我们知道了”shebang“这个符号通常在Unix系统的脚本中第一行开头中写到,它指明了执行这个脚本文件的解释程序。当然,如果我们没有指定shebang或者系统中没有你所指定的shebang时,会出现什么问题?下面总结了来自老师讲解的几种情况:
- 如果脚本文件中没有#!这一行,那么它执行时会默认用当前Shell去解释这个脚本(即:$SHELL环境变量)。
- 如果#!之后的解释程序是一个可执行文件,那么执行这个脚本时,它就会把文件名及其参数一起作为参数传给那个解释程序去执行。
- 如果#!指定的解释程序没有可执行权限,则会报错“bad interpreter: Permission denied”。如果#!指定的解释程序不是一个可执行文件,那么指定的解释程序会被忽略,转而交给当前的SHELL去执行这个脚本。
- 如果#!指定的解释程序不存在,那么会报错“bad interpreter: No such file or directory”。注意:#!之后的解释程序,需要写其绝对路径(如:#!/bin/bash),它是不会自动到$PATH中寻找解释器的。
- 当然,如果你使用”bash test.sh ”这样的命令来执行脚本,那么#!这一行将会被忽略掉,解释器当然是用命令行中显式指定的bash。
【变量】
了解了”shebang“的含义之后,再来介绍一个名词,变量。变量这个词在数学界的含义我们就不做了解,主要了解在计算机程序里它所代表的是什么。变量是指一个未被赋值的空的存储空间,当然一个变量只能存在一个值,当你对它进行第二次赋值时,之前的值将会被覆盖。在Linux中,你可以使用任何字母来定义一个变量,但是对于变量的定义也有一些小小的要求:
1、不能使程序中的保留字:例如if, for
2、只能使用数字、字母及下划线,且不能以数字开头
3、见名知义
4、统一命名规则:驼峰命名法
我们通过一个例子来简单说明一个变量:
由上面的例子我们应该可以很清楚的明白了变量的定义,那么在bash中我们还将变量分为几类,它们分别是本地变量、环境变量、局部变量、位置变量和特殊变量。我们重点来关注本地变量和环境变量。
什么是本地变量?什么是环境变量?在shell中,进程有父进程和子进程,这点首先要知道,如何开一个子进程?我们首先来做一个小实验:
从以上实验看到,我们定义了一个变量,这个变量只在shell下显示,在子进程里不显示值,这种变量称之为本地变量。本地变量的生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效。
环境变量:
由上个实验看出,在变量前加export
,该变量可以在子shell下显示值,这种变量称之为环境变量。环境变量的生效范围为当前shell进程及其子进程。
位置变量:$1, $2, …来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数。下面列出一些先简单坐下了解:
参数 | 注释 |
---|---|
$1, $2, … | 对应第1、第2等参数,shift [n]换位置 |
$0 | 命令本身 |
$* | 传递给脚本的所有参数,全部参数合为一个字符串 |
$@ | 传递给脚本的所有参数,每个参数为独立字符串 |
$# | 传递给脚本的参数的个数(不包含$0) |
$@ $* | 只在被双引号包起来的时候才会有差异 |
set – | 清空所有位置变量 |
特殊变量
参数 | 注释 |
---|---|
$0 | 命令本身 |
$? | 上个命令的退出状态(或函数的返回值) |
$$ | 当前shell进程ID |
$_ | 查看上一个命令的最后一个变量。 |
变量不仅可以赋值,还可以引用;
[root@localhost ~]# C1="root" 直接字符串引用
[root@localhost ~]# echo $C1
root
[root@localhost ~]# C1="$USER" 引用一个变量
[root@localhost ~]# echo $C1
root
[root@localhost ~]# C1=`ls /app/` 引用一个命令的执行结果,注意引用符号
[root@localhost ~]# echo $C1
bao.tar ceshi f1 file1 FILE1 fstab passwd profile.sh test.sh wan
说了这么多,怎么还没开始写脚本,憋着急,先来一个练练手?
练习:编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中。
[root@localhost bin]# vim /root/bin/backup.sh
#!/bin/bash 首行写"shebang"
# ------------------------------------------
# Filename: backup.sh
# Revision: 1.1
# Date: 2017/06/01 其余以#开头的行为注释行,为了使自己也使别人
# Author:wanwanperfect 更加快速的了解脚本的内容
# Description:
# ------------------------------------------
cp -a /etc /root/etc$(date +%F) 脚本需求
[root@localhost bin]# chmod u+x backup.sh 给脚本加上执行权限
[root@localhost bin]# ./backup.sh 执行这个脚本
[root@localhost bin]# cd /root/
[root@localhost ~]# ls
a.log bin Desktop Downloads file.grep motd new.file Public Videos anaconda-ks.cfg b.log Documents etc2017-08-10 initial-setup-ks.cfg Music Pictures Templates 创建成功
由以上的几个实验我们也可以基本的了解了脚本是什么以及脚本的组成和写法,接下来再来看一些更深入的脚本里的弯弯绕绕~~~
算数运算
在bash中,定义一个变量的值也可以使用算数运算,主要有以下几种方式:
(1) let var=算术表达式
(2) var=$[算术表达式]
(3) var=$((算术表达式))
(4) var=$(expr arg1 arg2 arg3 …) 乘法时要转译* ,即*
(5) declare –i var = 数值
(6) echo ‘算术表达式’ | bc (丢给计算机进行处理)
[root@localhost ~]# let a=2+3
[root@localhost ~]# echo $a
5
[root@localhost ~]# b=$[20*12]
[root@localhost ~]# echo $b
240
[root@localhost ~]# c=$((3+3))
[root@localhost ~]# echo $c
6
[root@localhost ~]# d=$(expr 4 \* 5 + 3)
[root@localhost ~]# echo $d
23
[root@localhost ~]# declare -i n=8
[root@localhost ~]# echo $n
8
[root@localhost ~]# echo 4*4 |bc
16
另外除了一些算数运算,变量还有自增赋值和自减赋值:
let var+=1 <表示自加1 后赋值>
let var++
let var-=1 <表示自减1 后赋值>
let var–
又来练习啦~~
编写脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和
#!/bin/bash
#------------------------------------------------------
#Filename:sumspace47.sh
#Revision:
#Date:2017-08-02
#Author:wanwanperfect
#------------------------------------------------------
file1=$(cat $1 |grep -o "^[[:space:]]*$"|wc -l) 定义$1、$2作为参数
file2=$(cat $2|grep -o "^[[:space:]]*$"|wc -l) 使用grep来找到空行
echo "空行之和为:"$[file1+file2]"" 使用算数运算来达到目的
unset file1 file2 别忘记使用unset命令来释放变量,不然会占取你的内存呦~
【逻辑运算】
在逻辑运算中,我们只定义两个值,即 true 和 false ,分别用 1 和 0 表示。
逻辑运算分为三种:
与:只要有一个假则为假
1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
或:只要有一个真则为真
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
! 1 = 0
! 0 = 1
短路与:前一个为假,则不再判断第二个值。 用 && 表示
短路或:前一个为真,则不再判断第二个值。 用 || 表示。
异或:异或的两个值,相同为假,不同为真 。 用 ^ 表示。
前两个符号我们常用于脚本中来判断一些条件的成立与否。
【 】~~~~~~~~~~
[root@localhost bin]# id root &> /dev/null && echo "user is exist" || echo "user is not exist"
user is exist 当返回结果为真时则执行&& 后的命令
[root@localhost bin]# id xiaoqiang &> /dev/null && echo "user is exist" || echo "user is not exist"
user is not exist 当返回结果为假时则执行||后的命令
【test命令】
test命令用来检测两个字符或者比较两个值的大小来返回&& 或者||的值。
其实,test 命令可以用 ”[“ 来表示,”[“命令还有一个参数为”]“ 。注意:它们一个是命令,一个是该命令的参数,并不是合体的~~ ”[ ]“ 表示将参数放入其中进行比较,与test 命令用法相同。例如:
[root@localhost bin]# a=aaa
[root@localhost bin]# b=aa
[root@localhost bin]# test "$a" == "$b" && echo true || echo false
false
[root@localhost bin]# [ "$a" == "$b" ] && echo true || echo false
false
【注意】在使用”[]” 左右大括号旁边必须有空格间隔。
数值测试:
参数 | 注释 |
---|---|
-gt | 是否大于 |
-ge | 是否大于等于 |
-eq | 是否等于 |
-ne | 是否不等于 |
-lt | 是否小于 |
-le | 是否小于等 |
字符串测试:
参数 | 注释 |
---|---|
== | 是否等于 |
> | ASCII码是否大于ASCII码 |
< | 是否小于 |
!= | 是否不等于 |
=~ | 左侧字符串是否能够被右侧所匹配【此表达式一般用于[[ ]]中;支持扩展的正则表达式】 |
-z | 字符串是否为空,空为真,不空为假 |
-n | 字符串是否不空,不空为真,空为假 |
文件测试:
参数 | 注释 |
---|---|
-a FILE | 文件存在性测试,存在为真,否则为假 |
-e FILE | 同-a |
-b FILE | 判断文件为块设备文件时为真 |
-c FILE | 判断文件为字符设备文件时为真 |
-d FILE | 判断文件为目录文件时为真 |
-f FILE | 判断文件为普通文件时为真 |
-h FILE 或 -L FILE | 判断文件为符号链接文件时为真 |
-p FILE | 判断文件为命名管道文件时为真 |
-S FILE | 判断文件为套接字文件时为真 |
-r FILE | 判断文件存在且可读时为真 |
-w FILE | 判断文件存在且可写时为真 |
-x FILE | 判断文件存在且可执行权限时为真 |
-u FILE | 判断文件且拥有suid权限时为真 |
-g FILE | 判断文件存在且拥有sgid权限时为真 |
-k FILE | 判断文件且拥有sticky权限时为真 |
-s FILE | 是否存在且非空时为真 |
-t fd | fd表示文件描述符是否已经打开且与某终端相关 |
-N FILE | 文件自动上一次被读取之后是否被修改过 |
-O FILE | 当前有效用户是否为文件属主 |
-G FILE | 当前有效用户是否为文件属组 |
【】~~~~~~~~~
[root@localhost bin]# b1=34
[root@localhost bin]# b2=12
[root@localhost bin]# [ $b1 -gt $b2 ] && echo true || echo false
true
[root@localhost bin]# [ $b1 -lt $b2 ] && echo true || echo false
false
[root@localhost bin]# [ $b1 -eq $b2 ] && echo true || echo false
false
练习:
编写脚本/root/bin/checkdisk.sh,检查磁盘分区空间和inode使用率,如果超过80%,就发广播警告空间将满
#!/bin/bash
var1=`df|egrep -o "[[:digit:]]{1,3}\b%"|cut -d '%' -f1|sort -nr|head -1`
var2=`df -i|egrep -o "[[:digit:]]{1,3}\b%"|cut -d '%' -f1|sort -nr|head -1`
[ $var1 -gt 80 ]&& wall warn:空间将满 ||wall 空间使用率不足80%
[ $var2 -gt 80 ]&& wall warn:inode空间将满 ||wall indoe空间使用率不足80%
unset var1 var2
【解题】将磁盘利用率和inode使用率的数字提取出来再与 80 进行比较就好了。
【bash 的组合测试条件】
(1)
命令1 && 命令2 表示并且
命令1 || 命令2 表示或者
! command 表示非
如:[[ -r file ]] && [[ -w file ]]
(2)
EXPRESSION1 -a EXPRESSION2 并且
EXPRESSION1 -o EXPRESSION2 或者
! EXPRESSION
必须使用测试命令进行
eg:[ -f /bin/cat -a -x /bin/cat ] && cat /etc/fstab
练练手:
编写脚本/root/bin/excute.sh ,判断参数文件是否为sh后缀的普通文件,如果是,添加所有人可执行权限,否则提示用户非脚本文件;
#!/bin/bash
#------------------------------------------------------
#Filename:excute.sh
#Author:wan
#Date:2017-08-04
#Description:判断文件是否为脚本文件
#-----------------------------------------------------
[ -f $1 ] && \
(varsh=`basename $1 |grep -o "\..*$"`
[[ "$varsh" == .sh ]] && [[ -f $1 ]] && (chmod u+x $1 && echo 该文件为脚本文件并且已添加可执行权限) || echo "该文件非脚本文件" )|| echo "该文件不存在"
unset varsh
【解题】这个脚本用到的都是以上所说的内容。首先判断这个文件是不是以 .sh
结尾的文件,然后判断是不是一个普通文件。这两个条件都满足时,判断这个文件是否存在,存在则给这个文件加上执行权限并输出正确结果,不满足其上任何一个条件则返回一个错误结果。
【read命令】
当我们用以上方法写脚本时,在脚本中我们就已经固定了变量的格式以及写法,但其实这种方法对于交互式的用户登陆时非常不人性化,用户并没有提前了解你的脚本里所定义的格式,所以需要我们使用一种方法来自动的提示用户所需要输入的内容,这时候就用到read命令。使用read来把输入值分配给一个或多个shell变量。参数如下:
参数 | 注释 |
---|---|
-p | 指定要显示的提示 |
-s | 静默输入,一般用于密码 |
-n # | 指定输入的字符长度最大值# |
-d ‘字符’ | 输入结束符,当你输入的内容出现这个字符时,立即结束输入 |
-t N | 超出N秒没有进行输入,则自动退出。 |
【Ps】read 从标准输入中读取值,给每个单词分配一个变量,其余剩余的单词都被分配给最后一个变量。
[root@localhost ~]# read -p "请输入用户名 :" username && echo "您的用户名为$username"
请输入用户名 :tom
您的用户名为 tom
[root@localhost ~]# read -sp "请输入密码 :" passwd && echo "您的密码为:$passwd "
请输入密码 :您的密码为:123456