目录
Bash(GNU Bourne-Again Shell)是一个为GNU计划编写的Unix shell,它是许多Linux平台默认使用的shell。
shell是一个命令解释器,是介于操作系统内核与用户之间的一个绝缘层。准确地说,它也是能力很强的计算机语言,被称为解释性语言或脚本语言。它可以通过将系统调用、公共程序、工具和编译过的二进制程序”粘合“在一起来建立应用,这是大多数脚本语言的共同特征,所以有时候脚本语言又叫做“胶水语言”
而我认为shell和bash的区别,bash就像是shell的一个子集,bash是目前比较常用的一种shell脚本,可移植性强,大多数情况说的shell 指的就是bash。
shell脚本规范
#!/bin/bash
# This is a comment
echo Hello World
#!/bin/bash
这一行是表示使用 /bin/bash 作为脚本的解释器,这行要放在脚本的行首并且不要省略。
#!
说明这个文件的类型,有点类似于 Windows 系统下用不同文件后缀来表示不同文件类型。Linux 系统根据
#!
及该字符串后面的信息确定该文件的类型。在 BASH 中 第一行的#!
及后面的 /bin/bash 就表明该文件是一个 BASH 程序,需要由 /bin 目录下的 bash 程序来解释执行。BASH 这个程序一般是存放在 /bin 目录下,如果你的 Linux 系统比较特别,bash 也有可能被存放在 /sbin 、/usr/local/bin 、/usr/bin 、/usr/sbin 或 /usr/local/sbin 这样的目录下
#
号开头的行都是注释语句
运行Bash脚本的方式:
# 使用shell来执行
$ sh 文件名.sh
# 使用bash来执行
$ bash 文件名.sh
# 使用.来执行
$ . ./文件名.sh
# 使用source来执行
$ source 文件名.sh
# 还可以赋予脚本所有者执行权限,允许该用户执行该脚本
$ chmod u+rx 文件名.sh
$ ./文件名.sh
bash字符
- 注释(#)
- 命令分隔符(;)
使用分号(;)可以在同一行上写两个或两个以上的命令。
- 终止case选项(;;)
- 等价于 source 命令(.)
bash 中的 source 命令用于在当前 bash 环境下读取并执行 FileName.sh 中的命令,即“. FileName.sh”命令等价于“source FileName.sh”
- 双引号(")和单引号(')
单引号会直接认为是字符,而双引号会被认为是一个变量
- 文件名路径分隔符(/)
- 转义字符(\)
\也提供续行功能,也就是在一行里编写多行命令的功能。\n 表示新的一行, \r 表示回车
- 命令优先执行(`)
反引号中的命令会优先执行,如cp `mkdir back` test.sh back,先创建了 back 目录,然后复制 test.sh 到 back 目录
- 空命令(:)
等价于“NOP”(no op,一个什么也不干的命令)
- 变量($)
- 命令组和初始化数组(( ))
在括号中的命令列表,将会作为一个子 shell 来运行。在括号中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的。父进程,也就是脚本本身,将不能够读取在子进程中创建的变量,也就是在子shell 中创建的变量。
如a=123;( a=321; );echo "$a",执行后a的值为123而不是321,,因为括号将判断为局部变量,脚本本身,不能够读取在子进程中创建的变量,也就是在子shell 中创建的变量
如arr=(1 4 5 7 9 21) \echo ${arr[3]} # get a value of arr
- 代码块({ })
又被称为内部组,这个结构事实上创建了一个匿名函数(一个没有名字的函数)。然而,与“标准”函数不同的是,在其中声明的变量,对于脚本其他部分的代码来说还是可见的。如a=123;{ a=321; };echo "a = $a"
- 条件表达式和引用数组元素([ ])
- 重定向(< 和 >)
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 不存在的话,将会被创建。
- 管道(|)
分析前边命令的输出,并将输出作为后边命令的输入。这是一种产生命令链的好方法。如ls -l | tr 'a-z' 'A-Z',将当前目录的详细信息全部用大写来输出
- 选项参数(-)
在所有的命令内如果想使用选项参数的话,前边都要加上“-”
- 表示 home 目录(~)
变量
变量的名字就是变量保存值的地方。引用变量的值就叫做变量替换。如果 variable 是一个变量的名字,那么 $variable 就是引用这个变量的值,即这变量所包含的数据。
定义变量时,变量名不加美元符号,如: myname=“shiyanlou”。注意:变量名和等号之间不能有空格。同时,变量名的命名须遵循如下规则:
- 首个字符必须为字母(a-z,A-Z)。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用bash里的关键字(可用help命令查看保留关键字)。
使用变量:变量名前加美元符号,如:echo $myname;echo ${myname},推荐给所有变量加花括号,加花括号帮助解释器识别变量的边界
只读变量:使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。如readonly variableName
局部变量:这种变量只有在代码块或者函数中才可见。
环境变量:这种变量将影响用户接口和 shell 的行为。在通常情况下,每个进程都有自己的“环境”,这个环境是由一组变量组成的,这些变量中存有进程可能需要引用的信息。在这种情况下,shell 与一个一般的进程没什么区别。
运算符
算数运算符
运算符 | 说明 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ | 除法 |
% | 取余 |
= | 赋值 |
== | 相等, 用于比较两个数字,相同则返回 true |
!= | 不相等,用于比较两个数字,不相同则返回true |
如下例:
#!/bin/bash
a=10
b=20
val=`expr $a \* $b`
echo "a * b : $val"
- 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用。
- expr 是一款表达式计算工具,使用它能完成表达式的求值操作。
- 注意使用的反引号(esc键下边)
- 表达式和运算符之间要有空格$a + $b写成$a+$b不行
- 条件表达式要放在方括号之间,并且要有空格[ $a == $b ]写成[$a==$b]不行
- 乘号(*)前边必须加反斜杠(\)才能实现乘法运算
关系运算符
运算符 | 说明 |
---|---|
-eq | 检测两个数是否相等,相等返回 true |
-ne | 检测两个数是否不相等,不相等返回 true |
-gt | 检测左边的数是否大于右边的,如果是返回 true |
-lt | 检测左边的数是否小于右边的,如果是返回true |
-ge | 检测左边的数是否大于等于右边的,如果是返回true |
-le | 检测左边的数是否小于等于右边的,如果是则返回true |
关系运算符只支持数字,不支持字符串
逻辑运算符
运算符 | 说明 |
---|---|
&& | 逻辑的AND |
|| | 逻辑的OR |
字符串运算符
运算符 | 说明 |
---|---|
= | 检测两个字符串是否相等,相等返回true |
!= | 检测两个字符串是否不相等,不相等返回true |
-z | 检测字符串长度是否为0,为0返回true |
-n | 检测字符串长度是否不为0,不为0返回true |
str | 检测字符串是否为空,不为空返回true |
如下例:
#!/bin/bash
a="abc"
if [ $a ]
then
echo "$a : The string is not empty"
else
echo "$a : The string is empty"
fi
文件比较运算符
运算符 | 说明 |
---|---|
-e | 如果文件存在返回true |
-f | 如果这个文件是一个一般文件(并不是目录或者设备文件),返回true |
-s | 如果文件大小不为零,返回true |
-d | 如果这是一个目录,返回true |
-b | 如果这是一个块设备(软盘,光驱,等等),返回true |
-c | 如果这是一个字符设备(键盘,modem,声卡,等等),返回true |
-p | 如果这个文件是一个管道,返回true |
-h | 如果这是一个符号链接,返回true |
-L | 如果这是一个符号链接,返回true |
-S | 如果这是一个socket,返回true |
-t | 文件(描述符)被关联到一个终端设备上,这个测试选项一般被用来检测脚本中的stdin([-t 0]) 或者stdout([-t 1])是否来自于一个终端 |
-r | 文件是否具有可读权限(指的是正在运行这个命令的用户是否具有读权限) |
-w | 文件是否具有可写权限(指的是正在运行这个命令的用户是否具有写权限) |
-x | 文件是否具有可执行权限(指的是正在运行这个命令的用户是否具有可执行权很) |
-g | set-group-id(sgid)标记被设置到文件或目录上 |
-k | 设置粘贴位 |
-O | 判断你是否为文件的拥有者 |
-G | 文件的group-id是否与你的相同 |
-N | 从文件上一次被读取到现在为止,文件是否被修改过 |
f1 -nt f2 | 如果文件f1比文件f2新,返回true |
f1 -ot f2 | 如果文件f1比文件f2旧,返回true |
f1 -ef f2 | 如果文件f1和文件f2是相同文件的硬链接,返回true |
! | 非, 反转上边所有测试的结果(如果没给出条件,那么返回true) |
如下例:
#!/bin/bash
file="/test.sh"
# 由于没有给出条件,所以返回true
if [ ! ]
then
echo "File exists"
else
echo "File not exists"
fi
流程控制
if else
语法格式:
if condition1
then
command1
elif condition2
then
command2
else
commandN
fi
for 循环
语法格式:
for var in item1 item2 ... itemN
do
command1
command2
...
commandN
done
如下例:
#!/bin/bash
for str in This is a string
do
echo $str
done
# 无限循环语法格式:
for (( ; ; ))
while 语句
while循环用于不断执行一系列命令,也用于从输入文件中读取数据
语法格式:
while condition
do
command
done
如下例:
#!/bin/bash
int=1
while(( $int<=5 ))
do
echo $int
let "int++"
done
while循环可用于读取键盘信息。下面的例子中,输入信息被设置为变量MAN,按结束循环。
如下例:
#!/bin/bash
echo 'press <CTRL-D> exit'
echo -n 'Who do you think is the most handsome: '
while read MAN
do
echo "Yes!$MAN is really handsome"
done
# 无限循环语法格式:
while :
do
command
done
或者
while true
do
command
done
until 循环
until循环执行一系列命令直至条件为真时停止。 until循环与while循环在处理方式上刚好相反。 一般while循环优于until循环,但在某些时候—也只是极少数情况下,until循环更加有用。
条件可为任意条件,判断发生在循环末尾,因此循环至少执行一次。
语法格式:
until condition
do
command
done
如下例:
#!/bin/bash
int=1
until (( $int>1 ))
do
echo $int
let "int++"
done
case 语句
多选择语句。可以用case语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令。
语法格式:
case 值 in
模式1)
command1
command2
...
commandN
;;
模式2)
command1
command2
...
commandN
;;
esac
- 取值后面必须为单词
in
,每一模式必须以右括号结束。取值可以为变量或常数。匹配发现取值符合某一模式后,其间所有命令开始执行直至;;
,两个分号在这里表示break。 - 取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号
*
捕获该值,再执行后面的命令。
如下例:
#!/bin/bash
echo 'Enter a number between 1 and 4:'
echo 'The number you entered is:'
read aNum
case $aNum in
1) echo 'You have chosen 1'
;;
2) echo 'You have chosen 2'
;;
3) echo 'You have chosen 3'
;;
4) echo 'You have chosen 4'
;;
*) echo 'You did not enter a number between 1 and 4'
;;
esac
break 命令
break命令允许跳出所有循环(终止执行后面的所有循环)
如下例:
#!/bin/bash
while :
do
echo -n "Enter a number between 1 and 5:"
read aNum
case $aNum in
1|2|3|4|5) echo "The number you entered is $aNum!"
;;
*) echo "The number you entered is not between 1 and 5!"
break
;;
esac
done
continue 命令
仅仅跳出当前循环
如下例:
#!/bin/bash
while :
do
echo -n "Enter a number between 1 and 5: "
read aNum
case $aNum in
1|2|3|4|5) echo "The number you entered is $aNum!"
;;
*) echo "The number you entered is not between 1 and 5!"
continue
echo "game over"
;;
esac
done
函数
语法格式:
[ function ] funname [()]
{
action;
[return int;]
}
- 可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。
- 参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果,作为返回值。 return后跟数值
n(0-255)
- 调用函数时可以向其传递参数。在函数体内部,通过 n 的形式来获取参数的值,例如
$1
表示第一个参数,$2
表示第二个参数… 当n>=10时,需要使用${n}
来获取参数。 - 函数返回值在调用该函数后通过
$?
来获得 - 所有函数在使用前必须定义
如下例:
#!/bin/bash
demoFun(){
a=266
echo "The first parameter is $1 !"
echo "The second parameter is $2 !"
echo "The tenth parameter is ${10} !"
echo "The eleventh parameter is ${11} !"
echo "The total number of parameters is $# !"
echo "Outputs all parameters as a string $* !"
return $(($a))
}
demoFun 1 2 3 4 5 6 7 8 9 34 73
echo "返回值为:$?"
为何最后红框内的数值出现异常呢?原因就是shell的函数规定了return后跟的数值范围在0-255