首页 > 代码库 > bash脚本编程基础
bash脚本编程基础
文件测试:
(文件测试的功能无非就是判断文件的存在性,权限等测试;字符测试和前面的讲到的数值测试,字符测试还是不一样的,数值测试和字符测试他们主要是使用双目测试,即需要两个测试数,只有使用到“-z”或“-n”时他们才是单目运算符,对于文件测试而言,大部分使用的是单目的,说白了就是需要一个操作数,)
文件存在性测试:
-a FILE //FILE就是表示需要自己替换成文件路径
-e FILE
文件的存在性测试,存在则为真,否则则为假;
举例:
[root@centos6 ~]# [ -e/etc/rc.d/init.d/functions ]
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# [ -e /etc/rc.d/rc.sysinit]
[root@centos6 ~]# echo $?
0
[root@centos6 ~]#
文件存在性及类型测试:(存在且为块设备)
-b FILE:是否存在并且为块设备文件;
-c FILE:是否存在并且为字符设备文件;
-d FILE:是否存在并且为目录文件;
-f FILE:是否存在并且为普通文件;
-h FILE或 -L FILE:是否存在并且为符号链接文件;
-p FILE:是否存在且为命名管道文件;
-S FILE:(大写的S)是否存在并且为套接字文件;
举例:
[root@centos6 ~]# test -b /dev/sda
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# test -b /dev/sdb
[root@centos6 ~]# echo $?
1
[root@centos6 ~]# [ -e /etc/inittab ] //文件存在
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# [ -d /etc/inittab ] //但她不是块设备
[root@centos6 ~]# echo $?
1
[root@centos6 ~]#
文件权限测试:
-r FILE:是否存在并且对当前用户可读;
-w FILE:是否存在并且对当前用户可写;
-x FILE:是否存在并且对当前用户可执行;
特殊权限测试:
-g FILE:是否存在并且拥有sgid权限;
-u FILE:是否存在并且拥有suid权限;
-k FILE:是否存在并且拥有sticky权限;
举例:
[root@centos6 ~]# ls -l /usr/bin/passwd
-rwsr-xr-x. 1 root root 30768 Nov 24 2015 /usr/bin/passwd
[root@centos6 ~]# [ -u /usr/bin/passwd ]
[root@centos6 ~]# echo $?
0
[root@centos6 ~]#
文件是否有内容测试:
-s FILE:(小写s)是否有内容也可以说成是否不为为空,有内容为真;
举例:
[root@centos6 ~]# touch yes
[root@centos6 ~]# [ -s yes ]
[root@centos6 ~]# echo $?
1
[root@centos6 ~]# [ -s /etc/fstab ]
[root@centos6 ~]# echo $?
0
[root@centos6 ~]#
文件侧时间戳做测试:
-N FILE:文件自从上一次读取操作后,是否被修改过;
从属关系测试:
-O FILE(大写O):当前用户是否为文件的属主;
-G FILE:当前用户是否属于文件的属组;
双目测试:
FILE1 -ef FILE2:FILE1与FILE2是否指向同一个文件系统的相同inode的硬链接;
FILE1 -nt FILE2:FILE1最近的一次更新时间是否比FILE2的更新时间距离这一刻 更近,所以简写为:FILE1是否新于FILE2;
FILE1 -ot FILE2:是否旧于FILE2;
组合测试条件:
逻辑运算:
第一种方式:
COMMAND1 && COMMAND2
COMMAND1 || COMMAND2
!COMMAND
举例:
当前用户为文件的属主,并且拥有读权限;
解:
[ -O FILE ] && [ -r FILE ]
第二种方式:
(就是我们经常编写成测试表达式的时候,而不是命令)
EXPRESSION1 -a EXPRESSION2 (与运算)
EXPRESSION1 -o EXPRESSION2 (或运算)
!EXPRESSION (取反运算)
上面的这个测试表达式有三种形式:
test EXPRESSION1 -a EXPRESSION2
[ EXPRESSION1 -a EXPRESSION2 ]
[[ EXPRESSION1 -a EXPRESSION2 ]]
举例:
判断当前用户是否属于这个文件的属主,并且有执行权限
解:
[ -O FILE -a -x FILE ]
练习:
将当前主机名称保存至hostName变量中;
主机名如果为空,或者为localhost.localdomain,则将其设置为www.mageedu.com;
解:
我们可以编写一个脚本
#!/bin/bash
hostName=$(hostname )
[ -z "$hostName" -o "$hostName" == "localhost.localdomain" -o "$hostName" == "localhost.localdomain" ] && hostname www.mageedu.com
向脚本传递参数:
常用的变量有五种类型:
局部变量
本地变量
环境变量
位置参数变量
特殊变量
位置参数:
接下来就讲解一下位置参数变量和特殊变量,其实向脚本传递参数就是用位置参数变量来实现,位置参数变量是什么?简单的来讲,当我们去运行ls命令的时候,我们一般后面跟上一个目录,例如:“ls /etc/var”那么这个“/etc/var”就是参数,那么我们在程序中我们如何知道对方给我们的参数,我们如何将我们的代码作用在我们的参数之上呢,就像ls这个命令的作者,再写ls时他必须能够从命令行获取用户传递过来的参数,并且能让自己内部的某些代码作用在这些参数上,而像这些传递给命令的参数,我们就称之为位置参数
对于shell脚本来讲他们的确是通过位置来引用的。
举例:
假如我们现在写一个脚本,脚本的名字就叫myscript.sh,并且向这个脚本中传递的第一个参数就是argu1,第二个参数是argu2,......等等。那么我们就可以在脚本中直接引用,只要是用户传递的参数,我们就可以在脚本中直接引用他
引用方式:
使用$1,$2来实现的,当我我们给脚本的参数很多的时候,当达到两位数的时候,我们,需要给数字加上花括号比如:${10},${11}......
而$1是整个脚本中位置参数中的第一个,我们传递的第一个参数他会自动的保存在$1中,这就跟我们之前讲到的反向引用一样,当我们需要向脚本中传递参数,一回车运行时,argu1就会自动的保存在$1中,argu2会自动的被保存在$2中,依次类推,那么此时的$1,$2....这些就被称为位置参数变量,他们的确是可变的但是每一次运行脚本时,通过传递参数的变化,来改变这些变量的值。
举例:
编写一个脚本,我们通过命令行随便给两个整数,我们能够求出这两个整数之和。
解:
我们编写的脚本的名字为“sum.sh”
脚本的内容为:
#!/bin/bash
echo $[$1+$2]
保存这个脚本,下面我们运行一下,我们可以给这个脚本一个执行权限,我们也可以使用bash直接运行这个脚本后面跟上我们的参数
[root@centos6scripts]# bash sum.sh 2 6
sum=8
[root@centos6scripts]#
轮替:
除此之外位置参数还可以做轮替
shift [n]:位置参数轮替;(解释后面的字母n表示依次轮替多少个参数,n为1,shift就能够踢掉此前的对应位的第一个参数,n为2表示剔除此前的前两个参数,但是shift不能指定是哪一位,n为3就代表剔除前三个。)
什么叫轮替嗯?
本来我们传递的第一个参数,就像刚才我们给定的第一个参数是2但是我们可以将这个2剔除,那么后面的6就变成了第一个了。
举例:
仍然写一个脚本,这个脚本的名字就叫 “shift.sh”
脚本的内容是:
#!/bin/bash
#
echo "First pos argu: $1"
shift
echo "Second pos argu: $1"
我们运行上面的脚本并给这个脚本的参数为:one two
[root@centos6scripts]# bash shift.sh onetwo
Firstpos argu:one
SecondPOS argu:
[root@centos6scripts]#
分析:
其实我们执行上面的脚本时,我们两次其实都是运行的 “$1”,也就是脚本的第一个位置参数,我们在脚本中执行“shift”命令时,其实我们往脚本中传送的第一个参数“one”其实就已经被踢掉了,那么我们再传递two时,其实还是保存在第一个位置参数$1中。那么以此类推我们想剔除几个就剔除几个。
我们也可以剔除两个,比如说我们先传递两个位置参数,然后我们将这两个都剔除,然后我们再传递一个参数,然后我们输出这个脚本的第一个位置参数。
#!/bin/bash
#
echo "First and Second pos argu: $1,$2"
shift 2 //表示我们这次剔除2个位置参数,如果我们不指定剔除几个,那么 shift默认是剔除1个。
echo "Third pos argu: $1"
执行脚本的结果显示我们位置1参数为3
[root@centos6scripts]# bash shift.sh 1 2 3
Firstand Second pos argu:1, 2
ThirdPOS argu:3
[root@centos6scripts]#
练习:
写一个脚本,通过命令行传递两个文本文件路径给脚本,计算空白行数之和;
解:
分析:我们看到“向脚本中传递参数”那么我们就应该想到引用位置参数。那么文件保存在$1和$2后,接下来我们就使用这个参数,然后利用命令讲这个参数中的文件中的空白行取出,首先我们先读取这个文件我们才能知道有没有空行,然后我们使用过滤文本和正则表达式,将空行取出,然后计算
脚本的内容如下:
#!/bin/bash
file1=$(cat $1 | grep "^$" | wc -l)
file2=$(cat $1 | grep "^$" | wc -l)
echo "sum_line=$[ $file1+$file2]"
执行这个脚本:
[root@centos6scripts]# bash file_line.sh/etc/fstab /etc/gdm/custom.conf
sum_line=8
[root@centos6scripts]#
老师的写法:
在求每个文件的空白行时是这样写的:file_lines=$(grep "^$" $1 | wc -l)
特殊变量:
$0:脚本文件路径本身;
$#:脚本参数的个数
$*:所有参数;
$@:所有参数;
"hello" "hi" "toyou"
"hello hi toyou"
注意:$*与$@的不同之处在于,向脚本传递了n个字符串,那么有一个是将这n个字符串当成了一个整体,就像"hello hi toyou";另外一个是将这个n个参数分别当成不同的参数进行传递仅脚本中就像"hello" "hi" "toyou",但是对于我们现在的用法来讲这两种没什么不同,我们可以先不用注意。
举例:
(1)$0表示是脚本名称,或者说脚本文件路径本身,假如我们给定一个脚本路径,假设这个脚本执行给的是全路径或者是“./”开始的,那么我们应该如何获取脚本文件自己的实际的文件名。
我们编写一个脚本,脚本的名字为“filename.sh”然后该脚本的内容为:
#!/bin/bash
echo $0
保存退出后,然后我们给文件加上执行权限
chmod +x filename.sh
./filename.sh
[root@centos6scripts]# ./filename.sh
./filename.sh //这里显示的脚本文件路径为“./”
[root@centos6scripts]#
我们可以将上面的脚本内容改为
#!/bin/bash
basename $0
再次执行这个脚本:
[root@centos6scripts]# ./filename.sh
filename.sh
[root@centos6scripts]#
(2)假设我们想知道我们一共向脚本中一共传递了多少个参数。我们可以使用“$#”
编写一个脚本,文件名为“pos.sh”脚本文件内容:
#!/bin/bash
echo $1,$2
echo $#
执行上面的脚本:
[root@centos6scripts]# ./pos.sh 5 6
5,6
2
[root@centos6scripts]#
[root@centos6scripts]# ./pos.sh hello hi
hello,hi
2
[root@centos6scripts]#
有了上面的特殊变量,那么我们马上就能做一个判断了,前面我们写过一个脚本,要求用户输入两个文件的路径,然后求空白行之和,那么如果当用户只是输入一个路径怎么办?
当我们只给一个路径然后执行这个脚本时,发现我们的脚本卡主了,因为在脚本中第二个命令grep无法运行了,那么这时候我们就可以要去用户至少给两个以上的参数,在运算之前就做判断了。
#!/bin/bash
#
#
[ $# -lt 2 ] && echo "At lease twofiles" && exit 1
file1=$(cat$1 | grep "^$" | wc -l)
file2=$(cat$2 | grep "^$" | wc -l)
echo"sum_line=$[$file1+$file2]"
执行脚本:
[root@centos6 scripts]# ./file_line.sh /etc/issue
At lease two files
[root@centos6 scripts]# echo $?
1 //这里的1不是命令执行失败的那个1,这是我们自定义的1,表示脚本命令 在这个地方结束了。
[root@centos6 scripts]#
过程式编程语言的代码执行顺序:
(1)顺序执行:逐条运行;
(2)选择执行:代码存在一个或多个分支,只执行一个分支;
代码有一个分支:条件满足时才会执行;
两个或两个以上的分支:只会执行其中一个满足条件的分支;
(3)循环执行:
代码片段(循环体)要执行0,1或多个来回;
选择执行:
单分支的if语句:
if 测试条件;then //两个注意点:测试条件为真代码才能往下执行;要有分号,没 有分号表示语法错误
代码分支
fi
上面的单分支语句还可以写成这样的格式:
if 测试条件
then
代码分支
fi
双分支的if语句:
if 测试条件;then //测试条件为布尔型测试语句,要么为真要么为假,没有其 他情况
条件为真时执行的分支
else
条件为假时执行的分支
fi
示例:
通过参数传递一个用户名给脚本,此用户不存在时,则添加用户;
分析:
我们写一个名字叫“useradd.sh”的脚本,通过脚本传递一个参数过来,不管传递的是什么,我们首先要判断用户是否存在,判断用户是否存在,我们现在有两种方法:一种是一个命令,另一种是一个表达式。如果是一个表达式,那我们就将其放在一个中括号中或者前面加test表示,怎样判断一个用户存在?无非就是有两种方法,一种是id 用户名,要是成功则说明这个用户存在,第二种就是grep这个用户名到/etc/passwd文件中
那么现在我们就使用在文件/etc/passwd中grep这个用户名,如果有结果表示存在。
则我们的测试条件表达式就是这样写,用户名传递给脚本则保存在位置参数$1中,那么我们就过滤这个变量$1,而且这个$1是在行首,是一个单词,那么我们就单词尾部锚定grep "^$1\>" /etc/passwd 这就是表示在文件中某一行行首的单词中有我们给定的这个用户名,则就表示这个用户存在,存在就不创建,但是我们现在要求是不存在为真。那么现在是存在为真,那么我们取反,就表示存在为假,那么也就是说不存在为真,所以测试条件因该这样写:!grep "^$1\>" /etc/passwd 这个过滤的 结果对我们不重要,那么我们=就将这个结果输出到文件/dev/null中。
综上所述:测试条件表达式为:! grep "^$1\>" /etc/passwd &> /dev/null
所以我们的脚本内容为:
if ! grep "^$1\>" /etc/passwd &> /dev/null;then //特别注意叹号前后都 有空格
useradd $1
echo "123456" | passwd --stdin $1 > /dev/null
echo "Add user $1 finished." //添加用户完成后的提示信息
fi
上面的脚本还可以更加的完善,就是当用户运行上面的脚本时,压根就忘了传参数,怎么办?那我们就可以在if语句的前面再加上一个判断参数的测试语句;
[ $# -lt 1 ] && echo "plese input a username" && exit 2
上面的这语句又可以写成一个条件判断语句:
if [ $# -lt 1 ];then
echo "Please input a username!"
exit 2
fi
执行上述脚本:
[root@centos6scripts]# ./useradd.sh
plese input a username
[root@centos6scripts]# echo $? //命令执行状态结果返回值是自己定义的2
2
[root@centos6scripts]#
上面的命令我们还可以使用双分支语法来写:
#!/bin/bash
#
if [ $# -lt 1 ];then
echo "Please input a username!"
exit 2
fi
if grep "^$1\>" /etc/passwd &> /dev/null;then
echo "the user $1 exists"
else
useradd $1
echo "123456" | passwd --stdin $1 &> /dev/null
echo "Add user $1 finished."
fi
练习1:通过命令行参数给定两个数字,输出其中较大的数值;
解:
#!/bin/bash
#
if [ $# -lt 2 ];then
echo "Two integers."
exit 2
fi
if [ $1 -ge $2 ];then
echo "Max number:$1"
else
echo "Max number:$2"
fi
上面的代码echo语句重复太多,我们可以将其优化一下,代码前面先声明一个变量
#!/bin/bash
#
declare -i max
if [ $# -lt 2 ];then
echo "Two integers."
exit 2
fi
if [ $1 -ge $2 ];then
max=$1
else
max=$2
fi
echo "Max number:$max"
练习2:通过命令行参数给定一个用户名,判断其ID号是偶数还是奇数;
解:
练习3:通过命令行参数给定两个文本文件名,如果某文件不存在,则结束脚本执行;
都存在时返回每个文件的行数,并说明其中行数较多的文件;
本文出自 “11847750” 博客,请务必保留此出处http://11857750.blog.51cto.com/11847750/1875123
bash脚本编程基础