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 获取信息: 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
条件测试 测试文件状态 -d: 目录 -s: 文件非空 -f: 正规文件 -w: 可写 -L: 符号链接 -u: 文件有 suid 位 -r: 可读 -x: 可执行 表达式 -eq = -ne != -gt > -lt < -n 非空串 -z 空串 -le <= -ge >= 逻辑操作 -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 useradd user$number echo Add a user whose name is:user$number number=`expr $number + 1` done
进程通信 进程间通信的作用 进程间需要数据传输、资源共享和事件通知。 进程间通信的方式 管道通信(无名管道和命名管道) 信号通信 内存资源共享 消息队列 信号量
管道通信 管道是单向的、先进先出的,把一个进程的输出 和另一个进程的输入连接在一起。一个进程(写 进程)在管道的尾部写入数据,另一个进程(读 进程)在管道的头部读出数据。 管道包括无名管道和有名管道两种,前者用于父 子进程间通信,后者用于运行任意两进程通信。
无名管道 创建无名管道: pipe() #include int pipe(int filedes[2]); 成功返回 0 ,否则返回 -1 filedes 是两个文件描述符, filedis[0] 为管道读端, filedis[1] 为管道写端
无名管道 读管道 使用读文件描述符 fd[0] ,调用 read( ) 系统调用。 #include ssize_t read(int fd,void *buf,size_t nbytes); 返回:读到的字节数,若已到文件尾为 0 ,出错为 -1 。 写管道 使用写文件描述符 fd[1] ,调用 write( ) 系统调用。 #include ssize_t write(int fd,const void *buf,size_t nbytes); 返回:已写字节数,若出错为 -1 。
fork () 创建子进程 一个现有进程调用 fork 函数可以创建一个子进程。 fork 函数被调用一次但返回两次。若在子进程中则返 回 0 ,父进程中则返回子进程 ID 。 main() {int i=fork(); if(i != 0) { 父进程执行的程序段; } else if (i != -1) { 子进程执行的程序段; } 父子进程都执行的程序段; }
Wait() 阻塞自己,直到子进程死亡 进程调用 wait (),立即阻塞自己,由 wait 自动分析是 否当前进程的某个子进程已经退出,如果让它找到了 这样一个已经变成僵尸的子进程, wait 就会收集这个 子进程的信息,并把它彻底销毁后返回;如果没有找 到这样一个子进程, wait 就会一直阻塞在这里,直到 有一个出现为止
无名管道 父进程写数据,子进程读数据 先调用 pipe() 创建一个管道用于父子进程间通信,再调 用 fork() 创建一个子进程,该子进程会继承父进程所创 建的管道。必须在系统调用 fork() 前调用 pipe() 。 之后如父进程关闭写端,子进程关闭读端,就可以实 现子进程向父进程发送消息。
管道 pipe(fd) 创建一个管道( #include ), 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 C 语言源程序.c 编译器 gcc 可执行文件
实验: Shell 编程与父子进程通信
作业 Shell 编程 设计一个 shell 程序,添加一个新组为 class1 ,然后添加 属于这个组的 30 个用户,用户名的形式为 stdxx ,其 中 xx 从 01 到 30 。 父子进程间管道通信 创建一个无名管道和子进程,子进程向父进程发送消 息(如 ”This message is sent from child process” ) ,父进程在收到消息后能做出反应(打印出 ”the parent process has received the message” 和该消息 )