35 行代码实现一个简单的 shell

69 篇文章 17 订阅
26 篇文章 1 订阅

先上代码

shell.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

#define	MAXLINE	4096						/* max line length */

int main(int argc, char *argv[])
{
	char	buf[MAXLINE];
	pid_t	pid;
	int		status;

	printf("%% ");							/* print prompt (printf requires %% to print %) */
	while (fgets(buf, MAXLINE, stdin) != NULL) {
		if (buf[strlen(buf) - 1] == '\n')
			buf[strlen(buf) - 1] = '\0';	/* replace newline with NULL */

		if ((pid = fork()) < 0) {
			perror("fork error");
		} else if (pid == 0) {				/* child */
			execlp(buf, buf, (char *)0);	/* The exec() functions return only if an error has occurred. */
			fprintf(stderr, "couldn't execute: %s: %s\n", buf, strerror(errno));
			exit(127);
		} else {							/* parent */
			if ((pid = waitpid(pid, &status, 0)) < 0)
				perror("waitpid error");
			printf("%% ");
		}
	}
	exit(0);
}

编译

$ gcc shell.c -o shell

运行

$ ./shell
% date
2021年 06月 06日 星期日 01:56:28 CST
% who
liyongjun :0           2021-05-23 09:43 (:0)
liyongjun tty3         2021-05-23 20:24
liyongjun tty4         2021-05-25 00:23
liyongjun pts/2        2021-05-25 00:50 (127.0.0.1)
% ls
shell shell.c 
% ^D
$

再讲程序

  • 以上代码便实现了一个简单的 shell 程序。该程序从标准输入读取命令,然后执行这些命令。
  • 该程序使用了一个不同的提示符(%),以区别与系统自带的 shell 的提示符。
  • 第 18 行用标准 I/O 函数 fgets 从标准输入一次读取一行。当键入文件结束符(通常是 Ctrl + D)作为行的第一个字符时,fgets 返回一个 NULL 指针,于是循环停止,进程也就终止。
  • 因为 fgets 返回的每一行都以换行符终止,后随一个 NULL 字节,故用标准 C 函数 strlen 计算此字符的长度,然后用一个 NULL 字节替换换行符。这样做是因为 execlp 函数要求参数以 NULL 而不是以换行符结束。
  • 调用 fork 创建一个新进程。新进程是调用进程的复制品,我们称调用进程为父进程,新创建的进程为子进程。fork 向父进程返回新子进程的进程 ID(大于 0),对子进程则返回 0。因为 fork 创建一新进程,所以说它被调用一次(由父进程),但返回两次(分别在父进程及子进程中)。
  • 在子进程中,调用 execlp 以执行从标准输入读入的命令。这就用新的进程文件替换了子进程原先执行的程序文件。fork 和跟随其后的 exec 两者的组合就是某些操作系统所称的产生(spawn)一个新进程。在 UNIX 系统中,这两个部分相互分隔,构成两个函数。
  • 子进程调用 execlp 执行新程序文件,而父进程希望等待子进程终止,这一要求由调用 waitpid 实现,其参数指定要等待的进程(在这里,pid 参数是子进程 ID)。waitpid 函数返回子进程的终止状态(status 变量)。在此简单程序中,没有使用该值。如果需要,可以用此值准确地判定子进程是因何终止的。
  • 该程序的最主要缺陷是不能向所执行的命令传递参数,例如不能对 ls 指定目录名,只能对工作目录执行 ls 命令。为了传递参数,先要分析输入行,然后用某种约定把参数分开(很可能使用空格或制表符),然后将分隔后的各个参数传递给 execlp 函数,后面有时间的话可以继续完善。
  • 42
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 114
    评论
评论 114
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li-Yongjun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值