内容目录
- 一、程序设计语言与shell脚本
- (1)一门程序设计语言有哪些必备要素和技能
- (2)这些要素和技能在shell脚本中如何呈现
- 二、sh脚本
- 三、sh脚本与C程序
- 四、命令行参数
- 五、sh变量
- 六、sh中的引号
- 七、sh命令
- (1)内置命令
- (2)linux命令
- 八、sh控制语句
- (1)if-else-fi
- (2)if-elif-else-fi复合语句
- (3)for语句
- 顺序输出当前列表中的数字
- 顺序输出字符串中的字符 - (4)while语句
-用于不断执行一系列命令
-用于从输入文件中读取数据 - (5)until-do语句
- (6)case语句
- 九、I/O重定向
- 十、嵌入文档
- 十一、sh函数
- (1)不带返回值的函数调用
- (2)带返回值的函数调用
- 十二、sh中的通配符
一、程序设计语言与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函数。这些技能与程序设计语言在逻辑上共通
二、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。
三、sh脚本与C程序
二者相似但在根本上不同
sh是一个解释程序,逐行读取sh脚本文件并直接执行这些行。如果行是可执行命令且为内置命令,那么sh可直接执行。否则,它会复刻一个子进程来执行命令,并等待子进程终止后再继续,这与它执行单个命令行完全一样。
sh脚本:一个包含sh语句的文本文件,命令解释程序sh要执行该语句。
sh:一个解释程序,逐行读取sh脚本文件并直接执行这些行。变量类型:字符串。
相反,C程序必须先编译链接到一个二进制可执行文件,然后通过主sh的子进程运行二进制可执行文件。其次,在C程序中,每个变量必须有一个类型,例如char、int、float、派生类型(如struct)等。相反,在sh脚本中,每个变量都是字符串,因此不需要类型,因为只有一种类型,即字符串。最后,每个C程序必须有一个main()函数,每个函数必须定义一个返回值类型和参数。相反,sh脚本不需要main函数。
四、命令行参数
可使用与运行sh命令完全相同的参数调用sh脚本,如:mysh one two three
五、sh变量
sh有许多内置变量,除此以外用户还可以使用任何符号作为sh变量,sh所有变量值都是字符串,不需要声明。未赋值的sh变量是NULL。
定义变量时,变量名不加美元符号$,如:my_name="xy"
如果A是一个变量,则$A是变量的值
sh有许多特殊字符,如$、/、*、>、<等。要想把他们用作普通字符,可以使用\或单引号来引用他们。单引号内没有替换。双引号用于保留双引号字符串中的空格。
六、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
七、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:从文件中删除连续重复行。
七、sh控制语句
(1)if-else-fi
if [ condition ] ; then
statements
else
statements
fi
与C语言不同的是,在sh脚本中,0为TRUE,非0为FALSE。
运算符-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,每一模式必须以右括号结束。取值可以为变量或常数,匹配发现取值符合某一模式后,其间所有命令开始执行直至 ;;。
取值将检测匹配的每一个模式。一旦模式匹配,则执行完匹配模式相应命令后不再继续其他模式。如果无一匹配模式,使用星号 * 捕获该值,再执行后面的命令。
8I/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
10sh函数
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 "输入的两个数字之和为 $? !"
11sh中的通配符
星号通配符:sh中最有用的通配符是,可扩展到当前目录中的所有文件。
file *:列出当前目录中所有文件的信息。
ls *.c:列出当前目录中所有以.c结尾的文件。
?通配符:查询某文件名中的字符
file ???:有3个字符的所有文件名。
ls *.??:一个点号.后有2个字符的所有文件名。
[]通配符 :查询文件名中一对[中的字符。
file [ab]:包含字符a或b的所有文件名
ls [xyz]:列出所有包含x、y或z的文件名。
ls [a-m]:列出包含a到m范围内字符的所有文件名。
问题解决
- gcc -Iinclude src/*.c -o bin/hello:
这是一个使用gcc编译器的命令,用于编译C语言源代码文件并生成可执行文件。
解释一下这个命令的各个部分:
gcc
:是GNU编译器集合中的C语言编译器。
-Iinclude
:表示在编译过程中搜索头文件的目录。这里指定了一个名为"include"的目录,编译器会在该目录中查找头文件。
src/*.c
:表示编译所有位于"src"目录下以".c"为扩展名的源代码文件。通配符"*"表示匹配任意文件名。
-o bin/hello
:表示生成的可执行文件的输出路径和名称。这里指定了一个名为"hello"的可执行文件,将被放置在名为"bin"的目录下。
综上所述,这个命令的作用是将"src"目录下的所有C语言源代码文件编译,并将生成的可执行文件命名为"hello",放置在"bin"目录下。同时,编译器会在"include"目录中查找头文件。
--引用自chatgpt
- gcc src/*.c -Iinclude -llib -lhello -o bin/hello
gcc
:是GNU编译器集合中的C语言编译器。
src/*.c
:表示编译所有位于"src"目录下以".c"为扩展名的源代码文件。通配符"*"表示匹配任意文件名。
-Iinclude
:表示在编译过程中搜索头文件的目录。这里指定了一个名为"include"的目录,编译器会在该目录中查找头文件。
-llib
:表示链接一个名为"lib"的库文件。这里没有指定具体的库文件名称,只给出了一个前缀"lib"。编译器会在指定的库路径中查找以"lib"开头的库文件。
-lhello
:表示链接一个名为"hello"的库文件。这里指定了具体的库文件名称为"hello"。编译器会在指定的库路径中查找名为"libhello.so"或"libhello.a"的库文件。
-o bin/hello
:表示生成的可执行文件的输出路径和名称。这里指定了一个名为"hello"的可执行文件,将被放置在名为"bin"的目录下。
综上所述,这个命令的作用是将"src"目录下的所有C语言源代码文件编译,并将生成的可执行文件命名为"hello",放置在"bin"目录下。同时,编译器会在"include"目录中查找头文件,并在指定的库路径中查找名为"libhello.so"或"libhello.a"的库文件进行链接。
--引用自chatgpt
- Gcc -c -fPIC -shared -Iinclude tmp/hello.c -o lib/libhello.so
gcc
:是GNU编译器集合中的C语言编译器。
-c
:表示只进行编译,不进行链接。这个选项会生成目标文件,而不是可执行文件。
-fPIC
:表示生成位置无关代码(Position Independent Code)。这个选项用于生成共享库文件。
-shared
:表示生成共享库文件。这个选项告诉编译器将目标文件链接为共享库文件。
-Iinclude
:表示在编译过程中搜索头文件的目录。这里指定了一个名为"include"的目录,编译器会在该目录中查找头文件。
tmp/hello.c
:表示要编译的C语言源代码文件的路径和名称。这里指定了一个名为"hello.c"的源代码文件,位于名为"tmp"的目录下。
-o lib/libhello.so
:表示生成的共享库文件的输出路径和名称。这里指定了一个名为"libhello.so"的共享库文件,将被放置在名为"lib"的目录下。
综上所述,这个命令的作用是将"tmp"目录下的名为"hello.c"的C语言源代码文件编译为位置无关的共享库文件,并将生成的共享库文件命名为"libhello.so",放置在"lib"目录下。同时,编译器会在"include"目录中查找头文件。
--引用自chatgpt
- libhello.a和libhello.so是什么文件,有什么区别
libhello.a和libhello.so都是库文件,用于在程序编译和链接过程中提供函数和符号的定义。区别如下:
静态库(libhello.a):
静态库是在编译时被链接到可执行文件中的库文件。
它包含了函数和符号的实现代码,并且这些代码会被复制到最终的可执行文件中。
静态库的文件大小较大,因为它包含了所有的实现代码。
在程序运行时,不需要依赖外部的库文件,因为所有的代码都已经被复制到可执行文件中。
静态库的使用方式是在编译时将库文件链接到可执行文件中,因此程序的可执行文件会变大。
共享库(libhello.so):
共享库是在程序运行时动态加载的库文件。
它包含了函数和符号的实现代码,但这些代码并不会被复制到最终的可执行文件中。
共享库的文件大小较小,因为它只包含了函数和符号的引用,实现代码会在运行时从库文件中加载。
在程序运行时,需要依赖外部的库文件,因为实现代码并没有被复制到可执行文件中。
共享库的使用方式是在程序运行时动态加载,因此程序的可执行文件大小不会增加。
总结来说,静态库在编译时被链接到可执行文件中,程序运行时不依赖外部库文件;而共享库在程序运行时动态加载,程序运行时需要依赖外部库文件。静态库的优点是移植性好,共享库的优点是节省空间和灵活性。选择使用哪种库文件取决于具体的需求和场景。
--引用自chatgpt
苏格拉底挑战
个人收获
- 对于sh控制语句进行了深入学习,更好地理解了sh语言与C语言的区别,sh作为解释型语言,能够轻易处理文件和目录之类的对象。
- 对于静态库和动态库的区别与实践有了较好的理解。