一、学习shell编程有什么用》
1.对shell编程的理解
shell编程就是通过shell这种新语法写出来的一个文件,这个文件称之为shell脚本。
shell脚本一般不是由开发工程师去写的,都是由运维工程师去完成,所以我们只需要懂得里面的语法就可以了。
在 linux下有一些已经写好的shell脚本 ,
ubuntu下的家目录下 : .bashrc -》每当一个用户开启一个新的终端时,就会执行这个脚本
开发板下的etc目录: profile -》每当开发板启动时,就会默认执行这个脚本
2、在linux下,如何解析命令?是谁在解析命令?
1)怎么理解shell? shell在英文中被翻译为"贝壳",贝壳里面的是珍珠,贝壳外面是用户,用户想访问珍珠,必须要通过贝壳才可以访问。
例子:
用户输入 shell解析器 内核
ls ----> ls是命令,所以需要系统来解析 ---> 将这个解析的结果传输给内核,内核就会列出所有的文件名
2)究竟shell解析器是谁?
gec@ubuntu:~$ ps -ef
PID号 父进程PID号
gec 2562 1626 0 May09 ? 00:00:50
/usr/lib/gnome-terminal/gnome-terminal-server --> 终端
gec 2569 2562 0 May09 pts/4 00:00:00 bash --> bash进程
gec 7934 2569 0 00:30 pts/4 00:00:00 ps -ef --> 命令
-------------------Terminal(终端)-------------------- -gec
@ubuntu:~$(bash进程)
ps -ef(命令) -
结论:
1)bash进程就是命令行。 所有的命令都是写在命令行上,没有命令行,命令就是无效,因为没有命令行去解析该命令。 shell解析器其实指的是命令行(因为命令行是专门用来解析命令的)。
2)为什么命令行叫bash进程? 因为命令行就是bash程序执行的结果。 因为在启动一个终端时,系统默认执行bash程序,而执行这个bash程序,就会在终端上产生一个命令。
3)把整个逻辑理清。
打开一个终端 --> 默认执行bash程序 -> 产生一个命令行(shell解析器) --> 命令写在命令行的后面 bash是终端的子进程 bash就是命令行 命令是命令行(bash)的子进程
4)bash进程在哪里?
gec@ubuntu:~$ which bash /bin/bash -> 当开启一个终端时,就会默认执行这个程序,而"gec@ubuntu:~$"就是这个程序的结果。
二、shell编程。
1、什么是shell编程? 现在我们知道命令是由命令行来解析的,我们现在将一些命令写到一个文件中,然后执行这个文件,那么shell解析器就会依次帮我们去执行里面的命令。
2、C语言程序与shell脚本有什么区别?
C语言程序 shell脚本
语法: C语言 shell语法
是否需要编译: 需要编译 不需要编译
文件后缀: xxxx.c xxxx.sh 编辑环境: linux/windows linux (因为在windows中编辑,可能会有问题)
3、C语言程序与shell脚本运行步骤?
C语言程序:
1)创建文件: touch xxx.c
2)编辑文件: gedit xxx.c
3)编译文件: gcc xxx.c -o xxx (因为gcc编译出来的文件默认是有执行权,所以不用再添加了)
4)执行文件: ./xxx
shell脚本:
1)创建文件: touch xxx.sh (因为新创建的文件是没有执行权)
2)编辑文件: gedit xxx.sh
3)添加执行权限: chmod 777 xxx.sh
4)执行脚本: ./xxx.sh
三、shell脚本中helloworld程序。 shell脚本中没有main函数,不能调用C语言函数,所以不需要包含头文件,但是一定要指明一个内容:解析器的路径。
1、怎么指明解析器? 只需要在shell脚本中的第一行写上: #!/bin/bash
2、如何输出字符串到终端上? --> echo命令 -> man 1 echo
功能: echo - display a line of text //显示一行文本到终端
使用格式:
echo [SHORT-OPTION]... [STRING]... echo [参数]... [字符串]...
参数:
-n do not output the trailing newline //不会在输出字符串之后提供换行
-e enable interpretation of backslash escapes //解析字符串上的转义字符
gec@ubuntu:~$ echo -n hello
hellogec@ubuntu:~$
gec@ubuntu:~$ echo "hello\n"
hello\n
gec@ubuntu:~$ echo -e "hello\n"
hello --> 解析了\n得到一个换行
--> 默认再给一个换行 gec@ubuntu:~$
3、写程序。
1)在linux下创建一个工程文件。 touch hello.sh
echo helloworld
3)由于在共享目录下,已经默认有执行权限了,那么不需要再给了 如果在别的目录下,就需要手动添加。chmod 0777 ./hello.sh
4)执行文件。 ./hello.sh
#!/bin/bash
echo -e "GZ2119\tguanguoyuan\t192.168.19.3"
echo -e "GZ2119\nguanguoyuan\n192.168.19.3"
四、shell脚本中变量的定义。
1、shell语法变量中定义变量规则与C语言一致,只能使用数字、字母,下划线组成,不能用数字开头。
2、shell变量不需要声明数据类型,因为变量默认都是字符串类型。
C语言: int a / char b
shell: a / b -> 默认就是字符串类型
3、shell语法中对变量赋值时,等号两边不允许有空格。
C语言: int a = 100 -> 正确
int a=100 -> 正确
shell: a=hello -> 正确
a = hello -> 错误
4、shell语法中对变量的引用,需要在变量的前面添加$
C语言:
int a = 100;
printf("%d\n",a); -> 在C语言中引用变量不需要添加任何符号。
shell:
a=helloworld
echo $a -> 在shell中需要使用$来引用变量。
5、变量的种类。
1)自定义变量 -> str=hello (str就是自定义变量)
2)系统环境变量 -> 通过shell命令env来查看 (PATH就是环境变量) PATH=/home/gec/bin:/home/gec/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/local/arm/5.4.0/usr/bin
3)命令行变量 -> 类似于C语言的argc与argv
C语言: ./hello aaa bbb
argc = 3
argv[0] = "./hello"
argv[1] = "aaa"
argv[2] = "bbb"
shell: ./hello.sh aaa bbb
$#:命令行额外的参数(不算"./hello.sh"在内的参数个数) $#=2
$1:第一个额外的参数 $1=aaa $2:第二个额外的参数
$2=bbb
$*:代表所有的额外参数 $=aaa bbb
$?:最后一个shell命令执行完之后的返回值。
shell命令
执行成功: 0
执行失败: 非0
#!/bin/bash
echo $#
echo $1
echo $2
echo $*
echo $?
五、shell编程符号。
1、双引号
作用:将某一块东西变成一个值。
1)空格问题。
当一个字符串中没有空格时
echo helloworld --> 正确
echo "helloworld" --> 正确 加不加双引号都无所谓
当一个字符串中有空格时
str=hello world --> 错误
str="hello world" --> 正确 必须要添加""
2)在""中引用变量? 可以。
#!/bin/bash
a=world
str="hello $a"
echo $str
3)可不可以在""中使用一些Linux命令?
echo "today is date" -> 结果: today is date
echo "today is date
" -> 结果: today is Mon May 10 02:15:54 PDT 2021
echo 'today is date
' -> 结果: today is date
2、单引号。
作用: 将单引号括起来的内容看做是一个字符串。
3、反引号。
作用: 把双引号中的命令标识出来。
六、字符串处理。
1、计算字符串中字符的个数。
str=ashdvasvdahsvdhasvbdasbdfysvdytfbsdfuyvstdfartsvd
echo "${#str}" -> 49
2、删除字符串左边/右边的内容。
常见shell通配符:(通用匹配符号)
*: 代表任意长度的任意字符。
?: 代表一个长度的任意字符。
[a-z] 代表一个长度的a-z之间的字符。
[az] 代表一个长度、只能匹配a/z字符。
[az] 代表一个长度、只能匹配除了a/z的字符。
#: 从左到右尽可能匹配少的字符。
##: 从左到右尽可能匹配多的字符。
%: 从右到左尽可能匹配少的字符。
%%: 从右到左尽可能匹配多的字符。
1)删除字符串左边的字符,
例如: str=hello320abc20world
echo "${str#20}" --> 从左到右尽可能少地去删除"20"这种东西。 --> 结果: abc20world
echo "${str##20}" --> 从左到右尽可能多地去删除"20"这种东西。 --> 结果: world
echo "${str##[ac]20}" --> 从左到右尽可能多地去删除"[ac]20"这种东西。 --> 结果: world
a20/c20
echo "${str##[^ac]20}" --> 从左到右尽可能多地去删除"ac20"这种东西。--> 结果: abc20world
*(一个不是a/c的字符)20
2)删除字符串右边的一些字符,
例如:
str=hello320abc20world
echo "${str%%20}" --> 从右到左尽可能多地去删除20这种东西 --> 结果: hello3
echo "${str%20}" --> 从右到左尽可能少地去删除20这种东西 --> 结果: hello320abc
七、测试语句。 1、什么是测试语句? 其实测试语句就是可以比较两个值(整型数据/字符串)是否相等/大于/小于/一致。 类似于C语言中的== != > >=
2、测试语句需要使用一个命令。 --> test -> man 1 test 功能: test - check file types and compare values //检查文件类型以及比较一些值
使用格式: test EXPRESSION 等价于 [ EXPRESSION ] test 判断表达式 等价于 [ 判断表达式 ]
判断表达式有以下这些:
STRING1 = STRING2 the strings are equal //判断两个字符串是否相等
STRING1 != STRING2 the strings are not equal //判断两个字符串是否不相等
INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2 //判断两个整型数据是否相等
INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2 //判断第一个整型数据是否大于/等于第二个整型数据
INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2 //判断第一个整型数据是否大于第二个整型数据
INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2 //判断第一个整型数据是否小于/等于第二个整型数据
INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2 //判断第一个整型数据是否小于第二个整型数据
INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2 //判断第一个整型数据是否不等于第二个整型数据
-e FILE FILE exists //判断文件是否存在
-r FILE FILE exists and read permission is granted //判断文件是否存在,并且可读。
-w FILE FILE exists and write permission is granted //判断文件是否存在,并且可写。
-x FILE FILE exists and execute (or search) permission is granted //判断文件是否存在,并且可执行。
八、分支语句。 -- if-else分支
C语言: if-else分支 switch分支
shell: if-else分支 case分支
1、shell中if-else分支的框架。
框架:
if 判定条件1(测试语句)
then
xxxx
elif 判定条件2(测试语句)
then yyyy
else -> 既不满足条件1,也不满足条件2
zzzz
fi
2、例题: 判断命令行额外的参数是不是为2个,如果不是2个,则输出一个字符串来报错。
$#
#!/bin/bash
if test $# -ne 2
then
echo "input arg error!"
fi
#!/bin/bash
if [ $# -ne 2 ]
then
echo "input arg error!"
fi
3、注意事项。
1)测试语句[]两边都必须有空格。
2)每一个if语句都会以fi作为结束标志。
3)if后面的判断条件必须是真(0)时,then后面的语句才会被执行。
4)else后面没有then。
练习5: 使用命令行来传递文件名,例如:./xxx.sh 1.txt,如果额外的参数不等于1个,则输出字符串来报错。判断该文件是否存在并可读,如果存在并且可读,那么在终端上 输出文件的内容。如果只是存在,但是不可读,那么就添加读的权限之后,再输出文件的内容,如果文件不存在,则输出"file not exists"这个字符串来报错。
1)请使用shell编程来完成这个题目。
#!/bin/bash
if test $# -ne 1
then
echo "input arg error"
exit
fi
if [ -r $1 ]
then
cat $1
elif [ -e $1 ]
then
chmod 777 $1
cat $1
else
echo "file not exists"
fi
2)请使用C语言来完成这个题目。
#include "head.h"
int main(int argc, char *argv[]) // ./p5 1.txt
{
if (argc != 2)
{
printf("input arg error!\n");
exit(-1);
}
char buf[100];
bzero(buf, sizeof(buf));
if (access(argv[1], R_OK) == 0) //存在并可读
{
sprintf(buf, "cat %s", argv[1]);
system(buf);
}
else if (access(argv[1], F_OK) == 0) //存在
{
sprintf(buf, "chmod 777 %s", argv[1]);
system(buf);
bzero(buf, sizeof(buf));
sprintf(buf, "cat %s", argv[1]);
system(buf);
}
else
{
printf("file not exists!\n");
}
return 0;
}
九、分支结构 -- case分支。
1、case分支框架。
C语言swtich分支框架:
swtich(变量) {
case x: xxxx; break;
case y: yyyy; break;
default: zzzz; break;
}
shell中case分支框架:
case $变量 in
x) xxxx;;
y) yyyy;;
*) zzzz;;
esac
2、拓展: 从键盘中获取数据。
C语言:
char A[10];
scanf("%s",A);
shell:
read a -> 阻塞从键盘中获取内容,然后存放在变量a中。
3、例题: 执行程序后,从键盘中获取一个值,如果该值为1,则打印one,如果该值为10,就打印ten,如果都不是,则打印error。
#!/bin/bash
read a
case $a in
1) echo "one";;
10) echo "ten";;
*) echo "error";;
esac
练习6: 假设有一个文件,名字叫test.txt,里面的内容是10。
现在要求你写一个脚本,来判断文件的内容。
内容是10,则打印large。
内容是5,则打印middle。
内容是1,则打印small。
如果都不是,则打印error。
1)请使用shell编程来完成这个题目。
#!/bin/bash
val="`cat $1`"
case $val in
10) echo "large";;
5) echo "mid";;
1) echo "small";;
*) echo "error";;
esac
十、循环结构。 -- while循环。
C语言:for循环、while循环、do-while循环。
shell:for循环、while循环、until循环。
1、while循环的框架。
C语言:
while(判定条件) { //循环体 ...; }
shell语言:
while 判定条件(测试语句)
do
//循环体 ....
done
2、在shell中处理整型数据。
str -> 默认是字符串类型。
#!/bin/bash
str=100
str=$str+1
echo $str
结果:100+1
declare -i str=100 -> 指定str是整数。
#!/bin/bash
declare -i str=100
str=$str+1
echo $str
结果:101
练习7: 使用while循环来打印1~100的值。
#!/bin/bash
declare -i n=1
while [ $n -le 100 ]
do
echo $n
n=$n+1
done
练习8: 研究while循环的死循环怎么写?
#!/bin/bash
declare -i n=1
while [ $n -eq 1 ]
do
n=1
done
十一、循环结构 --- until循环。
until循环与while循环非常相似。
while循环 -> 当xxx条件成立时,就做循环体的内容,当xxx条件不成立时,就不做循环体的内容。
until循环 -> 当xxx条件不成立时,就做循环体的内容,当xxx条件成立时,就不做循环体的内容。
until循环框架:
until 判定条件(测试语句)
do
//循环体 ....
done
练习9: 使用until循环打印1~100的值。
#!/bin/bash
declare -i n=1
until [ $n -gt 100 ]
do
echo $n
n=$n+1
done
练习10: 研究until循环的死循环。
#!/bin/bash
declare -i n=1
until [ $n -gt 100 ]
do
n=1
done
十二、循环结构 --> for循环
1、for循环框架。
for 变量 in 内容(使用空格分开)
do
//循环体
done
2、举例子。
#!/bin/bash
str="hello world apple tree"
for n in $str //每次循环,n就去str中取一个值。
do
echo $n
done
运行结果: gec@ubuntu:/mnt/hgfs/GZ2119/05 shell编程/code$ ./for.sh
hello
world
apple
tree
十三、shell函数。
1、在shell中,函数与C语言函数非常相似,都是用于封装内容,但是使用方式要比C语言更加简单。 C语言例子:
int fun(char *p1,char *p2) -> 形式参数
{
printf("p1 = %s\n",p1); //hello -> 调用形式参数
printf("p2 = %s\n",p2); //world
return 10; -> 使用return语句来返回一个值
}
int main(int argc,char *argv[])
{ i
nt ret;
ret = fun("hello","world");
printf("ret = %d\n",ret); //10
return 0;
}
2、shell的函数是怎么写的? 1)shell中函数形式参数怎么写?如何调用形式参数?以及返回值给调用的地方?
fun() -> 在括号中不需要写任何的东西
{
-> 函数体
-> $1 -> 第一个形式参数
-> $2 -> 第二个形式参数
-> $* -> 所有的形式参数
return 10 -> shell也是使用return来返回函数的返回值
}
2)如何调用函数?
fun hello world -> 可以理解为等价于执行了一条命令。
3)查看返回值?
只需要在函数调用之后,查看$?的值就可以了。
3、 使用shell编程来完成上述的代码。
#!/bin/bash
fun() {
for s in $*
do
echo $s
done
return 10
}
fun hello world
echo $?
注意: 什么时候$1是命令行的第一个参数,什么时候是第一个形式参数?
fun() {
$1 -> 第一个形式参数
$2 -> 第二个形式参数
$* -> 所有的形式参数
}
$1 -> 命令行第一个额外的参数
$2 -> 命令行第二个额外的参数
$* -> 命令行所有额外的参数
十四、trap命令。 --> man 1 trap
功能: trap — trap signals
捕捉信号
例题: 假设某一个正在运行的脚本收到SIGINT信号时,就执行一个函数。
#!/bin/bash
fun() {
echo "helloworld"
return 0;
}
trap fun SIGINT
declare -i n=0
while [ $n -eq 0 ]
do
n=0
done