讲师中心 微信公众号

工具推荐

视频效率加速
Seko
Seko

AI短剧一键生成成片

Laper
Laper

剧本策划到成片协作

AniShort
AniShort

AI短剧协同制作平台

剧云
剧云

中文短剧剧本制作平台

初识Linux · 自主Shell编写

星强君_1026

星强君_1026

发布时间:2025-06-19 16:27:03

|

465人浏览过

|

来源于php中文网

原创

本文介绍了自主shell编写的过程,模拟实现了bash解释器,并详细讲解了所需的预备知识,如进程的多方面知识。接下来,我们将直接进入shell编写部分。

1 命令行解释器部分

初识Linux · 自主Shell编写

我们在Centos版本下进行演示,通常看到的命令行解释器显示为当前用户名(如_lazy)、主机名(如VM-12-14-centos)和当前目录(如~)。我们的目标是复制一个类似的命令行解释器。

首先,如何获取用户名、主机名和当前目录?我们可以通过环境变量来获取这些信息。

初识Linux · 自主Shell编写初识Linux · 自主Shell编写初识Linux · 自主Shell编写

在环境变量表中,我们可以看到HOSTNAME、PWD和USER分别代表主机名、当前路径和当前用户名。我们可以通过三种方式获取这些变量:environ、命令行参数表和getenv。我们选择使用getenv来获取这些信息:

char* argv[] = {
    getenv("HOSTNAME"),
    getenv("USER"),
    getenv("PWD")
};

将获取到的环境变量放在数组argv中,然后进行打印。我们使用printf来打印数组的三个元素,但由于命令行参数是在后面输入的,我们不能使用\n作为结束符。相反,我们使用snprintf函数来将所有输出放入一个字符串中,这样更容易控制输出。如果你的man手册配置不全,可以使用以下命令:

初识Linux · 自主Shell编写初识Linux · 自主Shell编写

因此,第一部分的临时代码如下:

void OutputBash()
{
    char line[SIZE];
<pre class="brush:php;toolbar:false;">char* username = GetUser();
char* hostname = Gethost();
char* cwd = Getcwd();

snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, cwd);
printf("%s", line);
fflush(stdout);

// char* argv[] = {
//     getenv("HOSTNAME"),
//     getenv("USER"),
//     getenv("PWD")
// };
// char* line;
// //printf("[%s@%s %s]>", argv[0], argv[1], argv[2]);
// fflush(stdout);

}

相关函数的定义如下:

#define SIZE 512</p><p>char<em> GetUser()
{
char</em> user = getenv("USER");
if(user == NULL) return NULL;
return user;
}</p><p>char<em> Gethost()
{
char</em> host = getenv("HOSTNAME");
if(host == NULL) return NULL;
return host;
}</p><p>char<em> Getcwd()
{
char</em> cwd = getenv("PWD");
if(cwd == NULL) return NULL;
return cwd;
}

但这只是临时的,因为我们的pwd显示不完善:

初识Linux · 自主Shell编写

目前显示的并不是最完善的,应该只显示当前目录。我们可以通过分割字符串来解决这个问题,使用指针指向最后一个/,但不建议使用函数,因为需要二级指针。我们可以使用宏来实现:

初识Linux · 自主Shell编写

我们可以使用以下方法操作:

初识Linux · 自主Shell编写

这样,较为完善的命令行解释器部分就完成了。

2 获取用户命令行参数

我们已经解决了第一个问题,现在需要获取用户的命令行参数。在获取用户命令行参数时,我们应该使用什么函数呢?可以使用scanf吗?如果使用scanf,输入ls -l -n -a时,只能获取到ls,因为scanf是通过空格或换行符来获取的。因此,我们推荐使用fgets或gets,但由于后面有文件IO操作,我们选择使用fgets作为缓冲:

Linux Omniscient
Linux Omniscient

>全知全能技能Linux版。

下载
int GetUserCommand(char<em> usercommand, size_t n)
{
char</em> s = fgets(usercommand, n, stdin);
if(s == NULL) return -1;</p><pre class="brush:php;toolbar:false;">return strlen(s);

}

但此代码存在一些缺陷,详见第4部分。

3 命令行参数进行分割

获取命令后,不能带空格执行,因此我们需要使用函数将命令行参数分割。我们使用C语言的库函数strtok来进行分割:

初识Linux · 自主Shell编写

第一个参数是待分割的字符串,第二个参数是分割符。第一次分割后,将第一个参数置为NULL以继续分割。我们将分割后的字符串放入数组中,方便后面的进程替换工作。我们定义一个全局变量来存储分割后的字符串:

#define SEP " "
char<em> gArgv[SIZE];

需要注意的是,使用单引号的空格与strtok不匹配,因为这只是一个字符,不是const char。

void SplitCommand(char* usercommand)
{
gArgv[0] = strtok(usercommand, SEP);
int index = 1;
while((gArgv[index++] = strtok(NULL, SEP)));//分割之后函数返回NULL 恰好作为结尾
}

分割后的函数返回NULL,可以作为数组的结束标志。

4 执行命令

现在我们可以直接执行命令了,至少目前可以执行简单的命令。我们需要涉及进程程序替换,因为分割好的命令已经放在了全局变量中,我们可以直接创建函数:

void ExcuteCommand()
{
pid_t id = fork();</p><pre class="brush:php;toolbar:false;">if(id < 0)
{
    perror("fork error");
    return;
}
else if(id == 0)
{
    execvp(gArgv[0], gArgv);
    perror("execvp error");
    exit(-1);
}
else
{
    int status;
    waitpid(id, &status, 0);
    int lastcode = WEXITSTATUS(status);
    if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode);
}

}

这些代码是进程替换时介绍过的,我们只需进行一些修饰,使代码更美观。然而,执行时仍会遇到问题,因为命令行输入时会自动输入回车,导致无法执行。我们需要去掉回车:

初识Linux · 自主Shell编写

使用宏定义ZERO来解决这个问题。

5 判断命令是否为内建命令

如果我们执行的是echo、cd等内建命令,只能由父进程执行,我们不能创建子进程。我们需要判断是否为内建命令,如果是,则由父进程执行,并跳过下一步。我们可以通过strcmp来判断内建命令:

void Cd()
{
const char *path = gArgv[1];
if(path == NULL) path = Gethost();
// path 一定存在
chdir(path);</p><pre class="brush:php;toolbar:false;">// 刷新环境变量
char temp[SIZE*2];
getcwd(temp, sizeof(temp));
snprintf(cwd, sizeof(cwd), "PWD=%s", temp);
putenv(cwd); // OK

}

int IsInorder() { int yes = 0; const char *enter_cmd = gArgv[0]; if(strcmp(enter_cmd, "cd") == 0) { yes = 1; Cd(); } else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0) { yes = 1; printf("%d\n", lastcode); lastcode = 0; } return yes; }

以cd为例,判断cd是内建命令后,在cd函数中实现。我们使用chdir函数改变当前工作目录,之后更新环境变量中的PATH。此时,自主Shell编写工作基本完成。

感谢阅读!

热门AI工具

更多
蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

超级麦吉

一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

PixTV
PixTV Hot

PixTV是一款面向AIGC内容创作的AI视频生成工具。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

569

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

fgets在c语言中的用法
fgets在c语言中的用法

本专题整合了c语言中fgets用法介绍,阅读专题下面的文章了解更多详细内容。

153

2025.08.27

printf用法大全
printf用法大全

php中文网为大家提供printf用法大全,以及其他printf函数的相关文章、相关下载资源以及各种相关课程,供大家免费下载体验。

948

2023.06.20

fprintf和printf的区别
fprintf和printf的区别

fprintf和printf的区别在于输出的目标不同,printf输出到标准输出流,而fprintf输出到指定的文件流。根据需要选择合适的函数来进行输出操作。更多关于fprintf和printf的相关文章详情请看本专题下面的文章。php中文网欢迎大家前来学习。

1029

2023.11.28

c语言const用法
c语言const用法

const是关键字,可以用于声明常量、函数参数中的const修饰符、const修饰函数返回值、const修饰指针。详细介绍:1、声明常量,const关键字可用于声明常量,常量的值在程序运行期间不可修改,常量可以是基本数据类型,如整数、浮点数、字符等,也可是自定义的数据类型;2、函数参数中的const修饰符,const关键字可用于函数的参数中,表示该参数在函数内部不可修改等等。

2458

2023.09.20

全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

5285

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

2250

2025.09.18

Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

0

2026.11.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Revel 官方 Deployment 文档
Revel 官方 Deployment 文档

共0课时 | 0人学习

Uvicorn官方部署指南
Uvicorn官方部署指南

共0课时 | 0人学习

Conan DevOps 文档
Conan DevOps 文档

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn