Bash脚本编程

Posted by jjx on September 26, 2016

什么是Bash?

1.bash简介

Bash(GNU Bourne-Again Shell)是一个为GNU计划编写的Unix shell,它是许多Linux平台默认使用的shell。

shell是一个命令i解释器,是介于操作系统内核与用户之间的一个绝缘层。准确地说,它也是能力很强的计算机语言,被称为解释性语言或脚本语言。它可以通过将系统调用、公共程序、工具和编译过的二进制程序”粘合“在一起来建立应用,这是大多数脚本语言的共同特征,所以有时候脚本语言又叫做“胶水语言” 事实上,所有的UNIX命令和工具再加上公共程序,对于shell脚本来说,都是可调用的。Shell脚本对于管理系统任务和其它的重复工作的例程来说,表现的非常好,根本不需要那些华而不实的成熟紧凑的编译型程序语言。

初步练习

1.Hello World

Bash之Hello World

$ vim hello.sh
使用vim编辑hello.sh ,输入如下代码并保存:

 #!/bin/bash
 # This is a comment
 echo Hello World
运行Bash脚本的方式:

# 使用shell来执行
$ sh hello.sh
# 使用bash来执行
$ bash hello.sh
还可以让脚本本身就具有可执行权限,通过chmod命令可以修改:

# 赋予脚本的所有者该执行权限,允许该用户执行该脚本
$ chmod u+rx hello.sh
# 执行命令,这将使用脚本第一行指定的shell来执行,如果指定shell不存在,将使用系统默认shell来执行
$  ./hello.sh

利用bash编程可以有效的提高工作效率,避免重复输入命令,比如上传github的bash脚本

cd ~/data/whuhan2013.github.io/
git add .
git commit -m 'update'
git push origin master

expect实现ssh登录脚本

安装expect:brew install expect

代码

#!/usr/bin/expect
spawn ssh root@192.168.22.194
expect "*password:"
send "123\r"
expect "*#"
interact

参考链接:expect详解

bash中的特殊字符

一、#符号
1.#注释
转意后的#不能作为注释使用

二、分号(;)

1.命令分隔符
使用分号(;)可以在同一行上写两个或两个以上的命令。

2.终止case选项(双分号)

使用双分号(;;)可以终止case选项。

三、点号(.)

1.等价于 source 命令

bash 中的 source 命令用于在当前 bash 环境下读取并执行 FileName.sh 中的命令。

四、引号

1.双引号(”)

“STRING” 将会阻止(解释)STRING中大部分特殊的字符。

2.单引号(’)

‘STRING’ 将会阻止STRING中所有特殊字符的解释,这是一种比使用”更强烈的形式。

五、斜线和反斜线

1.斜线(/)

文件名路径分隔符。分隔文件名不同的部分(如/home/bozo/projects/Makefile)。也可以用来作为除法算术操作符。

2.反斜线(\)

一种对单字符的引用机制。\X 将会“转义”字符X。这等价于”X”,也等价于’X’。\ 通常用来转义双引号(”)和单引号(’),这样双引号和单引号就不会被解释成特殊含义了。

六、反引号(`)

1.命令替换

command 结构可以将命令的输出赋值到一个变量中去。在后边的后置引用(backquotes)或后置标记(backticks)中也会讲解。

反引号中的命令会优先执行

$ cp `mkdir back` test.sh back
$ ls

先创建了 back 目录,然后复制 test.sh 到 back 目录

七、冒号(:)

1.空命令

等价于“NOP”(no op,一个什么也不干的命令)。也可以被认为与shell的内建命令true作用相同。“:”命令是一个bash的内建命令,它的退出码(exit status)是(0)。

2.变量扩展/子串替换

在与>重定向操作符结合使用时,将会把一个文件清空,但是并不会修改这个文件的权限。如果之前这个文件并不存在,那么就创建这个文件。

 $ : > test.sh   # 文件“test.sh”现在被清空了
 # 与 cat /dev/null > test.sh 的作用相同
 # 然而,这并不会产生一个新的进程, 因为“:”是一个内建命令

在与»重定向操作符结合使用时,将不会对预先存在的目标文件(: » target_file)产生任何影响。如果这个文件之前并不存在,那么就创建它。

也可能用来作为注释行,但不推荐这么做。使用 # 来注释的话,将关闭剩余行的错误检查,所以可以在注释行中写任何东西。然而,使用 : 的话将不会这样。

”:”还用来在 /etc/passwd 和 $PATH 变量中做分隔符,如:

八、问号(?)

1.测试操作符

在一个双括号结构中,? 就是C语言的三元操作符,如:

九、美元符号($)

1.变量替换

2.命令替换(同反引号)

bash中的特殊字符(下)

一、小括号(( ))

1.命令组

在括号中的命令列表,将会作为一个子 shell 来运行。

在括号中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的。父进程,也就是脚本本身,将不能够读取在子进程中创建的变量,也就是在子shell 中创建的变量

2.初始化数组

二、大括号({ })

1.文件名扩展

注意: 在大括号中,不允许有空白,除非这个空白被引用或转义。

2.代码块

代码块,又被称为内部组,这个结构事实上创建了一个匿名函数(一个没有名字的函数)。然而,与“标准”函数不同的是,在其中声明的变量,对于脚本其他部分的代码来说还是可见的。

三、中括号([ ])

1.条件测试

条件测试表达式放在[ ]中。值得注意的是[是shell内建test命令的一部分,并不是/usr/bin/test中的外部命令的一个链接。

双中括号([[ ]])也用作条件测试(判断),后面的实验会详细讲解。

2.数组元素

在一个array结构的上下文中,中括号用来引用数组中每个元素的编号。

四、尖括号(< 和 >)

1.重定向

test.sh > filename 重定向test.sh的输出到文件 filename 中。如果 filename 存在的话,那么将会被覆盖。

test.sh &> filename 重定向 test.sh 的 stdout(标准输出)和 stderr(标准错误)到 filename 中。

test.sh >&2 重定向 test.sh 的 stdout 到 stderr 中。

test.sh >> filename 把 test.sh 的输出追加到文件 filename 中。如果filename 不存在的话,将会被创建。
五、竖线(

管道

分析前边命令的输出,并将输出作为后边命令的输入。这是一种产生命令链的好方法。

六、破折号(-)

1.选项,前缀

在所有的命令内如果想使用选项参数的话,前边都要加上“-”。

2.用于重定向stdin或stdout

变量和参数

一、变量替换

1.概念

变量的名字就是变量保存值的地方。引用变量的值就叫做变量替换。

如果 variable 是一个变量的名字,那么 $variable 就是引用这变量的值,即这变量所包含的数据。

$variable 事实上只是 ${variable} 的简写形式。在某些上下文中 $variable 可能会引起错误,这时候你就需要用 ${variable} 了。

二、变量赋值

1.说明

赋值操作前后都不能有空白。

因为 = 和 -eq 都可以用做条件测试操作,所以不要与这里的赋值操作相混淆。

注意: = 既可以用做条件测试操作,也可以用于赋值操作,这需要视具体的上下文而定。bash中==也可作为条件判断。

三、变量不区分类型

1.说明

与大多数编译型语言不同,Bash并不区分变量的”类型”。本质上,Bash变量都是字符串。但是依赖于具体的上下文,Bash也允许比较操作和整数操作。其中的关键因素就是,为变量赋的值是否只有数字。

引用和转义

1.介绍

在一个双引号中通过直接使用变量名的方法来引用变量,一般情况下都是没问题的。这么做将阻止所有在引号中的特殊字符被重新解释(即都被当作普通的字符串),包括变量名,但是 $、`(后置引用)和 “"(转义符)除外。

保留 $ 作为特殊字符的意义是为了能够在双引号中也能够正常的引用变量(”$variable”)。

使用双引号还能够阻止单词分割,即使这个参数包含有空白,单词也不会被分隔开。 如 variable1=”a variable containing five words”

IFS(Internal Field Seperator)在Linux的shell中预设的分隔符。IFS是shell脚本中的一个重要概念,在处理文本数据时,它是相当有用的。内部字段分隔符是用于特定用途的定界符。IFS是存储定界符的环境变量,它是当前shell环境使用的默认定界字符串。

当我们设置了bash内置变量IFS后,再使用echo输出时,会将所设定的字符用空格去代替。

单引号(’ ‘)操作与双引号基本一样,但是不允许引用变量,因为 $ 的特殊意义将被关闭。

在单引号中,任何特殊字符都按照字面的意思进行解释,除了单引号本身。所以说单引号(全引用)是一种比双引号(部分引用)更严格的引用方法。

二、转义

1.概念

转义是一种引用单个字符的方法。一个前面放上转义符(\)的字符就是告诉shell 这个字符按照字面的意思进行解释,换句话说,就是这个字符失去了它的特殊含义。

退出和退出状态码

一、退出状态码

1.退出

exit 被用来结束一个脚本,它也返回一个值,并且这个值会传递给脚本的父进程,父进程会使用这个值做下一步的处理。

2.退出状态码

每个命令都会返回一个退出状态码(有时候也被称为返回状态)。

成功的命令返回 0,不成功的命令返回非零值,非零值通常都被解释成一个错误码。行为良好的UNIX命令、程序和工具都会返回 0 作为退出码来表示成功,虽然偶尔也会有例外。

同样的,脚本中的函数和脚本本身也会返回退出状态码。在脚本或者是脚本函数中执行的最后的命令会决定退出状态码。在脚本中,exit nnn 命令将会nnn退出码传递给shell(nnn必须是十进制数,范围必须是0-255)。

当脚本以不带参数的exit命令来结束时,脚本的退出状态码就由脚本中最后执行的命令来决定(就是 exit 之前的命令)。

$? 指代的是上一条指令的执行结果

条件判断

一、条件测试结构

1.if/then 结构

if/then 结构用来判断命令列表的退出状态码是否为 0,因为0表示成功,如果成功的话,这里应该那么就执行接下来的一个或多个命令。

注意: 这里与C语言的等其它语言不同,不能直接使用0或者1作为判断条件,而应该以false,true代替。以其它大多数语言相反的true返回的是0,false返回的是1

有一个专有命令 [ (左中括号,特殊字符)。这个命令与test命令等价,由于效率上的考虑,bash将它作为一个内建命令。

注意: 由于bash的语法检查机制,如果在条件测试时只使用一个[将会出现一个错误提示,为了避免这个问题,我们通常将使用一对方括号包含条件测试[]

在版本2.02的Bash中, 引入了 “[[ … ]]” 扩展测试命令,[[ 是一个关键字,并不是一个命令。

if 命令能够测试任何命令,并不仅仅是中括号中的条件。

二、文件测试操作符

操作符	说明
-e	文件存在
-a	文件存在,这个选项的效果与 -e 相同。但是它已经被“弃用”了,并且不鼓励使用。
-f	表示这个文件是一个一般文件(并不是目录或者设备文件)
-s	文件大小不为零
-d	表示这是一个目录
-b	表示这是一个块设备(软盘,光驱,等等)
-c	表示这是一个字符设备(键盘,modem,声卡,等等)
-p	这个文件是一个管道
-h	这是一个符号链接
-L	这是一个符号链接
-S	表示这是一个socket
-t	文件(描述符)被关联到一个终端设备上,这个测试选项一般被用来检测脚本中的 stdin([ -t 0 ]) 或者 stdout([ -t 1 ])是否来自于一个终端
-r	文件是否具有可读权限(指的是正在运行这个测试命令的用户是否具有读权限)
-w	文件是否具有可写权限(指的是正在运行这个测试命令的用户是否具有写权限)
-x	文件是否具有可执行权限(指的是正在运行这个测试命令的用户是否具有可执行权限)

三、二元比较操作符

1.整数比较

-eq 等于

if [ "$a" -eq "$b" ]
-ne 不等于

if [ "$a" -ne "$b" ]
-gt 大于

if [ "$a" -gt "$b" ]
-ge 大于等于

if [ "$a" -ge "$b" ]
-lt 小于

if [ "$a" -lt "$b" ]
-le 小于等于

if [ "$a" -le "$b" ]
< 小于(在双括号中使用)

(("$a" < "$b"))
<= 小于等于(在双括号中使用)

(("$a" <= "$b"))
大于(在双括号中使用)
(("$a" > "$b"))
= 大于等于(在双括号中使用)
(("$a" >= "$b"))

2.字符串比较

= 等于

if [ "$a" = "$b" ]
== 等于,与=等价

if [ "$a" == "$b" ]
!= 不等号

if [ "$a" != "$b" ]
< 小于,按照ASCII字符进行排序

if [[ "$a" < "$b" ]]
if [ "$a" \< "$b" ]
注意"<"使用在[ ]结构中的时候需要被转义
大于,按照ASCII字符进行排序
if [[ "$a" > "$b" ]]
if [ "$a" \> "$b" ]
注意“>”使用在[ ]结构中的时候需要被转义
-z 字符串为“null”,意思就是字符串长度为零 -n 字符串不为“null”