Linux命令行解释程序

本文详细介绍了如何实现一个Linux命令行解释程序,包括主函数main、核心函数eval、命令行解析函数parseline、内部命令处理函数builtin_command以及信号处理函数sigchld_handler。实现了诸如pwd、ls、cat、rm、touch和exit等内部命令,并提供了myshell脚本的编写和测试过程,讨论了技术难点和解决方案,如加载和运行可执行文件、execve参数生成以及批量编译链接C程序。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  

目录

头文件

实验步骤

主函数main

确定shell的运行模式

代码

核心函数eval

分析和计算(evaluate)用户输入,执行内部命令或可执行文件

代码

命令行解析函数parseline

建立argv数组,返回命令属性(foreground / background)

代码

内部命令处理函数builtin_command

判断命令属性,运行内部命令

代码

信号处理函数sigchld_handler

保持errno不变,回收所有子进程

代码

实现内部命令

pwd:显示当前目录

ls:显示指定目录下的所有文件

cat:显示文件内容

rm:删除文件

touch:建立空文件

exit:终止当前进程(直接调用了_exit系统调用)

创建专属命令——myshell

编写myshell脚本

更改脚本权限

技术难点和解决方案

如何正确的加载和运行一个可执行文件

如何为execve系统调用生成合适的参数

如何一次性完成对数十个C程序的编译和链接

makefile文件的编写

变量OBJS

目标main

目标clean

具体内容

测试

myshell命令

前台进程

后台进程

内部命令

pwd

ls

cat

touch

rm

exit


头文件

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include <signal.h>
#include <dirent.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAXARGS	 128
#define	MAXLINE	 8192  /* Max text line length */

void eval(char* cmdline);
int builtin_command(char** argv);
int parseline(char* buf, char** argv);

void print_working_directory();
void list(char* filename);
void concatenate(char* filename);
void remove_file(char* filename);
void touch(char* filename);
void directly_systemcall_exit();

实验步骤

主函数main

确定shell的运行模式

  1. 使用signal系统调用设置SIGCHLD信号的处理函数(回收僵尸进程)
  2. 打印出提示符
  3. 从键盘(标准输入)获取一条命令
  4. 分析和运行该命令

代码

int main() {
	char cmdline[MAXLINE];

	if (signal(SIGCHLD, sigchld_handler) == SIG_ERR)
		printf("signal error\n");

	while (1) {
		printf("> ");
		fgets(cmdline, MAXLINE, stdin);
		if (feof(stdin)) exit(0);

		eval(cmdline);
	}
}

 

核心函数eval

分析和计算(evaluate)用户输入,执行内部命令或可执行文件

  1. 首先对输入的命令进行语法分析(parse),并建立argv数组
  2. 然后检查argv数组的第一项argv[0],检查其是否为内部命令(builtin command)
  3. 如果是内部命令,那么执行该命令
  4. 如果不是内部命令,那么将其视作可执行文件,调用fork系统调用创建一个子进程,然后在子进程中调用execve系统调用来为该可执行文件创建进程上下文
  5. 对于为可执行文件生成的进程,检查其为前台进程(foreground)还是后台进程(background),如果是前台进程,那么在父进程中调用waitpid系统调用来等待其终止;如果是后台进程,输出其进程号和当前命令行,回到主函数

代码

void eval(char* cmdline)
{
	char* argv[MAXARGS];	//argument list execve()
	char buf[MAXLINE];		//holds modified command line
	int bg;					//should the job run in bg or fg?
	pid_t pid;				//process id

	strcpy(buf, cmdline);
	bg = parseline(buf, argv);
	if (argv[0] == NULL) return;	//ignore empty lines

	if (!builtin_command(argv)) {
		if ((pid = fork()) == 0) {	//child runs user job
			if (execve(argv[0], argv, NULL) < 0) {
				printf("%s: Command not found.\n", argv[0]);
				exit(0);
			}
		}

		/* parent waits for foreground job to terminate */
		if (!bg) {
			int status;
			if (waitpid(pid, &status, 0) < 0)
				printf("waitfg: waitpid error\n");
		}
		else printf("%d %s", pid, cmdline);
	}
	return;
}

命令行解析函数parseline

建立argv数组,返回命令属性(foreground / background)

  1. 用空格作为分隔符,来划分用户输入的字符串,每个子串首地址作为argv数组的一项,并将argv数组尾元素的下一个位置设为NULL
  2. 检查argv数组的最后一个字符串是否为“&”,如果是,那么该命令生成一个后台进程(bg=1);否则该命令生成一个前台进程(bg=0)

代码

/* parseline - Parse the command line and build the argv array */
int parseline(char* buf, char** argv)
{
	char* delim;		//points to first space delimiter
	int argc;			//number of args
	int bg;				//background job?

	buf[strlen(buf) - 1] = ' ';		//replace trailing '\n' with space
	while (*buf && (*buf == ' ')) ++buf;	//ignore leading spaces

	/* build the argv list */
	argc = 0;
	while ((delim = strchr(buf, ' '))) {
		argv[argc++] = buf;
		*delim = '\0';
		buf = delim + 1;
		whi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值