目录
0 程序设计语言与shell脚本
大体上,可以将程序设计语言可以分为两类:编译型语言和解释型语言。
编译型语言:
Python,C,Java等语言是编译型语言。这类语言需要预先将我们写好的源代码(source code)转换成目标代码(object code),这个过程被称作“编译”运行程序时,直接读取目标代码(object code)。由于编译后的目标代码(object code)非常接近计算机底层,因此执行效率很高,这是编译型语言的优点。
但是,由于编译型语言多半运作于底层,所处理的是字节、整数、浮点数或是其他机器层级的对象,往往实现一个简单的功能需要大量复杂的代码。例如,在C++里,就很难进行“将一个目录里所有的文件复制到另一个目录中”之类的简单操作。
解释型语言:
解释型语言也被称作“脚本语言”。执行这类程序时,解释器(interpreter)需要读取我们编写的源代码(source code),并将其转换成目标代码(object code),再由计算机运行。因为每次执行程序都多了编译的过程,因此效率有所下降。
使用脚本编程语言的好处是,它们多半运行在比编译型语言还高的层级,能够轻易处理文件与目录之类的对象;缺点是它们的效率通常不如编译型语言。不过权衡之下,通常使用脚本编程还是值得的:花一个小时写成的简单脚本,同样的功能用C或C++来编写实现,可能需要两天,而且一般来说,脚本执行的速度已经够快了,快到足以让人忽略它性能上的问题。脚本编程语言的例子有awk、Perl、Python、Ruby与Shell。
(1)一门程序设计语言有哪些必备的要素和技能
程序设计语言有3个方面的因素,即语法、语义、语用
语法:程序的结构或形式;语义:程序的含义;语用:程序与使用者的关系
使用程序设计语言编写程序需要掌握程序运行逻辑,以及变量定义、条件控制、循环控制、函数编写、代码调试等技能。
(2)这些要素和技能在shell脚本中是如果呈现出来的
-
脚本语言是介于HTML和C、C++、JAVA及C#之间的程序设计语言
-
脚本语言也使用变量和函数
(sh脚本中每一个变量都是字符串且不需要main函数,而C语言必须要有一个main函数,其变量也必须要有一个类型)
-
脚本语言一般有相应的脚本引擎来解释执行,一般需要解释器才能运行
(sh逐行读取sh脚本并直接执行,而C语言必须先编译链接,再通过主sh的子进程运行文件)
-
脚本语言一般以文本形式存在,类似一种命令
-
学习shell脚本包括如何编写sh脚本,如sh变量、sh语句、sh内置命令、常规系统命令和命令替换;需要学习sh控制语句,其中包括测试条件、for循环、while循环、do-until 循环、case语句等;学习如何编写sh函数以及使用参数调用sh函数。这些技能与程序设计语言在逻辑上共通
1 sh脚本
sh脚本是一个包含sh语句的文本文件,命令解释程序sh要执行该语句。例如,我们可以创建一个文本文件,命令解释程序sh要执行该语句。例如,我们创建文本文件mysh:
#! /bin/bash
# comment line
echo hello
使用chmod +x mysh
使其可执行。然后运行mysh。sh脚本的第一行通常以#!组合开始,通常称为shebang。当sh见到shebang时,会读取脚本所针对的程序名并调用该程序。sh有许多不同的版本,例如:Linux的bash、BSD Unix的csh和IBM AIX的ksh等。所有sh程序基本上都执行相同的任务,但它们的脚本在语法上略有不同。shebang允许主sh调用适当版本的sh来执行脚本。如果未指定shebang,它将运行默认的sh,即Linux中的/bin/bash。当bash执行mysh脚本时,将会打印hello。
2 sh脚本与C程序
二者相似但在根本上不同
sh是一个解释程序,逐行读取sh脚本文件并直接执行这些行。如果行是可执行命令且为内置命令,那么sh可直接执行。否则,它会复刻一个子进程来执行命令,并等待子进程终止后再继续,这与它执行单个命令行完全一样。
sh脚本:一个包含sh语句的文本文件,命令解释程序sh要执行该语句。
sh:一个解释程序,逐行读取sh脚本文件并直接执行这些行。变量类型:字符串。
相反,C程序必须先编译链接到一个二进制可执行文件,然后通过主sh的子进程运行二进制可执行文件。其次,在C程序中,每个变量必须有一个类型,例如char、int、float、派生类型(如struct)等。相反,在sh脚本中,每个变量都是字符串。因此不需要类型,因为只有一种类型,即字符串。最后,每个C程序必须有一个main()函数,每个函数必须定义一个返回值类型和参数。相反,sh脚本不需要main函数。
3 命令行参数
可使用与运行sh命令完全相同的参数调用sh脚本,如:mysh one two three
4 sh变量
sh有许多内置变量,除此以外用户还可以使用任何符号作为sh变量,sh所有变量值都是字符串,不需要声明。未赋值的sh变量是NULL。
定义变量时,变量名不加美元符号$,如:my_name="xy"
如果A是一个变量,则$A是变量的值
#! /bin/bash
echo A
echo $A
A="this is fun "
echo $A
B=A
echo $B
B=$A
echo $B
sh有许多特殊字符,如$、/、*、>、<等。要想把他们用作普通字符,可以使用\或单引号来引用他们。单引号内没有替换。双引号用于保留双引号字符串中的空格。
5 sh中的引号
双引号的优点:
- 双引号里可以有变量
- 双引号里可以出现转义字符
your_name="runoob"
# 使用双引号拼接
greeting="hello, "$your_name" !"
greeting_1="hello, ${your_name} !"
echo $greeting $greeting_1
# 使用单引号拼接
greeting_2='hello, '$your_name' !'
greeting_3='hello, ${your_name} !'
echo $greeting_2 $greeting_3
6 sh命令
(1)内置命令
sh有许多内置命令,这些命令由sh执行,不需要创建一个新进程。
.file:读取并执行文件。
break [n]:从最近的第n个嵌套循环中退出。
cd [dirname]:更换目录。
continue [n]:重启最近的第n个嵌套循环。
eval [arg ...]:计算一次参数并让sh执行生成的命令。
exec [arg ...]:通过这个sh执行命令,sh将会退出。
exit [n]:使sh退出,退出状态为n。
export [var ...]:将变量导出到随后执行的命令。
read [var ...]:从stdin中读取一行并为变量赋值。
set [arg ...]:在执行环境中设置变量。
shift:将位置参数$2 $3 ...重命名为$1 $2 ...。
trap [arg][n]:接收到信号n后执行参数。
umask [ddd]:将掩码设置为八进制数ddd的。
wait [pid]:等待进程pid,如果没有给出pid,则等待所有活动子进程。
(2)Linux命令
sh可以执行所有的Linux命令。其中,有些命令几乎已经成为sh不可分割的一部分。
echo命令:讲参数字符串作为行回显到stdout
expr命令:间接更改sh变量的值(数值),讲两个参数字符串转换为数字,然后对数字执行(二进制)操作op,再将得到的数字转换为字符串
管道命令:在sh脚本中经常使用管道作为过滤器。
实用命令:除了上面的Linux命令之外,sh还使用许多其他实用程序作为命令。其中包括:
awk:数据处理程序。
cmp:比较两个文件。
comm:选择两个排序文件共有的行。
grep:匹配一系列文件的模式。
diff:找出两个文件的差异。
join:通过使用相同的键来连接记录以比较两个文件。
sed:流或行编辑命令。
sort:排序或合并文件。
tail:打印某个文件的最后n行。
tr:一对一字符翻译。
uniq:从文件中删除连续重复行。
7 sh控制语句
(1)if-else-fi
if [ condition ] ; then
statements
else
statements
fi
需要注意的是,在sh中,0为TURE,而非0为FALSE。
这里我一度遇到了问题,后来发现条件表达式要放在方括号之间,并且与前后括号之间要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]
运算符-eq、-ne、-It、-gt等将参数作为整数进行比较。
除了比较字符串或数值之外,测试程序还可以测试文件操作中经常需要的文件类型和文件属性。
if [-e name ] test whether file name exists
if [ -f name ] test whether name is a(REG)file
if [-d name ] test whether name is a DIR
if [ -r name ] test whether name is readable; similarly for -w,-x,etc.
if[ f1 -ef f2 ] test whether f1,f2 are the SAME file
(2)if-elif-else-fi复合语句
if[ condition1 ]: then
commands
elif [ condition2 ] ; then
commands
else
commands
fi
复合条件:与在C语言中一样,sh也允许在符合条件中使用&&(AND)和||(OR),但是语法比C语言更加严格。条件必须用喻队匹配的双括号[[和]]括起来。
对于双分支的if语句,注意不要忘了结束语fi(finish)
(3)for语句
for VARIABLE in string1 string2 ...stringn
do
commands
done
①顺序输出当前列表中的数字
②顺序输出字符串中的字符
(4)while语句
while[ condition ]
do
commands
done
①用于不断执行一系列命令
以上实例使用了 Bash let 命令,它用于执行一个或多个表达式,变量计算中不需要加上 $ 来表示变量
②用于从输入文件中读取数据
(5)until-do语句
until 循环执行一系列命令直至条件为 true 时停止。
until [ $ANS = "give up" ]
do
echo -n "enter your answer : "
read ANS
done
问题解决:
最终代码及运行结果:
(6)case语句
case $variable in
pattern1) commands;
pattern2) commands;
patternN) commands ;
esac
case 工作方式如上所示,取值后面必须为单词 in,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
8 I/O重定向
当进入sh命令时,我们可以指示sh将I/O重定向到除默认stdin、stdout和sterr以外的文件。I/O重定向由以下形式和含义:
/> file std转向文件,如果文件不存在,将会创建文件。
/>> file stdout追加到文件。
/< file 将文件用作stdin;文件必须存在并具有r权限。
/<< word 从here文件中获取输入,知道只包含“word”的行。
9 嵌入文档
echo << END #一直按行输入并输出直到一行只剩下END
END
cat << DONE #一直按行输入输出直到DONE
DONE
10 sh函数
linux shell 可以用户定义函数,然后在shell脚本中可以随便调用。
注意:sh脚本中不需要main函数
shell中函数的定义格式如下:
[ function ] funname [()]
{
action;
[return int;]
}
(1)不带返回值的函数调用
#!/bin/bash
demoFun(){
echo "这是我的第一个 shell 函数!"
}
echo "-----函数开始执行-----"
demoFun
echo "-----函数执行完毕-----"
(2)带返回值的函数调用
#!/bin/bash
funWithReturn(){
echo "这个函数会对输入的两个数字进行相加运算..."
echo "输入第一个数字: "
read aNum
echo "输入第二个数字: "
read anotherNum
echo "两个数字分别为 $aNum 和 $anotherNum !"
return $(($aNum+$anotherNum))
}
funWithReturn
echo "输入的两个数字之和为 $? !"
11 sh中的通配符
星号通配符:sh中最有用的通配符是,可扩展到当前目录中的所有文件。
file *:列出当前目录中所有文件的信息。
ls *.c:列出当前目录中所有以.c结尾的文件。
?通配符:查询某文件名中的字符
file ???:有3个字符的所有文件名。
ls *.??:一个点号.后有2个字符的所有文件名。
[]通配符 :查询文件名中一对[中的字符。
file *[ab]*:包含字符a或b的所有文件名
ls *[xyz]*:列出所有包含x、y或z的文件名。
ls *[a-m]*:列出包含a到m范围内字符的所有文件名。