250 likes | 420 Views
Linux 环境及 Shell 程序. 操作系统实验 1. 二、 Shell 编程与进程通信. 常用 shell 命令. 文件及文件属性操作 ls 、 cp 、 mv 、 rm ln 、 ln –s 、 chmod 、 groupadd 、 useradd 输入输出操作 echo 、 cat >> 、 > 、 | 目录操作 cd 、 pwd 、 ls mkdir 、 cp 、 mv 、 rm -r 文本过滤 head 、 tail 、 grep 、 sort 、 sed 、 awk 其他 find 、 expr
E N D
Linux环境及Shell程序 操作系统实验1
常用shell命令 • 文件及文件属性操作 • ls、cp、mv、rm • ln、ln –s、chmod、groupadd、useradd • 输入输出操作 • echo 、cat • >>、>、| • 目录操作 • cd、 pwd、 ls • mkdir、 cp、 mv、 rm -r • 文本过滤 • head、 tail、 grep、sort、sed、awk • 其他 • find、expr • 获取信息: man
Shell简介 • shell是用户和内核之间的接口。为屏蔽内核复杂性,在内核周围建一个外壳(shell),用户向shell提出请求,shell将请求传给内核。Shell有多种,一般使用bash(bonurne shell)。 • Shell是一种命令解释器,支持变量、函数、控制语句等,类似于一种高级编程语言。 • 以#!/bin/sh开头,指示所用的命令解释器,#后接注释
为什么要有shell脚本 • 系统管理员每天需要进行大量且重复的管理工作,如文件路径备份、更新等。这时候可以将重复进行的操作写入一个文本文件,作为shell脚本运行。 • 如,系统管理员每天都需要运行如下命令 groupadd groupname usradd –m username1 -g groupname usradd –m username2 –g groupname -m默认方式,/home/username1
Shell脚本运行过程 • 编写shell脚本 • 使用任意编辑器(gedit、vim、nano等)编写脚本 • 添加执行权限: chmod u+x test.sh • 运行脚本: ./test.sh • 运行脚本的几种方式 • sh 脚本名:sh 只执行bash,读文件,只需r权限;开启新的儿子shell运行脚本 • ./脚本名 :当前目录下该脚本,作为可执行程序运行,需x权限,每次开启新的儿子shell运行脚本 • . 脚本名:. 是一个命令,不开启儿子shell,当前shell运行
Vim编辑器 • 三种模式 • 插入模式: 键盘按键当作文本 • 命令模式: 键盘按键当作命令 • 底行模式: 输入“:” 进入。 • 另存为 :w 新文件名 • 保存退出 :wq • 不保存退出:q!
Shell脚本示例 #!/bin/sh #This is s1 sudo groupadd g1 sudo usradd –m u1 –g g1 sudo usradd –m u2 -g g1 chmod u+x s1 ./s2 /etc/group查看组 /etc/passwd查看用户
Shell脚本示例 #!/bin/sh #This is s2 #print “Hello Shell!” echo “Hello Shell!” chmod u+x s2 ./s2
Shell的变量 • 变量名=变量值 • 不需要声明类型,直接赋值使用 • 等号后无空格,若变量值中有空格,需要引号,如a=“hello world” • 都为字符串类型,数值运算需要外部命令,expr • 变量取值:在变量名称前加“$” • 双引号“”:可引用除$、`、\外任意字符或字符串 • 单引号‘’:可引用引号里所有字符(包括引号) • $ A=a • $ echo $A 输出结果为 a • $ echo “$A” 输出结果为 a • $ echo ‘$A’ 输出结果为 $A
系统变量和特殊变量 • 系统变量 env命令查看所有环境变量 • $HOME用户主目录,~ • $PATH执行命令时所搜寻的目录 • 特殊变量 • $0这个程序的执行名字$n这个程序的第n个参数值$*这个程序的所有参数$#这个程序的参数个数$$这个进程的PID$?执行上一个指令的返回值 test.sh: #!/bin/sh echo Filename: $0 echo Arguments: $* echo Number of args.: $# echo 1st arg.: $1 echo 2nd arg.: $2 执行: ./test.sh 1 2
条件测试 • 表达式 -eq = -ne != -gt > -lt < -n 非空串 -z 空串 -le <= -ge >= • 测试文件状态 -d:目录 -s:文件非空 -f:正规文件 -w:可写 -L:符号链接 -u:文件有suid位 -r:可读 -x:可执行 • 逻辑操作 -a && -o || ! # [ -f /etc/passwd -a -f /etc/inittab ] # echo $? 0 # [ -x /etc/passwd -o -x /bin/sh ] # echo $? 0
控制语句 • if 语句 if [条件测试] then action elif [条件] then action else action fi #!/bin/sh scores=40; if [ $scores -gt 90 ] then echo "very good!" elif [ $scores -gt 80 ] then echo "good!" elif [ $scores -gt 60 ] then echo "pass!" else echo "no pass!" fi
循环语句 • for循环: for var in [list]do commands... done • while循环: while [condition]do commands... done #!/bin/sh number=1 while [ $number -le 10 ] do useradduser$number echo Add a user whose name is:user$number number=`expr $number + 1` done
进程通信 • 进程间通信的作用 进程间需要数据传输、资源共享和事件通知。 • 进程间通信的方式 • 管道通信(无名管道和命名管道) • 信号通信 • 内存资源共享 • 消息队列 • 信号量
管道通信 • 管道是单向的、先进先出的,把一个进程的输出和另一个进程的输入连接在一起。一个进程(写进程)在管道的尾部写入数据,另一个进程(读进程)在管道的头部读出数据。 • 管道包括无名管道和有名管道两种,前者用于父子进程间通信,后者用于运行任意两进程通信。
无名管道 • 创建无名管道:pipe() • #include <unistd.h> • int pipe(int filedes[2]); 成功返回0,否则返回-1 • filedes是两个文件描述符,filedis[0]为管道读端,filedis[1]为管道写端
无名管道 • 读管道 • 使用读文件描述符fd[0] ,调用read( )系统调用。 • #include <unistd.h> • ssize_t read(int fd,void *buf,size_t nbytes); • 返回:读到的字节数,若已到文件尾为0,出错为-1。 • 写管道 • 使用写文件描述符fd[1],调用write( )系统调用。 • #include <unistd.h> • ssize_t write(int fd,const void *buf,size_t nbytes); • 返回:已写字节数,若出错为-1。
fork() • 创建子进程 • 一个现有进程调用fork函数可以创建一个子进程。fork函数被调用一次但返回两次。若在子进程中则返回0,父进程中则返回子进程ID。 main() { int i=fork(); if(i != 0) { 父进程执行的程序段;} elseif (i != -1) { 子进程执行的程序段;} 父子进程都执行的程序段; }
Wait() • 阻塞自己,直到子进程死亡 • 进程调用wait(),立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一个子进程,wait就会一直阻塞在这里,直到有一个出现为止
无名管道 • 父进程写数据,子进程读数据 • 先调用pipe()创建一个管道用于父子进程间通信,再调用fork()创建一个子进程,该子进程会继承父进程所创建的管道。必须在系统调用fork()前调用pipe()。 • 之后如父进程关闭写端,子进程关闭读端,就可以实现子进程向父进程发送消息。
管道 • pipe(fd) 创建一个管道(#include <unistd.h>),fd[0]为管道的读端;fd[1]为管道的写端。管道可用来实现父进程与其子孙进程之间的通信。管道以FIFO方式传送消息。 例: • main() • { int x,fd[2]; • char buf[30],s[30]; • pipe(fd); /*创建管道*/ • while((x=fork())= = -1); /*创建子进程失败时,循环*/ • if(x = = 0) • { sprintf(buf,“This is an example\n”); • close(fd[0]); • write(fd[1],buf,30); /*把buf中的字符写入管道*/ • exit(0); } • wait(); • read(fd[0],s,30); /*父进程读管道中的字符*/ • printf(“%s”,s); • }
Linux下c编程 • 运行c文件,hello.c为例 • gcc 文件名 • ./ 运行程序 • gcc hello.c 编译+链接,得到可执行文件a.out • gcc –o hello hello.c 编译+链接,得到可执行文件hello 编译器 gcc 可执行文件 C语言源程序 .c
作业 • Shell编程 • 设计一个shell程序,添加一个新组为class1,然后添加属于这个组的30个用户,用户名的形式为stdxx,其中xx从01到30。 • 父子进程间管道通信 • 创建一个无名管道和子进程,子进程向父进程发送消息(如”This message is sent from child process”),父进程在收到消息后能做出反应(打印出”the parent process has received the message”和该消息)