1.概念:
Shell
是
linux
中一个重要的层次,它是用户与系统交互作用的界面。在介绍
linux
命令时,
shell
都作为命令解释程序出现:它接收用户打入的命令,进行分析,创建子进程实现命令所规定的功能,等子进程终止工作后,发出提示符。这是
shell
最常见的使用方式。
Shell
除了作为命令解释程序以外,还是一种高级程序设计语言,
它有变量,关键字,有各种控制语句,如
if
,
case
,
while
,
for
等语句,有自己的语法结构
。利用
shell
程序设计语言可以编写出功能很强、但
代码简单的程序,特别是它把相关的
linux
命令有机地组合在一起,可大大提高编程的效率,充分利用
linux
系统的开放性能,设计出适合自己要求的命令。
2.简单的脚本:
脚本文件后缀为.sh,创建和打开用vim命令。第一行为
#!/bin/bash
后面可将Linux中的命令加上去,最终的结果和在终端输入一致。
下面是一个简单的shell脚本示例:
#!/bin/bash
#This is to show what a example looks like
date
echo "Our first example."
echo #this inserts an empty line in output .
echo "We are currently in the following diractory."
echo
/bin/pwd
echo
echo "This diractory contains following files"
/bin/ls
Shell
脚本中用
#
表示注释,相当于
C
语言的
//
注释。但如果
#
位于第一行开头,并且是
#! (
称为
Shebang)则例外,它表示该脚本使用后面指定的解释器
/bin/sh
解释执行。如果把这个脚本文件加上可执行权限然
后执行
:,执行方式如下:
user02@vir:~$ chmod a+x test.sh
user02@vir:~$ ./test.sh #方法一
user02@vir:~$ . test.sh #方法二
user02@vir:~$ source test.sh #方法三
更改权限则使用chmod命令。
3.shell语法:
数据类型只有字符串
Shell
变量名通常是以
字母或下划线打头的字母、数字和下划线字符序列,并且大小写字母意义不同
。有两种类型的
Shel
l变量:环境变量和临时变量
环境变量:
环境变量可以从父进程传给子进程,因此
Shell
进程的环境变量可以从当前
Shell
进程传给
fork
出来的子进程。用
printenv
命令可以显示当前
Shell
进程的环境变量。
临时变量:
只存在于当前
Shell
进程,用
set
命令可以显示当前
Shell
进程中定义的所有变量
(包括临时变量和环境变
环境变量是任何进程都有的概念,而临时变量是
Shell
特有的概念。在
Shell
中,环境变量和临时变量的定义和用法相似。在
Shell
中定义或赋值一个变量
量)
和函数:
user02$ VARNAME=HELLO #定义一个临时变量
user02$ export VARNAME=HELLO #定义一个环境变量
user02$ env | grep VARNAME #env能查到就是环境变量,查不到就是临时变量
user02$ unset VARNAME #取消环境变量
文件名代换:
这些用于匹配的字符称为通配符
(wildcard),
如
: * ? [ ]
具体如下
:
*
:匹配
0
个或多个任意字符
?
:匹配一个任意字符
[
若干字符
]
: 匹配方括号中任意一个字符的次出现一次的文件名
命令代换:
由
“ ' ”
反引号括起来的也是一条命令,
Shell
先执行该命令,然后将输出结果立刻代换到当前命令行中。例如
.
定义一个变量存放
date
命令的输出:
user02$ DATE=`date`
user02$ echo=$DATE #$变量名:变量的值
命令代换也可以用
S()
表示
:
user02$ DATE=$(date)
算数代换:
使用
$(())
,用于算术计算,
(())
中的
Shell
变量取值将转换成整数,同样含义的
$[ ]
等价例如
:
user02$ VAR=45
user02$ echo $(($VAR+3)) #
等价
$((VAR+3))
或
$[VAR+3]
S(()))
中只能用
+-*/
和
()
运算符,并且只能做整数运算
S[base#n],
其中
base
表示进制,
n
按照
base
进制解释,后面再有运算数,按十进制解释
user02$ echo $[2#10+3] #
二进制
10
,然后+
3
user02$ echo $[8#10+3] #
八进制
10
,然后
+3
转义字符:
和
C
语言类似,
\
在
Shell
中被用作转义字符,用于去除紧跟其后的单个字符的特殊意义
(
回车除外
)
,换句话说,紧跟其后的字符取字面值。例如
:
user02$ echo \$PATH
user02$ \\
创建文件
--test
touch --test #
报错
单引号:
Shell
脚本中的单引号和双引号一样都是字符串的界定符,而不是字符的界定符。单引号用于保持引号内所有字符的字面值,即使引号内的
\
和回车也不例外,但是字符串中不能出现单引号。如果引号没有配对
就输入回车,
Shell
会给出续行提示符,要求用户把引号配上对。例如
:
user02$ echo "abcde"
user02$ echo 'abcd'
双引号:
被双引号括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。单引号不能展开变量。
user02$ VAR=`date`
user02$ echo "$VAR"
2022
年
09
月
17
日星期六
10:22:52 CST
user02$ echo "${VAR}"
2022
年
09
月
17
日星期六
10:22:52 CST
user02$ echo 'VAR'
user02$ echo '${VAR}'
4.脚本语法:
条件测试:
命令
test
或
[
可以测试一个条件是否成立,如果测试结果为真,则该命令的
Exit Status
为
0
,如果测试结果为假,则命令的
Exit Status
为
1
。例如测试两个数的大小关系
:
user02$ VAR=2
user02$ test $var -gt 1
user02$ echo $?
常见的测试命令如下表所示
:
[ -d DIR ]如果DIR存在并且是一个目录则为真
[ -f FILE ]如果FILE存在且是一个普通文件则为真
[ -z STRING ]如果STRING的长度为零则为真
[ -n STRING ]如果STRING的长度非零则为真
[ STRING1 = STRING2 ]如果两个字符串相同则为真
[ STRING1 != STRING2 ]如果字符串不相同则为真
[ ARG1 OP ARG2 ] ARG1和ARG2应该是整数或者取值为整数的变量,op是-eq (等于) -ne (不等于) -lt (小于) -le (小于等于) -gt (大于) -ge (大于等于)之中的一个。
和
C
语言类似,测试条件之间还可以做与、或、非逻辑运算
:
[ ! EXPR ] EXPR
可以是上表中的任意一种测试条件,
!
表示
“
逻辑反
(
非
)”
。
[ EXPR1 -a EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-a表示“逻辑与”
[ EXPR1 -o EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-o表示“逻辑或”
示例如下所示:
$ VAR=abc
$ [ -d user02 -a $VAR = 'abc' ]
$ echo $?
4.分支:
if/then/elif/fi:
和
C
语言类似,在
Shell
中用
if
、
then
、
elif
、
else
、
fi
这几条命令实现分支控制。这种流程控制语句本质上也是由若干条
Shell
命令组成的
if [ -f ~/.bashrc ] ; then
. ~/.bashrc
fi
其实是三条命令,
if [ -f ~/.bashrc ]
是第一条,
then . ~/.bashrc
是第二条,
fi
是第三条。如果两条命
令写在同一行则需要用
==
;
==
号隔开,一行只写一条命令就不需要写
==
;
==
号了,另外,
then
后面有换行,但这条命令没写完,
Shell
会自动续行,把下一行接在
then
后面当作一条命令处理。和
[
命令一
样,要注意命令和各参数之间必须用空格隔开。
if
命令的参数组成一条子命令,如果该子命令的
Exit
Status
为
0 (
表示真
)
,则执行
then
后面的子命令,如果
Exit Status
非
0 (
表示假
)
,则执行
elif
、
else
或者
fi
后面的子命令。
if
后面的子命令通常是测试命令,但也可以是其它命令。
用
fi
表示
if
语句块的结束。见下例
:
#! /bin/bash
if [ -f test.c ];then
echo "test.c is a file"
else
echo "test.c is not a file"
fi
“==
:
==”
是一个特殊的命令,称为空命令,该命令不做任何事,但
Exit Status
总是真。
echo "Is it morning? Please answer yes or no. "
read YES_OR_NO
if [ "$YES_OR_NO" = "yes" ]; then
echo "Good morning!"
elif [ "$YES_OR_NO" = "no" ]; then
echo "Good afternoon!"
else
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
return ;
fi
上例中的
read
命令的作用是等待用户输入一行字符串,将该字符串存到一个
Shell
变中。
此外,
Shell
还提供了
&&
和
||
语法,和
C
语言类似,具有
Short-circuit
特性,很多
Shell
脚本喜欢写成这样
:
test "$(whoami)" != 'root' && (echo you are using a non-privileged account;)
&&
相当于
“if...then..."
而
||
相当于
“if not...then..."
&&
和
||
用于连接两个命令,而上面讲的
-a
和
-o
仅用于在测试表达式中连接两个测试条件,要注意它们的区别,例如
:
test "$VAR" -gt 1 -a "$VAR" -lt 3
示例如下:
#! /bin/bash
echo "请输入你要判别的文件名: "
read file_ name
if [ -f "$file_ name" ]; then
echo 'it is a file
elif [ -d "$file_ nane" ]; then
echo 'It is a dir'
elif [ -p $file_ _name ] ; then
echo 'It is pipe
else
echo 'it is not a known file'
exit 1
fi
case/esac:
case
命令可类比
C
语言的
switch/case
语句,
esac
表示
case
语句块的结束。
C
语言的
case
只能匹配整型或字符型常量表达式,而
Shell
脚本的
case
可以匹配字符串和
Wildcard
,
每个匹配分支可以有若干条命令,
末尾必须以
;;
结束
,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到
esac
之后,不需要
像
C
语言一样用
break
跳出
示例如下:
#! /bin/bash
echo "Is it morning? Please answer yes or no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)
echo "Good Morning!";;
[nN][oO])
echo "Good Afternoon!";;
*)
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
return 1;;
esac