bash内容回顾

Shell Liemer_Lius 1187℃ 0评论

bash及其特性:

shell: 外壳程序

广义的shell分类:
GUI:Gnome, KDE, Xfce, 常用的三种
CLI: sh(bone开发的), csh(Berkeley, Bill Joy开发的.), ksh(商业的,社区版), bash(Bone again shell, 开源, 众多Linux的默认shell), tcsh(kshell的增强版, 开源的), zsh(功能更加强大, 但是不是很流行)

root, student
程序:进程可以理解为程序的副本, 可以是多个进程, 但程序只有一个; 进程是程序的副本,是程序执行实例. 进程是有生命周期的.

进程:在每个进程看来,当前主机上只存在内核和当前进程, 看不到其他进程的存在.

用户工作环境:每个用户打开都有自己的环境, 设定可以各不相同, 默认一般是一样的.

bash: 命令提示符prompt
#: 管理员
$: 普通用户

shell中还能打开子shell. 用bash命令可以直接打开bash的子shell.
shell,子shell

Example:
# bash; bash
# pstree | tail -5
|-rsyslogd—3*[{rsyslogd}]
|-snmpd
|-sshd—sshd—bash—bash—bash-+-pstree
| `-tail
`-udevd—2*[udevd]
可以看到sshd中有三层bash. 用exit来退出子shell.
每一层shell有自己独立的环境, 因此在父shell中的设定, 在子shell中是无效的.
shell中可以打开不同版本的shell:

bash: 常用的特性
1、命令历史、命令补全
2、管道、重定向
3、命令别名
4、命令行编辑:快速定位、删除等
5、命令行展开:如:touch {a,b}.txt = touch a.txt b.txt
6、文件名通配
7、变量
8、编程: 最重要的一点, 编写的程序就是脚本.

命令行编辑:用快捷键实现快速编辑命令行
光标跳转:
Ctrl+a:跳到命令行首
Ctrl+e:跳到命令行尾
Ctrl+u: 删除光标至命令行首的内容
Ctrl+k: 删除光标至命令行尾的内容
Ctrl+l: 清屏,等于clear命令
Ctrl+ ← →:跳动一个单词

命令历史:Linux的命令,可以用上下箭头来查看以前键入的命令。
查看命令历史:history
-c:清空内存中的命令历史, clear; 虽然清空了命令历史, 但是~/.bash_history中仍然存放着命令操作的历史内容.
-d OFFSET: 删除指定位置的命令,偶尔用到. 如果删除多个,可以在OFFSET后面增加一个数字,表示删除的个数。
history -d 500 3: 删除第500开始的3个命令历史
-w:手动保存命令历史缓存中信息至历史文件中~/.bash_history; 通常, 只有正常退出的时候, 命令历史会追加到命令历史文件中.
命令文件:~/.bash_history中.

命令历史的使用技巧:
!n:执行命令历史中的第n条命令;
!-n:执行命令历史中的倒数第n条命令;
!!: 执行上一条命令;
!string:执行命令历史中最近一个以指定字符串开头的命令
!$:引用前一个命令的最后一个参数;
Esc –> .: 按一下Esc, 再按一下”.”, 可以引用前一个命令的最后一个参数, 相当于!$的用法. 可以一直键入,翻看命令历史中最后一个参数。
Alt+.: 按住Alt, 再按一下”.”, 也可以实现这个功能; 但是, 这个用法只能在虚拟终端上使用, 远程模拟终端无法实现.

环境变量
PATH:命令搜索路径
HISTSIZE: 命令历史缓冲区大小, 默认是100, echo $HISTSIZE查看.
HISTFILESIZE: 命令文件的缓冲条目大小
# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/apache2/bin:/root/bin

命令补全:搜索PATH环境变量所指定的每个路径下以我们给出的字符串开头的可执行文件,如果多于一个,两次tab,可以给出列表;否则将直接补全
方法: 输入命令的开头字符, 按Tab键. 如果字符的开头只有一个命令, 直接补全, 并附上空格键; 如果有多个命令以给定的字符开头, 按两下Tab键, 显示所有以给定字符开头的命令.
路径补全:方法和上面一样, 但是机制不同. 命令查找在PATH中查找; 路径补全在打头路径下查找. 搜索我们给出的起始路径下的每个文件名,并试图补全;

命令别名:
alias CMDALIAS=’COMMAND [options] [arguments]’
在shell中定义的别名仅在当前shell生命周期中有效;别名的有效范围仅为当前shell进程;
如果要永久有效, 需要修改配置文件, 甚至可以实现全局有效, 所有用户都可以用到.

别名的删除/撤销:
ualias CMDALIAS: 不要输入别名指定的内容。

常用alias:
alias cdnet=’cd /etc/sysconfig/network-scripts’
alias lftps=’lftp 172.16.0.1/pub’

很多情况, 命令别名和命令本身一样, 如:
alias ls=’ls –color=tty’
这时候, 如果要引用命令本身的作用, 需要加反斜线.
\CMDALIAS

命令替换: 把命令中某个子命令替换为其执行结果的过程
两种方法:
$(COMMAND):
# a=$(date); echo $a
2017年 07月 13日 星期四 16:03:06 CST

反引号:`COMMAND`
# a=`echo hello`; echo $a
hello

Example:
# echo “The current directory is $(pwd).”
# touch file-$(date +%F-%H-%M-%S)
file-2013-02-28-14-53-31.txt

$()的作用就是实现命令替换, 可以用反引号“来引用, 即:
# touch file-`date +%F-%H-%M-%S`

bash支持的引号:
“: 命令替换, 等于$()
“”: 弱引用,可以实现变量替换
”: 强引用,不完成变量替换

文件名通配, globbing
*: 匹配任意长度的任意字符
?:匹配任意单个字符
[]:匹配指定范围内的任意单个字符
[abc], [a-m], [a-z], [A-Z], [0-9], [a-zA-Z], [0-9a-zA-Z]
[:space:]:空白字符,不止空格
[:punct:]:标点符号
[:lower:]:小写字母
[:upper:]: 大写字母
[:alpha:]: 大小写字母
[:digit:]: 数字
[:alnum:]: 数字和大小写字母
注意, 如上的部分表示的是一个范围, 而如果和[]联合使用, 以匹配单个字符的话, 需要在外面再包一层[]:
[[:space:]] … …

# man 7 glob //man命令获取标点符号的帮助, 可以获得这些列表.
[[^]]: 内部的^符号, 表示取反的意思. 匹配指定范围之外的任意单个字符
‘^[[:digit:]]’: 正则表达式中也有^, 通常在[]外面引号里面, 表示以[]的字符开头.

Example:
# touch a bd 12dkj dj54 sd9 dk21 aa 232 ddj
# ls
12dkj 232 a aa bd ddj dj54 dk21 sd9

# ls [a-zA-Z]*[0-9] //
dj54 dk21 sd9

# ls [^0-9]* //匹配非数字开头的项.
a aa bd ddj dj54 dk21 sd9

# touch ‘a b’ //空格也是可以作为文件名的.
# ls a\ b
a b

# ls *[[:space:]]* //用空白字符的标准写法来匹配
a b
# ls *\ * // 和用反引号的效果相同
a b

练习:
1、创建a123, cd6, c78m, c1 my, m.z, k 67, 8yu, 789等文件;注意,以上文件是以逗号隔开的,其它符号都是文件名的组成部分;
# touch a123 cd6 c78m c1 my m.z k\ 67 8yu 789 //注意, 空格可以作为文件名的组成部分, 但是在命令行中要用\来脱义才能执行成功, 否则空格被认为是分割符.

2、显示所有以a或m开头的文件;
ls [am]*

3、显示所有文件名中包含了数字的文件;
ls *[0-9]*
ls *[[:digit:]]*

4、显示所有以数字结尾且文件名中不包含空白的文件;
ls *[^[:space:]]*[0-9] ????????? 这个结果到底能不能完全表示如上的结果? 正则表达式能够完全表达上面的要求.

5、显示文件名中包含了非字母或数字的特殊符号的文件;
ls *[^[:alnum:]]*

6、匹配以大小写字母开头, 中间夹着一个空格, 以非大小写字母结尾的任意字段.
[[:alpha:]]*[[:space:]]*[^[:alpha:]]:

shell编程:是一种脚本编程, 边解释边执行.

编程语言:机器语言 –> 汇编语言 –> 高级语言

编程语言的分类:
1. 静态语言:编译型语言
通常是强类型(变量)
事先转换成可执行格式, 完全转换
C、C++、JAVA、C#

2. 动态语言:解释型语言, on the fly, 边解释, 边执行.
通常是弱类型: 通常是弱类型的语言.
边解释边执行, 不需要事先转换. 解释器通常都是C开发的。
PHP、SHELL、python(现在比较流行)、perl(早先流行)

编译器:静态语言用到的
解释器:动态语言用到的,解释器是由静态语言编写.

编程语言还可以遵循如下的分类方法:
面向过程:Shell, C, 把整个编程的着眼点放在问题解决过程本身. 适合开发小型的应用程序. 但Linux内核是用面向过程的C研发的.
面向对象: 把整个要实现的项目, 抽象成一个一个的对象, 并且定义对象之间的动作, 适合开发大型的应用程序. JAVA, Python, perl, C++, 一般适合开发大型的应用程序. Python和JAVA是完全面向对象的, 后面两个也有部分面向过程. C++有许多的优势, 也会产生许多难以排解的bug.

变量:命名的内存空间.
内存:编址的存储单元, 16进制.

进程:执行的程序,程序的实例.
运算的时候, 内存的分配并不是将每个变量都给予一段内存, 一段内存可以复用, 即过期的值自动作废, 换新的值.

变量类型:用来事先确定数据的存储格式和长度
字符: 一个字符为8bit
数值: 较字符来说要节省空间
整型: 整数
浮点型: 11.23,1.123*10^1, 0.1123*10^2
日期类型:2013/10/10, 64bit
如果是数值, 将1970年1月1日到现在; 字符串就要浪费很多存储空间. 存储的格式是不一样的.
布尔型:真、假

整型,8bit: 如果要存储256呢?
8bit只能存储的数值换算成十进制为: 0-255, 存储256时候, 由于超出范围而溢出, 仅保留后八位, 结果是0.
溢出如果是精心设计的, 可以将其余进程覆盖掉, 这是缓冲区溢出的机制; 如果覆盖的是管理相关的进程, 则可以获取系统权限, 达到入侵计算机的目的.

逻辑运算:与、或、非、异或
与:两个部分都为真,整体结果才为真
或:两个部分有一个为真,整体结果就为真
非:! 真 = 假; ! 假 = 真
异或运算: 相同为真, 不同为假(操作数相同得1,不相同得0:如1和0得0,0和0得1)

短路逻辑运算: 知道一部分条件满足, 即可确定最终结果.
1. 对与运算, 一个为假, 结果一定为假;
2. 对或运算, 一个为真, 结果一定为真.

shell: 弱类型编程语言
强:变量在使用前,必须事先声明,甚至有时候还需要初始化(给一个原始值,否则是一个随机数;一般数字初始化为0,字符串初始化为空NULL);
弱:变量用时声明,甚至不区分类型;

变量赋值:VAR_NAME=VALUE
赋值方式还有其他机制.

变量转换:
显式转换: 事先声明转换的类型
隐式转换: 内部机制自动完成转换,如1+a,有的程序自动将a转换成ACSII码的id来进行相加。
编译式语言在编译的时候就已经转换完成; 脚本语言在执行的时候, 需要转换时候完成转换.

变量的引用:
${}来引用: 多数情况{}可以省略, 但是下面的这种情况不能省略.
# ANIMAL=pig
# echo “There are 4 $ANIMALs.” //默认会将$后面的所有内容当做变量的名称, 而$ANIMALs没有定义, 因此显示为空.
There are 4 .
# echo “There are 4 ${ANIMAL}s.”
There are 4 pigs.

bash变量类型(作用范围和变量的表示型来分):默认的shell变量的值都是字符串
环境变量: 如PATH变量等,都是环境中的一些配置变量
本地变量(局部变量): 局部变量未必是本地变量
位置变量: 类似后向引用
特殊变量: 保存某些特殊数据的变量, 也成系统变量.
特殊变量:
$?: 执行状态返回码
$#:参数的个数
$*: 参数列表, 列出参数
$@:参数列表, 和$*有不同,
变量一定是进程的变量!

位置变量的应用: $1, $2, …
shift: 引用一个参数以后就踢出去. 如, 就一个$1, 参数却是很多个. shift以后, 第一个自动剔除, 第二个代替第一个.
shift可以用后面加数字n的方式, 来指定轮替的个数.

各变量解析:
1、本地变量:
set VARNAME=VALUE: 作用域为整个bash进程,对子shell无效,不用set默认就是本地变量。
2、局部变量:
local VARNAME=VALUE:作用域为当前代码段,仅在函数中使用,函数外失效;
3、环境变量:作用域为当前shell进程及其子进程;
export VARNAME=VALUE

脚本在执行时会启动一个子shell进程,所以环境变量是有效的;而shell脚本中定义的变量,在结束脚本以后失效的原因就是因为它是一个子shell进程,对父进程没有影响。所以系统自动执行的脚本(非命令行启动,如cron任务)就需要自己定义需要各环境变量;
命令行中启动的脚本会继承当前shell环境变量(就是一段命令,如果是bash test.sh这种,之前定义的变量是没有作用的,除非export一下);

环境变量的定义方法:
1、方法一
VARNAME=VALUE
export VARNAME
“导出”

# NAME=Jerry
# export NAME
# echo $NAME
Jerry
# bash
# echo $NAME
Jerry
2、方法二
export VARNAME=VALUE

4、位置变量:引用脚本的参数的变量
$1, $2, …

5、特殊变量:保存特殊数据的变量
$?: 上一个命令的执行状态返回值;

程序执行,可能有两类返回结果:
1. 程序执行结果
2. 程序状态返回代码(0-255)
0: 正确执行
1-255:错误执行,1,2(参数错误的返回值),127(命令错误的返回值)系统预留;其他用户可以自己定义.

# id student; echo $?
返回值可以判定上一条命令是否执行正确, 做为条件来为下一步命令来提供条件.

位桶:/dev/null: 软件设备,bit bucket,数据黑洞, 可以吃掉所有数据, 不显示任何内容. 但是, echo $?可以显示状态值.
# id sss &> /dev/null; if [ `echo $?` -eq 0 ]; then echo “Yes”; else echo “No”; fi
No
# id root &> /dev/null; if [ `echo $?` -eq 0 ]; then echo “Yes”; else echo “No”; fi
Yes

如果是字符串变量,可以在后面进行丰富,就像$PATH一样,如:
$ ANIMALS=pig
$ ANIMALS=$ANIMALS:goat
$ echo $ANIMALS
pig:goat
而这个变量,我们可以通过循环,将每一个动物分割出来:
$ for i in `echo $ANIMALS | awk -F: ‘{print $1,$2}’`; do echo $i; done
pig
goat

撤消变量(无论什么类型的变量):直接加变量的名称, 不要加$.
# unset VARNAME
导出变量的时候, 也是这样的原理, 只export VARNAME, 而没有用$.

查看当shell中变量:不加任何参数,显示包括本地变量、环境变量;
# set
查看当前shell中的环境变量:以下三个命令都可以
printenv
env
export

变量名称的定义要求:
1、只能包含字母、数字和下划线,并且不能数字开头;
2、不应该跟系统中已有的环境变量重名;
3、最好做到见名知义;

脚本:命令的堆砌,按实际需要,结合命令流程控制机制实现的源程序

内核不能理解单纯的命令堆积, 只理解ELF(Executed Linked File)格式, 需要添加执行权限, 添加以个脚本解释器, 这就是下面的魔数.
shebang: 魔数, magic number, 由#!和脚本解释器路径组成.
#!/bin/bash, 以#!开头, 加上解释器的路径.
# 注释行,不执行

练习:写一个脚本,完成以下任务
1、添加5个用户, user1,…, user5
2、每个用户的密码同用户名,而且要求,添加密码完成后不显示passwd命令的执行结果信息;
3、每个用户添加完成后,都要显示用户某某已经成功添加;
useradd user1
echo “user1” | passwd –stdin user1 &> /dev/null
echo “Add user1 successfully.”

bash中条件测试类型:
整数测试
字符测试
文件测试: 是否存在

条件测试的表达式:
[ expression ]: 必须有空格
[[ expression ]]: [[]]是bash的关键字测试,与上面的不一样.
test expression: test后面加表达式

练习:如果一个变量等于一段字符串,则打印一些信息。
#!/bin/bash
# Test usage of [[]].
A=hello
if [[ $a == ‘hello’ ]]; then
echo “Yes”
else
echo “No”
fi
# bash b.sh
Yes

整数比较:
-eq: 测试两个整数是否相等;比如 $A -eq $B
-ne: 测试两个整数是否不等;不等,为真;相等,为假;
-gt: 测试一个数是否大于另一个数;大于,为真;否则,为假;
-lt: 测试一个数是否小于另一个数;小于,为真;否则,为假;
-ge: 大于或等于
-le:小于或等于

命令的间逻辑关系:符合短路逻辑
逻辑与: &&, 其中一个为假, 结果一定为假, 后面的就不执行了, 符合”短路逻辑”.
第一个条件为假时,第二条件不会再判断,最终结果已经有;
第一个条件为真时,第二条件才会判断, 且必须得判断;
逻辑或: ||

示例:
如果用户user6不存在,就添加用户user6: 两种方法可用
! id user6 && useradd user6
id user6 || useradd user6

如果是复合使用,如下:(不过多复杂,从左到右依次判断)
! id user6 && useradd user6 || userdel user6
这里表示,如果不存在用户就创建;如果存在了就删除。

如果用户存在,就显示用户已存在;否则,就添加此用户;
id user1 && echo “user1 exists.” || useradd user1

如果用户不存在,就添加;否则,显示其已经存在;
! id user1 && useradd user1 || echo “user1 exists.”

如果/etc/inittab文件的行数大于100,就显示好大的文件;
[ `wc -l /etc/inittab | cut -d’ ‘ -f1` -gt 100 ] && echo “Large file.”

如果用户不存在,添加并且给密码;否则,显示其已经存在;
#!/bin/bash
# Add user1, user2, user2 if not exist.
! id user1 &> /dev/null && useradd user1 && echo “Add user1 successfully.” && echo “user1” | passwd –stdin user1 &> /dev/null && echo “user1 authorized successfully.” || echo “user1 exists.”
! id user2 &> /dev/null && useradd user2 && echo “Add user2 successfully.” && echo “user2” | passwd –stdin user2 &> /dev/null && echo “user2 authorized successfully.” || echo “user2 exists.”
! id user3 &> /dev/null && useradd user3 && echo “Add user3 successfully.” && echo “user3” | passwd –stdin user3 &> /dev/null && echo “user3 authorized successfully.” || echo “user3 exists.”
USER_NUM=`wc -l /etc/passwd |cut -d’ ‘ -f1`
echo “There are $USER_NUM users.”
注: wc -l /etc/passwd的结果如下:
37 /etc/passwd
所以, 要用cut命令, 截取其中的数字. 分割符是空格.

练习
写一个脚本,完成以下要求:
1、添加3个用户user1, user2, user3;但要先判断用户是否存在,不存在而后再添加;
2、添加完成后,显示一共添加了几个用户;当然,不能包括因为事先存在而没有添加的;
3、最后显示当前系统上共有多少个用户;
#!/bin/bash
! id user1 &> /dev/null && useradd user1 && echo “user1” |passwd –stdin &> /dev/null || echo “user1 exists.”

写一个脚本,完成以下要求:
给定一个用户:
1、如果其UID为0,就显示此为管理员;
2、否则,就显示其为普通用户;
方法一:
#!/bin/bash
# Confirm whether user is Administrator.
if test `id -u` -eq 0; then
echo “Admin”
else
echo “Common user.”
fi

方法二:
NAME=user16
USERID=`id -u $NAME`
if [ $USERID -eq 0 ]; then
echo “Admin”
else
echo “common user.”
fi

方法三:
NAME=user16
if [ `id -u $NAME` -eq 0 ]; then
echo “Admin”
else
echo “common user.”
fi

区别:if id $NAME; then,这个是表示用户存在才执行某些命令的。

练习:写一个脚本
判断当前系统上是否有用户的默认shell为bash;
如果有,就显示有多少个这类用户;否则,就显示没有这类用户;
grep “bash$” /etc/passwd &> /dev/null
RETVAL=$?
if [ $RETVAL -eq 0 ]; then

if grep “bash$” /etc/passwd &> /dev/null; then

提示:“引用”一个命令的执行结果,要使用命令引用;比如: RESAULTS=`wc -l /etc/passwd | cut -d: -f1`;
使用一个命令的执行状态结果,要直接执行此命令,一定不能引用;比如: if id user1一句中的id命令就一定不能加引号;
如果想把一个命令的执行结果赋值给某变量,要使用命令引用,比如USERID=`id -u user1`;
如果想把一个命令的执行状态结果保存下来,并作为命令执行成功与否的判断条件,则需要先执行此命令,而后引用其状态结果,如
id -u user1
RETVAL=$?
此句绝对不可以写为RETVAL=`id -u user1`;

练习:写一个脚本
给定一个文件,比如/etc/inittab
判断这个文件中是否有空白行;
如果有,则显示其空白行数;否则,显示没有空白行。

#!/bin/bash
# To show whether there is blank lines in a file.
grep “^$” /etc/inittab &> /dev/null
RETVAL=`echo $?`
if [ $RETVAL -eq 0 ]; then
NUM=`grep “^$” /etc/inittab |wc -l |cut -d’ ‘ -f1`
echo “Blank lines exist. There are $NUM blank lines.”
else
echo “There has no blank line.”
fi
–by Liemer_Lius

#!/bin/bash
A=`grep ‘^$’ /etc/inittab | wc -l`
if [ $A -gt 0 ]; then
echo “$A”
else
echo “meiyoukongbaihang”
fi
—— by 张帅

#!/bin/bash
FILE=/etc/inittab
if [ ! -e $FILE ]; then
echo “No $FILE.”
exit 8
fi

if grep “^$” $FILE &> /dev/null; then
echo “Total blank lines: `grep “^$” $FILE | wc -l`.”
else
echo “No blank line.”
fi

练习:写一个脚本
给定一个用户,判断其UID与GID是否一样
如果一样,就显示此用户为“good guy”;否则,就显示此用户为“bad guy”。

#!/bin/bash
# To determain whether the UID equals GID.
if [ `id -u` -eq `id -g` ]; then
echo “The UID and GID are the same.”
else
echo “The UID and GID are different.”
fi

#!/bin/bash
USERNAME=user1
USERID=`id -u $USERNAME`
GROUPID=`id -g $USERNAME`
if [ $USERID -eq $GROUPID ]; then
echo “Good guy.”
else
echo “Bad guy.”
fi

进一步要求:不使用id命令获得其id号;

#!/bin/bash // 首先的判断,确定有该用户
#
USERNAME=user1
if ! grep “^$USERNAME\>” /etc/passwd &> /dev/null; then
echo “No such user: $USERNAME.”
exit 4
fi

#!/bin/bash
# Don’t use id command to determain whether the uid and the gid are the same.
USER_ID=`head -1 /etc/passwd |cut -d: -f3`
GROUP_ID=`head -1 /etc/passwd |cut -d: -f4`
if [ $USER_ID -eq $GROUP_ID ]; then
echo “The UID and the GID are the same.”
else
echo “The UID and GID are different.”
fi

USERID=`grep “^$USERNAME\>” /etc/passwd | cut -d: -f3`
GROUPID=`grep “^$USERNAME\>” /etc/passwd | cut -d: -f4`
if [ $USERID -eq $GROUPID ]; then
echo “Good guy.”
else
echo “Bad guy.”
fi

练习:写一个脚本
给定一个用户,获取其密码警告期限;
而后判断用户密码使用期限是否已经小于警告期限;
提示:计算方法,最长使用期限减去已经使用的天数即为剩余使用期限;

如果小于,则显示“Warning”;否则,就显示“OK”。

圆整:丢弃小数点后的所有内容

#!/bin/bash
W=`grep “student” /etc/shadow | cut -d: -f6`
S=`date +%s`
T=`expr $S/86400`
L=`grep “^student” /etc/shadow | cut -d: -f5`
N=`grep “^student” /etc/shadow | cut -d: -f3`
SY=$[$L-$[$T-$N]]

if [ $SY -lt $W ]; then
echo ‘Warning’
else
echo ‘OK’
fi
—— by 董利东

#!/bin/bash
NEWPASS=`grep ‘^\<root\>’ /etc/shadow |cut -d: -f3`
LONGTIME=`grep ‘^\<root\>’ /etc/shadow |cut -d: -f5`
WORNTIME=`grep ‘^\<root\>’ /etc/shadow |cut -d: -f6`
NOWTIME=$[`date +%s`/65536]

TIMEUSED=$[$NOWTIME-$NEWPASS]
TIME_LEFT=$[$LONGTIME-$TIMEUSED]

if test $TIME_LEFT -le $WORNTIME; then
echo “It time to change your password.”
else
echo “Your password is effective.”
fi

——by myself

练习:写一个脚本
判定命令历史中历史命令的总条目是否大于1000;如果大于,则显示“Some command will gone.”;否则显示“OK”。
# history |tail -1 // history命令得到的数值,可能会超过1000,这时候才提示。cut的时候,应该取第二段,第一段是空的。
456 history |tail -1

shell中如何进行算术运算:
A=3
B=6
1、let 算术运算表达式
let C=$A+$B
2、$[算术运算表达式]
C=$[$A+$B]
3、$((算术运算表达式))
C=$(($A+$B))
4、expr 算术运算表达式,表达式中各操作数及运算符之间要有空格,而且要使用命令引用
C=`expr $A + $B`

# A=2
# B=5
# C=`expr $A + $B`
# echo $C
7
注: expr – evaluate expressions

条件判断,控制结构:

单分支if语句
if 判断条件; then
statement1
statement2

fi

双分支的if语句:
if 判断条件; then
statement1
statement2

else
statement3
statement4

fi

多分支的if语句:if和elif都需要then
if 判断条件1; then
statement1

elif 判断条件2; then
statement2

elif 判断条件3; then
statement3

else
statement4

fi

测试脚本:
#!/bin/bash
#
A=2
B=3
if test $A -eq $B; then
echo “A equals B.”
else
echo “A not equal B.”
fi
执行脚本:
# bash -x a.sh
+ A=2
+ B=3
+ test 2 -eq 3
+ echo ‘A not equal B.’
A not equal B.

文件测试:
-e FILE:测试文件是否存在
-f FILE: 测试文件是否为普通文件
# if test -f b.sh; then echo “Common file.”; else echo “Exordinary file.”; fi
-d FILE: 测试指定路径是否为目录
# if test -d /bin; then echo “Directory.”; else echo “Not.”; fi
-r FILE: 测试当前用户对指定文件是否有读取权限;
-w FILE: 测试当前用户对指定文件是否有写取权限;
-x FILE: 测试当前用户对指定文件是否有执行取权限;

[ -e /etc/inittab ]
[ -x /etc/rc.d/rc.sysinit ]

另外, test指令也是可以使用的:
#!/bin/bash
#
if test -e /bin/bash; then
echo “Exist.”
else
echo “Not exist.”
fi

练习:写一个脚本
给定一个文件:
如果是一个普通文件,就显示之;
如果是一个目录,亦显示之;
否则,此为无法识别之文件;

#!/bin/bash
# Input something and analyze what type it is.

read -p “Please input a file or directory: ” STH

if [ ! -e $STH ]; then
echo “No such file or directory.”
elif [ -f $STH ]; then
echo “It’s a file.”
else
echo “It’s a directory.”
fi

定义脚本退出状态码
exit: 退出脚本
exit #
如果脚本没有明确定义退出状态码,那么,最后执行的一条命令的退出码即为脚本的退出状态码;
通常, 在exit前面, 通常会有一个echo来输出提示信息; echo本身的执行, 肯定是一个正确的状态. 而默认如果exit后面不定义状态码的时候, 默认以最后一条命令的执行状态码做为整个脚本的执行状态结果, 因此常常显示为0, 这不是我们预期的. 所以, 在定义exit的时候, 一定要记得自定义一个执行状态码.

测试脚本是否有语法错误:
bash -n 脚本: 查看语法错误,但很模糊。
bash -x 脚本:单步执行,便于排除bug。

练习:写一脚本
能接受一个参数(文件路径)
判定:此参数如果是一个存在的文件,就显示“OK.”;否则就显示”No such file.”
脚本如下: 丰富参数个数的判断, 丰富参数的列出, 丰富各个参数的单独判断并打印相关的提示信息.

#!/bin/bash
#
if test $# -lt 1; then
echo “We need at least 1 options.”
exit 3
fi
echo -e “The options are: $*”

for i in $*; do
if test -e $i; then
echo “$i exist.”
else
echo “No such file named $i”
fi
done

执行脚本:
# ./b.sh /etc/inittab /etc/passwd /proc/meminfoo
The options are: /etc/inittab /etc/passwd /proc/meminfoo
/etc/inittab exist.
/etc/passwd exist.
No such file named /proc/meminfoo

练习:写一个脚本
给脚本传递两个参数(整数);
显示此两者之和,之乘积;

#!/bin/bash
#
if [ $# -lt 2 ]; then
echo “Usage: cacl.sh ARG1 ARG2”
exit 8
fi

echo “The sum is: $[$1+$2].”
echo “The prod is: $[$1*$2].”

转载请注明:liutianfeng.com » bash内容回顾

喜欢 (0)

发表回复