1、执行脚本
文件头:#! /bin/sh
Shell
脚本中用
#
表示注释,相当于
C
语言的
//
注释。但如果
#
位于第一行开头,并且是
#!
(称为
Shebang
)则例外,它表示该脚本使用后面指定的解释器
/bin/sh
解释执行。如果把这个脚本文件加上可执行权限然后执行
执行方式:
(1)创建进程执行
itcast$chmod a+x test.sh
itcast$./test.sh
itcast$ /bin/sh ./test.sh
itcast$ (./test.sh)
shell会fork()出一个子进程来执行。
(2)在交互式shell下执行
itcast$ source ./test.sh
itcast$ . ./test.sh
2、基本语法
2.1、变量
Shell变量通常由字母加下划线开头,由任意
长度的字母、数字、下划
线组成。
(1)环境变量
环境变量可以从父进程传给子进程,因此
Shell
进程的环境变量可以从当前
Shell
进程传给
fork
出来的子进程。
(2)本地变量
环境变量是任何进程都有的概念,而本地变量是Shell
特有的概念。在Shell
中,环境变量和本地变量的定义和用法相似。在Shell
中定义或赋值一个 变量:
itcast$ VARNAME=value。
注意
等号两边都不能有空格
,否则会被Shell
解释成命令和命令行参数。
一个变量定义后仅存在于当前Shell
进程,它是
本地变量
,用export
命令可以把本地变量导出为环境变量,定义和导出环境变量通常可以一步完成:
eg1:用export命令把本地变量导出为环境变量。
itcast$ export VARNAME=value
eg2:
用
unset
命令可以
删除
已定义的环境变量或本地变量。
itcast$ unset VARNAME
2.2、文件名代换(
Globbing
)
通配符
(Wildcard
),如:*? []
具体如下:
*
匹配0
个或多个任意字符
?
匹配一个任意字符
[
若干字符]
匹配方括号中任意一个字符的一次出现
itcast$ ls /dev/ttyS*
itcast$ ls ch0?.doc
itcast$ ls ch0[0-2].doc
itcast$ ls ch[012] [0-9].doc
2.3、命令代换(
Globbing
)
(1)``
eg:
itcast$ DATE=`date`
itcast$ echo $DATE
(2)$()
eg:
itcast$ DATE=$(date)
2.4、算术代换
注意:
+-*/
和
()
运算符,并且只能做整数运算
(1)$(()) 或者 $[]
eg
itcast$ VAR=45
itcast$ echo $(($VAR+3)) 等价于 echo $[VAR+3]或 $[$VAR+3]
(2)$[base#n],其中 base表示进制,n按照 base 进行解释,后面再有运算数,
按十进制解释。
echo $[2#10+11]
echo $[8#10+11]
echo $[16#10+11]
2.5、变量替换
变量替换可以根据变量的状态(是否为空、是否定义等)来改变它的值
可以使用的变量替换形式:
形式 | 说明 |
${var} | 变量本来的值 |
${var:-word} | 如果变量 var 为空或已被删除(unset),那么返回 word,但不改变 var 的值。 |
${var:=word} | 如果变量 var 为空或已被删除(unset),那么返回 word,并将 var 的值设置为 word。 |
${var:?message} | 如果变量 var 为空或已被删除(unset),那么将消息 message 送到标准错误输出,可以用来检测变量 var 是否可以被正常赋值。 若此替换出现在Shell脚本中,那么脚本将停止运行。 |
${var:+word} | 如果变量 var 被定义,那么返回 word,但不改变 var 的值。 |
2.6、转义字符
和
C
语言类似,
\
在
Shell
中被用作转义字符,用于去除紧跟其后的单个字符的特殊意义(回车除外),换句话说,紧跟其后的字符取字面值。
2.7、单引号
和C
语言同,Shell
脚本中的单引号和双引号一样都是字符串的界定符(双引号下一节介绍),而不是字符的界定符。单引号用于保持引号内所有字符的字面值,即使引号内的\
和回车也不例外,但是字符串中不能出现单引号。如果引号没有配对就输入回车,Shell
会给出续行提示符,要求用户把引号配上对。例如:
itcast$ echo '$SHELL'
$SHELL
itcast$ echo 'ABC\
(回车)
> DE'
(再按一次回车结束命令)
ABC\
DE
2.8、双引号
被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引号的处理方式不同
itcast$ DATE=$(date)
itcast$ echo "$DATE"
itcast$ echo '$DATE'
3、shell脚本语法
3.1、条件测试
(1)命令
test
或
[
可以测试一个条件是否成立,如果测试结果为真,
则该命令的
Exit Status
为
0
,如果测试结果为假,
则命令的
Exit Status
为
1
(注意与
C
语言的逻辑表示正好相反)。
eg、测试两个数的大小关系
itcast@ubuntu:~$ var=2
itcast@ubuntu:~$ test $var -gt 1
itcast@ubuntu:~$ echo $?
0
itcast@ubuntu:~$ test $var -gt 3
itcast@ubuntu:~$ echo $?
1
itcast@ubuntu:~$ [$var -gt 3]
itcast@ubuntu:~$ echo $?
1
(2)【】
传给命令的各参数之间应该用空格隔开,比如:[$VAR
、-gt
、3
、]
是[
命令的四个参数,它们之间必须用
空格隔开
。命令test
或[
的参数形式是相同的,只不过test
命令不需要]
参数。以[
命令为例, 常见的测试命令如下表所示:
[ -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
语言类似,测试条件之间还可以做与、或、非逻辑运算:
[ EXPR1 -a EXPR2 ] EXPR1
和EXPR2
可以是上表中的任意一种测试条件,-a
表示“逻辑与”
[ EXPR1 -o EXPR2 ] EXPR1
和EXPR2
可以是上表中的任意一种测试条件,-o
表示“逻辑或”
eg:
$ VAR=abc
$ [ -d Desktop -a $VAR = 'abc' ]
$ echo $?
0
注意
,如果上例中的$VAR
变量事先没有定义,则被Shell
展开为空字符串,会造成测试条件的语法错误(展开为[ -d Desktop -a =
‘abc
’ ]
),
作为一种好的
Shell
编程习惯
,
应该总是把变量取值放在双引号之中
(展开为[ -d Desktop -a
“” =
‘abc
’ ]
):
3.2、分支
3.2.1、if/then/elif/else/fi
(1)
“
:
”
是一个特殊的命令,称为空命令,该命令不做任何事,但
Exit Status
总是真。此外,也可以执行
/bin/true
或
/bin/false
得到真或假的
Exit Status
。
eg:
#! /bin/sh
if [ -f /bin/bash ]
then
echo "/bin/bash is a file"
else
echo "/bin/bash is NOT a file"
fi
if :; then echo "always true"; fi
(2)read 命令
的作用是等待用户输入一行字符串,将该字符串存到一个
Shell
变量中。
eg:
#! /bin/sh
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."
exit 1
fi
exit 0
3.2.2、case/esac
Shell
脚本的case
可以匹配字符串和Wildcard
,
每个匹配分支可以有若干条命令,末尾必须以
;;
结束
,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac
之后,不需要像C
语言一样用break
跳出。
eg:
#! /bin/sh
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]*)
echo "Good Afternoon!";;
*)
echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
exit 1;;
esac
exit 0
3.3、循环
3.3.1、for/do/done
eg:
将当前目录下的
chap0
、
chap1
、
chap2
等文件名改为
chap0~
、
chap1~
、
chap2~
$ for FILENAME in chap?
do
mv $FILENAME $FILENAME~
done
也可以这样写:
$ for FILENAME in `ls chap?`
do
mv $FILENAME $FILENAME~
done
3.3.2、
while/do/done
eg1:
写一个验证密码的脚本
#! /bin/sh
echo "Enter password:"
read TRY
while [ "$TRY" != "secret" ]
do
echo "Sorry, try again"
read TRY
done
eg2:
循环10次
#! /bin/sh
COUNTER=1
while [ "$COUNTER" -lt 10 ]
do
echo "Here we go again"
COUNTER=$(($COUNTER+1))
done
3.3.3、
break
和
continue
(1)break[n]
可以指定跳出几层循环;
(2)continue
跳过本次循环,但不会跳出循环。
即break
跳出,continue
跳过。
3.3.4、
until循环
until 循环格式为:
until commanddo Statement(s) to be executed until command is truedone
command 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。
eg1
,使用 until 命令输出 0 ~ 9 的数字:
- #!/bin/bash
- a=0
- until[!$a -lt10]
- do
- echo$a
- a=`expr$a+1`
- done
3.4、
位置参数和特殊变量
(1)$0
相当于C
语言main
函数的argv[0]
(2)$1
、$2...
这些称为位置参数(Positional Parameter
),相当于C
语言main
函数的argv[1]
、argv[2]...
(3)$#
相当于C
语言main
函数的argc - 1
,注意这里的#
后面不表示注释
(4)$@
表示参数列表"$1" "$2" ...
,例如可以用在for
循环中的in
后面。
(5)$*
表示参数列表"$1" "$2" ...
,同上
(6)$?
上一条命令的Exit Status
(7)$$
当前进程号
$* 和 $@ 的区别
$* 和 $@ 都表示传递给函数或脚本的所有参数,不被双引号(" ")包含时,都以"$1" "$2" … "$n" 的形式输出所有参数。
但是当它们被双引号(" ")包含时,"$*" 会将所有的参数作为一个整体,以"$1 $2 … $n"的形式输出所有参数;"$@" 会将各个参数分开,以"$1" "$2" … "$n" 的形式输出所有参数。
4、输入输出
4.1、echo
echo [option] string
-e
解析转义字符
-n
不回车换行。默认情况echo
回显的内容后面跟一个回车换行。
eg
:
echo "hello\n\n"
echo -e "hello\n\n"
echo "hello"
echo -n "hello"
4.2、管道
可以通过
|
把一个命令的输出传递给另一个命令做输入。
eg:
cat myfile | more
ls -l | grep "myfile"
df -k | awk '{print $1}' | grep -v "
文件系统"
df -k
查看磁盘空间,找到第一列,去除“文件系统”,并输出
4.3、tee
tee
命令把结果输出到标准输出,另一个副本输出到相应文件。
eg1:
df -k | awk '{print $1}' | grep -v "
文件系统" | tee a.txt
tee -a a.txt
表示追加操作。
eg2:
df -k | awk '{print $1}' | grep -v "
文件系统" | tee -a a.txt
4.4、文件重定向
cmd > file
把标准输出重定向到新文件中
cmd >> file
追加
cmd > file 2>&1
标准出错也重定向到1
所指向的file
里
cmd >> file 2>&1
cmd < file1 > file2
输入输出都定向到文件里
cmd <&fd
把文件描述符fd
作为标准输入
cmd >&fd
把文件描述符fd
作为标准输出
cmd <&-
关闭标准输入
命令 | 说明 |
command > file | 将输出重定向到 file。 |
command < file | 将输入重定向到 file。 |
command >> file | 将输出以追加的方式重定向到 file。 |
n > file | 将文件描述符为 n 的文件重定向到 file。 |
n >> file | 将文件描述符为 n 的文件以追加的方式重定向到 file。 |
n >& m | 将输出文件 m 和 n 合并。 |
n <& m | 将输入文件 m 和 n 合并。 |
<< tag | 将开始标记 tag 和结束标记 tag 之间的内容作为输入。 |
5、函数
原形
foo()
{
参数: $0 $1 ...
return ExitStatus
}
eg1:
带有return语句的函数:
- #!/bin/bash
- funWithReturn(){
- echo"The function is to get the sum of two numbers..."
- echo -n"Input first number: "
- read aNum
- echo -n"Input another number: "
- read anotherNum
- echo"The two numbers are $aNum and $anotherNum !"
- return$(($aNum+$anotherNum))
- }
- funWithReturn
- # Capture value returnd by last command
- ret=$?
- echo"The sum of two numbers is $ret !"
运行结果:
The function is to get the sum of two numbers...Input first number: 25Input another number: 50The two numbers are 25 and 50 !The sum of two numbers is 75 !
函数返回值在调用该函数后通过 $? 来获得。
eg2:
函数嵌套
- #!/bin/bash
- # Calling one function from another
- number_one () {
- echo"Url_1 is http://see.xidian.edu.cn/cpp/shell/"
- number_two
- }
- number_two () {
- echo"Url_2 is http://see.xidian.edu.cn/cpp/u/xitong/"
- }
- number_one
运行结果:
Url_1 is http://see.xidian.edu.cn/cpp/shell/Url_2 is
http://see.xidian.edu.cn/cpp/u/xitong/
像删除变量一样,删除函数也可以使用
unset
命令,不过要加上 .f 选项,如下所示:
$unset
.
f function_name
6、Shell文件包含
Shell 中包含脚本可以使用:
- . filename
或
- source filename
两种方式的效果相同,简单起见,一般使用点号(.),但是注意点号(.)和文件名中间有一空格。
例如,创建两个脚本,一个是被调用脚本 subscript.sh,内容如下:
- url="http://see.xidian.edu.cn/cpp/view/2738.html"
一个是主文件 main.sh,内容如下:
- #!/bin/bash
- ../subscript.sh
- echo$url
执行脚本:
$chomd +x main.sh./main.shhttp://see.xidian.edu.cn/cpp/view/2738.html$
注意:被包含脚本不需要有执行权限。