首页 > 代码库 > bash脚本编程基础
bash脚本编程基础
回顾:
文件系统管理
管理工具:mkfs,mke2fs,e2label,tune2fs,dumpe2fs,e2fsck,blkid
mkfs.xfs,mkfs.vaft,fsck
mmkswap,swapon,swapoff
mount,umount,fuer,lsof
df,du
fstab文件:
设备 挂载点 文件系统类型 挂载选项 转储频率 自检次序
文件系统:
目录也是文件
目录也有元数据inode,元数据存放在inode table中
数据:data blocks
下级文件或目录的文件名与其inode对应关系
dentry
文件名:上级目录;
删除文件:
(我们从别处复制或者创建一个2G的文件非常慢,但是删除一个文件速度会非常快,这是什么原因导致的?
其实删除文件的方式非常容易,因为他无需填充数据,也无需移除数据,)
删除文件:
第一步:将此文件指向的所有data block标记为未使用状态(这就意味着其他数据在填充时,可以覆盖里面的原有数据了。可以使用这些磁盘块了);
第二步:此文件的inode标记为未使用;(这仅需要修改inode表中条目的状态,和各datablock,并修改inode位图,和datablock位图即可完成);
之所以有些文件删除以后,能够通过一些文件恢复工具找回,原理是我们扫描这些数据块上是否依然有文件,只需要扫描某些inode,这些inode中是否有有效的数据,而不用管他是否有标记未使用;从而能够把误删除的文件找回来。但是我们如果在此分区上,删除了文件,又存储了文件,这时候数据很难找回。
复制和移动文件:
复制:就是新建文件。
(其实就是新建一个文件,将原文件的数据流导出来,然后在流入cetons,无论是在本地文件系统,还是跨文件系统,复制就是新建文件。)
移动文件:
我们剪切文件时,我们发现速度非常快,尤其是在同一个分区上的时候,但是跨分区时就不一定了。原因:在同一个文件系统上移动文件,仅仅是将其路径映射修改了,inode还是那个原来的inode,连这个文件的inode某没有变,那么inode指向的data base数据也就不变。
[root@centos6 ~]# ls -i fstab.2
2373340 fstab.2
[root@centos6 ~]# mv fstab.2 /tmp
[root@centos6 ~]# ls -i /tmp/fstab.2 //移动前后,文件的inode不变
2373340 /tmp/fstab.2
[root@centos6 ~]#
移除文件:
在同一个文件系统:改变的仅仅是其路径;
在不同文件系统;复制数据至目标文件,并删除源文件;
符号连接:
一般情况下,一个文件都有一个元数据和数据,但是符号链接没有数据,即data block,因为在符号链接的元数据中的数据块指针中,存放的是另一个目录或文件的路径。但是当指向的目标文件的路径非常长时,当数据块指针空间不够用时,也是需要使用数据块的。
权限为:lrwxrwxrwx 777
硬链接:
指向同一个inode,任何一个文件路径被删除,另一个文件路径还有效,
bash脚本编程:
脚本文件格式:
首行顶格写:#!/bin/bash
注释信息:用#表示
代码注释:
缩进,适度添加空白行;
语言:编程语法格式,库,算法和数据结构
编程思想:
问题空间--------->解空间
脚本:
在脚本中前面一般需要写一些说明信息:
description(描述)
#version:0.0.1
author:dong<qq.com>
date:2015-11-10
变量:
(shell脚本中没有复杂的数据结构,但是并不妨碍我们自己去组织出来复杂的数据结构,为了组织出来复杂的数据结构,我们就需要一些内建的数据存储机制,比如说像变量,还有像多个变量连续组织在一起的存储结构就叫数组。)
shell脚本中的变量类型:
局部变量(代码片段),
本地变量(当前shell进程)
环境变量:当前进程及其子进程
位置参数变量
特殊变量
变量其实就是有名字的内存空间。
变量中的数据类型:
字符型
数值型
精确数值型;
整型
近似数值型:
浮点数(单精度,双精度)
但是shell脚本内部是不支持浮点数的,只能借助其他 的工具来实现。
shell编程是典型的弱类型语言。
因为shell将所有的数值类型都看作是字符型。所以他的算术运算,加“+”就看做是字符串的连接,其他得减,乘,除就没什么意义了。
[root@centos6 ~]# apple=iphone //下面我们想输出iphones
[root@centos6 ~]# echo "$apples"
[root@centos6 ~]# echo "${apple}s" //要加大括号括起来
iphones
[root@centos6 ~]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
[root@centos6 ~]# PATH="$PATH:/local/apache/bin" //在PATH后面再加一个路径,可以 不用加双引号。
[root@centos6 ~]# echo $PATH
/usr/lib64/qt-3.3/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin:/local/apache/bin
[root@centos6 ~]#
算术运算:
+ - * / % **(次方运算)
let VAR=expression
VAR=$[expression]
VAR=$((expression))
VAR=$(expr 参数1 运算符 参数2) //$() 是命令引用。这个运算表达式运算 符前后都有空格
注意:有些时候乘法符号*需要转义;
在shell脚本中支持变量的增强型赋值:
[root@centos6 ~]# declare -i i=1
[root@centos6 ~]# echo $i
1
[root@centos6 ~]# i=$[$i+1]
[root@centos6 ~]# echo $i
2
[root@centos6 ~]# i=$[$i+1]
[root@centos6 ~]# echo $i
3
[root@centos6 ~]#
上面的这个例子就是变量I将自身每次加1然后存回i本身,像这样的运算就是变量的增强型赋值
增强型赋值:
变量做某种算术运算后,回存至此变量中。
let i=$i+#
let i+=#
常用的增强型的表达式:
+= -= *= /= %=
这种增强型的赋值,在shell脚本中我们一般要使用let进行描述。
自增:
VAR=$[$VAR+1]
let VAR+=1
let VAR++ //这个表示在自身上每次都加1
自减:
VAR=$[$VAR-1]
let VAR-=1
let VAR--
演示:
我们写一个脚本,创建两个用户,并求出这两个用户UID的和
编写的脚本内容是:
#!/bin/bash
id user1 &> /dev/null || useradd user1
id user2 &> /dev/null || useradd user2
user1_id=$( id -u user1 )
user2_id=$( id -u user2 )
id_sum=$[ $user1_id+$user2_id ]
echo "T he id sum: $id_sum."
运行上面的脚本,我们可以直接执行,不给他赋执行权限的话:
bash idsum.sh
练习:
1.写一个脚本
计算/etc/passwd文件中的第10个用户和第20个用户的id号之和
解:
分析:先用命令实现将两个用户的ID号取出:
id1=$(head -10 /etc/passwd | tail -1 | cut -d: -f3) id2=$(head -20 /etc/passwd | tail -1 | cut -d: -f3)
问题拓展,如果要想取出UID最大的两个用户,然后求其UID之和,那就用sort进行一下排序,然后再取。
2.写一个脚本
计算/etc/rc.d/init.d/functions和/etc/inittab文件的空白行数之和;
解:
分析:要先怎样去界定空白行:
grep"^[[:space:]]*$" /etc/rc.d/init.d/functions |wc -l
条件测试:
(什么是条件测试?
如果某用户不存在我们就去添加用户,那怎样去判断用户是否存在呢?之前我们用的是id命令,id后跟用户名,如果执行失败就添加用户,那么这就是条件测试)
条件测试:
判断某需求是否满足,需要由测试机制来实现;
(专用的测试表达式,需要由测试命令来辅助完成,那问题是我们如何实现测试机制,如何完成测试条件的描述)
举例:
判断某用户是否存在?判断某文件中是否有空白行?
解:
我们编写一个命令,只要这条命令执行正确,我们就认为这个文件是存在空白行,我们在查看上面的这条命令的执行状态结果为0即为正确执行:
(1)我们判断一个用户存在不存在,我们要先知道怎样用命令将这个用户名取出来,然后再利用命令执行状态结果的返回值来判断
[root@centos6 ~]# who | grep "^centos\>"
[root@centos6 ~]# echo $?
1 //说明命令不存在
[root@centos6 ~]#
(2) grep "^$" /etc/rc.d/init.d/functions
[root@centos6 ~]# grep "^$" /etc/rc.d/init.d/functions&> /dev/null
[root@centos6 ~]# echo $?
0
[root@centos6 ~]#
上面这条命令我们根据他的执行状态结果的返回值来完成测试。
但是也有一些条件测试我们无法给他编写成命令
比如我们判断一下2是否大于3
这是我们需要使用一个比较表达式执行测试,像“2>3”这个表达式本身不是一个命令,不能在命令行中运行,这时我们就需要给借助一个专门用于比较的测试表达式来实现
[root@centos6 ~]# test 2 > 3
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# test 2 < 3
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# test 2<3
[root@centos6 ~]# echo $?
1
[root@centos6 ~]# test 2>3
[root@centos6 ~]# echo $?
1
[root@centos6 ~]#
注意:由上面的执行命令,我们需要说明的是,在shell脚本中进行数值的比较并不是使用大于号或者小于号进行的,而是使用特定的符号
如何编写测试表达式,以实现所需的测试:
(1)执行命令,并利用命令的状态返回值来判断;
0:成功
1-255:失败
(2)测试表达式:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:在测试EXPRESSION两端必须有空白字符,否则为语法错误;
bash测试类型:
数值测试
字符串测试
文件测试
数值测试:数值比较
-eq:是否等于
举例: [ $num1 -eq $num2 ]
-ne:是否不等于
-gt:是否大于
-ge:是否大于等于
-lt:是否小于
-le:是否小于等于
举例:
[root@centos6~]# test 5 -ge 6 //比较符前后要有空格
[root@centos6~]# echo $?
1
[root@centos6~]# test 5 -lt 6
[root@centos6~]# echo $?
0
[root@centos6~]#
字符串测试:字符比较
==:两个等于号,表示两个字符是否相同;(这使用一个等号也是可以的,因 为比较表达式卸载中括号中,我们的赋值是不会写在中括号中的。)
> :是否大于;
< :是否小于;
!= :是否不等于;
=~ :左侧字符串是否能够被右侧的PATTERN所匹配
-z "STRING":判断指定的字串是否为空;空则为真,不空则为假;
-n"STRING":判断指定的字符串是否不空;不空则为真,空则为假;
字符间的比较无非就是按照字符在ASCII表中对应的数值进行比较。
注意:
(1)字符串要加引号,表示引用;做变量替换就用双引号,不做替换可以用单引号
(2)做字符间比较时要使用[[]];
我们在做字符间比较时,尽量使用双中括号“[[]]”在单中括号中比较是不生效的。
举例1:
[root@centos6 ~]# [ tom == Tom ]
[root@centos6 ~]# echo $?
1
[root@centos6 ~]# [ tom == tom ]
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# [ tom == $name ]
-bash: [: tom: unary operator expected
[root@centos6 ~]# [ tom =="$name" ] //由于name的值不存在,也就是null值,不加引 号的话,我们的tom不知道跟谁比较,所以我们 加上引号,表示至少这也是个空串
[root@centos6 ~]# echo $?
1
[root@centos6 ~]#
注意:在有些场景中我们必须要给他加引号,但凡使用变量,在执行变量之间的等值或者大小比较时,一定要记得加引号。平时我们用的时候不加引号没有问题。
举例2:
[root@centos6~]# [[ a>b ]]
[root@centos6~]# echo $?
1
[root@centos6~]# [[ a<b ]]
[root@centos6 ~]# echo $?
0
[root@centos6~]# [ a>b ]
[root@centos6~]# echo $?
0
[root@centos6~]# [ a<b ]
[root@centos6~]# echo $?
0
[root@centos6~]#
演示字符比较中的“=~”
[root@centos6 ~]# name=jerry
[root@centos6 ~]# [[ "$name" =~e.* ]] //这也说明了这个也不是做全部匹配的,只要能匹 配模式中的一部分就行
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# [[ "$name" =~ e]]
[root@centos6 ~]# echo $?
0
[root@centos6 ~]# [ "$name" =~ e] //这也看出来做字符比较时我们一定要使用[[]]
-bash: [: =~: binary operator expected
[root@centos6 ~]#
脚本的状态返回值:
默认是脚本中执行的最后一条命令的状态返回值;
(但是二者不能等同,比如说脚本前面30条命令都错了,就最后一个命令是对的,那最后echo一下发现脚本是正确的,因为最后的echo命令正常执行了,为了避免出现这种结果,我们自定义执行状态结果返回值)
自定义状态退出状态码;
exit [n]:不加数字表示状态码为0,加数字n为自己指定的状态码;
注意:在shell脚本中不能轻易使用exit,shell进程遇到exit时,即会终止,因此,整个脚本执行即为结束;
那么exit不能随便加,我们需要给他加一个判断条件以后再加exit:
举例:
使用条件测试来判断我们要不要退出:
不如说,先判断用户是否存在,存在我们就退出,不存在我们就添加用户,
[root@centos6 ~]# id user3 &> /dev/null && exit 0 ||useradd user3
这里连终端都退出了。但是在shell脚本中运行时是退出脚本。
上面的这个命令就用到了短路与,前面的命令执行正确,那么就执行exit,前面的得错误就直接执行后面的创建。
本文出自 “11847750” 博客,请务必保留此出处http://11857750.blog.51cto.com/11847750/1875121
bash脚本编程基础