Linux项目——仿写bash程序

功能

  • 打印提示符信息:[当前用户名@主机名 当前工作目录名] 标识符($ #)
  • 获取用户的命令:命令名称 选项 参数
  • 对命令做分类:
    1)内置命令:exit cd
    2)外置命令:单独的程序,由bash创建的子进程运行/bin下的可执行文件
  • 创建子进程:子进程替换用户输入的命令对应的可执行文件

源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#include <sys/types.h>
#include <sys/utsname.h>
#include <pwd.h>
#include <signal.h>

#define NUM 20
#define LENGTH 128

char OLDPWD[LENGTH] = { 0 }; // 保存上一次的工作路径

//输出提示符
void PrintfTag()
{
	//[用户名@主机名 当前工作目录]符号
	
	char flag = '$';    //普通用户
	if(getuid() == 0)   //root用户
	{
		flag = '#';
	} 

	//getuid    返回当前用户的uid
	//getpwuid  根据uid返回用户信息(返回类型为结构体指针)
	struct passwd *pw = getpwuid(getuid());
	assert(pw != NULL);

	//获取主机信息
	struct utsname hostname;
	uname(&hostname);

	//getcwd 获取当前工作目录的绝对路径
	char path[LENGTH] = { 0 };
	getcwd(path, LENGTH - 1);

	char *dirName = NULL;
	if(strcmp(path, pw->pw_dir) == 0) //判断当前路径是不是家目录
	{
		dirName = "~";
	}
	else if(strlen(path) == 1)        //判断路径是不是根目录
	{
		dirName = "/";
	}
	else
	{
		dirName = path + strlen(path);
		while(*dirName != '/')
		{
			dirName--;
		}
		dirName++;
	}

	printf("[%s@%s %s]%c", pw->pw_name, hostname.nodename, dirName, flag);
}

//按空格切割命令字符串
void CutCommandString(char *cmd, char *Argv[])
{
	// cmd: "ls -l /bin"
	// Argv数组的每一个指针指向一个切割后的字符串
	int index = 0;
	char *p = strtok(cmd, " ");
	while(p != NULL)
	{
		Argv[index++] = p;
		p = strtok(NULL, " ");
	}

	//验证切割是否成功
	/*
	int i = 0;
	for(; i < index; i++)
	{
		printf("%s\n", Argv[i]);
	}
	*/
}

//实现cd命令
void AchieveCd(char *path)
{
	/*
	 * cd 路径---> int chdir(const char *path);成功返回1,失败返回0
	 * cd / cd ~
	 * cd -
	*/

	// 获取当前工作目录的绝对路径
	char nowPath[LENGTH] = { 0 };
	getcwd(nowPath, LENGTH - 1);

	if(path == NULL || strncmp(path, "~", 1) == 0)  //切换到家目录或家目录下的目录
	{
		struct passwd *pw = getpwuid(getuid());
		path = strcat(pw->pw_dir, path + 1);
	}
	
	if(strncmp(path, "-", 1) == 0) //切换到上一次所在目录
	{
		if(strlen(OLDPWD) == 0)
		{
			printf("mybush: OLDPWD no set\n");
			return;
		}
		path = OLDPWD;
	}

	//切换目录
	if(-1 == chdir(path))
	{
		perror(path);
		return;
	}

	//保存切换之前的路径到OLDPWD中
	memset(OLDPWD, 0, LENGTH);
	strcpy(OLDPWD, nowPath);

	//测试代码
	/*
	memset(nowPath, 0, LENGTH);
	getcwd(nowPath, LENRTH - 1);
	printf("%s\n", nowPath);
	*/
}

//分类命令,并且处理内置命令
int DealBuiltInCmd(char *Argv[])
{
	// Argv[0]: 用户输入的命令
	if(strncmp(Argv[0], "exit", 4) == 0)
	{
		exit(0);  //结束mybush
	}
	if(strncmp(Argv[0], "cd", 2) == 0)
	{
		AchieveCd(Argv[1]); // 切换到指定的工作目录
		return 1;
	}

	return 0;
}

//信号处理函数
void SignBack(int sign)
{
	wait(NULL);
}

//创建子进程
void CreatChild(char *Argv[])
{
	// 子进程结束后会向父进程发送SIGCHLD
	signal(SIGCHLD, SignBack);

	pid_t pid = fork();
	assert(pid != -1);

	if(pid == 0)
	{
		//默认去/bin下搜索可执行文件
		char path[LENGTH] = "/bin/";     //必须是绝对路径
		if(strstr(Argv[0], "/") != NULL) //判断用户给定的命令是否有路径
		{
			memset(path, 0, LENGTH);
			strcpy(path, Argv[0]);
		}
		else
		{
			strcat(path, Argv[0]);
		}

		execv(path, Argv);
		perror(path);
		exit(0);  //防止子进程exec替换失败
	}
	else // 后台运行
	{
		int i = NUM - 1;
		for(; i >= 0; i--)
		{
			if(Argv[i] != NULL)
			{
				break;
			}
		}
		if(strcmp(Argv[i], "&") != 0)	
		{
			wait(NULL);
		}
	}
}


int main()
{
	while(1)
	{
		//1. 输出提示符信息
		PrintfTag();

		//2. 获取用户输入命令  字符串
		char cmd[LENGTH] = { 0 };
		fgets(cmd, LENGTH - 1, stdin);  //从标准输入将字符串写入cmd  遇到回车结束,并且将回车符也获取进来
		cmd[strlen(cmd) - 1] = 0;       //将回车符置0

		//3. 字符串的切割 按照空格将命令与选项和参数分开
		char *Argv[NUM] = { 0 };        //指针数组,存储分割后的各个字符串
		CutCommandString(cmd, Argv);

		//4. 分类命令  1.内置(cd exit)  2.外置
		if(DealBuiltInCmd(Argv))        //如果DealBuiltInCmd函数处理的是内置命令,则程序返回while第一句,否则继续执行
		{
			continue;
		}
		
		//5. 创建子进程,子进程替换外置命令,父进程wait等待子进程执行命令结束
		CreatChild(Argv);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值