用C制作自己的Linux外壳

要了解有关外壳的更多信息,请单击 在这里 .

null

我们都在Ubuntu、Fedora等Linux发行版中使用内置的终端窗口,但它们实际上是如何工作的呢?在本文中,我们将处理一些 引擎盖下 在外壳中实际工作的特性和算法。所有Linux操作系统都有一个终端窗口来写入命令。但它们进入后如何正确执行? 另外,如何处理保留命令历史记录和显示帮助等额外功能?所有这些都可以通过创建自己的shell来理解。 图片[1]-用C制作自己的Linux外壳-yiteyi-C++库

基础知识

输入命令后,将执行以下操作:

  1. 输入命令,如果长度不为空,则将其保留在历史记录中。
  2. 解析:解析是将命令分解为单个单词和字符串
  3. 检查管道等特殊字符
  4. 检查是否需要内置命令。
  5. 如果 正在处理管道。
  6. 通过执行系统命令和库 分叉 孩子和呼唤 执行 .
  7. 打印当前目录名并请求下一次输入。

为了保存命令的历史记录,使用箭头键恢复历史记录,以及使用tab键处理autocomplete,我们将使用GNU提供的readline库。

实施

要安装readline库,请打开terminal窗口并写入

sudo apt-get install libreadline-dev

它会询问你的密码。输入它。在下一步中按y键。

  • 打印目录可以使用 getcwd .
  • 获取用户名可以通过以下方式完成: getenv(“用户”)
  • 解析可以通过使用 STREP(“”) .它将根据空格分隔单词。总是跳过长度为零的单词,以避免存储额外的空格。
  • 解析后,检查内置命令列表,如果存在,则执行它。如果不是,则将其作为系统命令执行。要检查内置命令,请将命令存储在字符指针数组中,并将所有命令与 strcmp() . 注意:“cd”不能使用execvp在本机工作,因此它是一个内置命令,使用 chdir() .
  • 为了执行系统命令,将创建一个新的子级,然后使用execvp执行命令,并等待它完成。
  • 检测管道也可以通过使用 STREP(“|”) 。要处理管道,请首先将命令的第一部分与第二部分分开。然后在解析每个部分后,使用execvp在两个单独的新子级中调用这两个部分。管道是指将第一个命令的输出作为第二个命令的输入传递。
    1. 声明一个大小为2的整数数组,用于存储文件描述符。文件描述符0用于读取,1用于写入。
    2. 使用pipe()函数打开管道。
    3. 创建两个孩子。
    4. 在儿童1->
      Here the output has to be taken into the pipe.
      Copy file descriptor 1 to stdout.
      Close  file descriptor 0.
      Execute the first command using execvp()
      
    5. 儿童2->
      Here the input has to be taken from the pipe.
      Copy file descriptor 0 to stdin.
      Close file descriptor 1.
      Execute the second command using execvp()
      
    6. 等这两个孩子在家长会议上结束。

// C Program to design a shell in Linux
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<readline/readline.h>
#include<readline/history.h>
#define MAXCOM 1000 // max number of letters to be supported
#define MAXLIST 100 // max number of commands to be supported
// Clearing the shell using escape sequences
#define clear() printf(" 33[H 33[J")
// Greeting shell during startup
void init_shell()
{
clear();
printf ( "******************"
"************************" );
printf ( " ****MY SHELL****" );
printf ( " -USE AT YOUR OWN RISK-" );
printf ( "*******************"
"***********************" );
char * username = getenv ( "USER" );
printf ( "USER is: @%s" , username);
printf ( "" );
sleep(1);
clear();
}
// Function to take input
int takeInput( char * str)
{
char * buf;
buf = readline( ">>> " );
if ( strlen (buf) != 0) {
add_history(buf);
strcpy (str, buf);
return 0;
} else {
return 1;
}
}
// Function to print Current Directory.
void printDir()
{
char cwd[1024];
getcwd(cwd, sizeof (cwd));
printf ( "Dir: %s" , cwd);
}
// Function where the system command is executed
void execArgs( char ** parsed)
{
// Forking a child
pid_t pid = fork();
if (pid == -1) {
printf ( "Failed forking child.." );
return ;
} else if (pid == 0) {
if (execvp(parsed[0], parsed) < 0) {
printf ( "Could not execute command.." );
}
exit (0);
} else {
// waiting for child to terminate
wait(NULL);
return ;
}
}
// Function where the piped system commands is executed
void execArgsPiped( char ** parsed, char ** parsedpipe)
{
// 0 is read end, 1 is write end
int pipefd[2];
pid_t p1, p2;
if (pipe(pipefd) < 0) {
printf ( "Pipe could not be initialized" );
return ;
}
p1 = fork();
if (p1 < 0) {
printf ( "Could not fork" );
return ;
}
if (p1 == 0) {
// Child 1 executing..
// It only needs to write at the write end
close(pipefd[0]);
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[1]);
if (execvp(parsed[0], parsed) < 0) {
printf ( "Could not execute command 1.." );
exit (0);
}
} else {
// Parent executing
p2 = fork();
if (p2 < 0) {
printf ( "Could not fork" );
return ;
}
// Child 2 executing..
// It only needs to read at the read end
if (p2 == 0) {
close(pipefd[1]);
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[0]);
if (execvp(parsedpipe[0], parsedpipe) < 0) {
printf ( "Could not execute command 2.." );
exit (0);
}
} else {
// parent executing, waiting for two children
wait(NULL);
wait(NULL);
}
}
}
// Help command builtin
void openHelp()
{
puts ( "***WELCOME TO MY SHELL HELP***"
"Copyright @ Suprotik Dey"
"-Use the shell at your own risk..."
"List of Commands supported:"
">cd"
">ls"
">exit"
">all other general commands available in UNIX shell"
">pipe handling"
">improper space handling" );
return ;
}
// Function to execute builtin commands
int ownCmdHandler( char ** parsed)
{
int NoOfOwnCmds = 4, i, switchOwnArg = 0;
char * ListOfOwnCmds[NoOfOwnCmds];
char * username;
ListOfOwnCmds[0] = "exit" ;
ListOfOwnCmds[1] = "cd" ;
ListOfOwnCmds[2] = "help" ;
ListOfOwnCmds[3] = "hello" ;
for (i = 0; i < NoOfOwnCmds; i++) {
if ( strcmp (parsed[0], ListOfOwnCmds[i]) == 0) {
switchOwnArg = i + 1;
break ;
}
}
switch (switchOwnArg) {
case 1:
printf ( "Goodbye" );
exit (0);
case 2:
chdir(parsed[1]);
return 1;
case 3:
openHelp();
return 1;
case 4:
username = getenv ( "USER" );
printf ( "Hello %s.Mind that this is "
"not a place to play around."
"Use help to know more.." ,
username);
return 1;
default :
break ;
}
return 0;
}
// function for finding pipe
int parsePipe( char * str, char ** strpiped)
{
int i;
for (i = 0; i < 2; i++) {
strpiped[i] = strsep(&str, "|" );
if (strpiped[i] == NULL)
break ;
}
if (strpiped[1] == NULL)
return 0; // returns zero if no pipe is found.
else {
return 1;
}
}
// function for parsing command words
void parseSpace( char * str, char ** parsed)
{
int i;
for (i = 0; i < MAXLIST; i++) {
parsed[i] = strsep(&str, " " );
if (parsed[i] == NULL)
break ;
if ( strlen (parsed[i]) == 0)
i--;
}
}
int processString( char * str, char ** parsed, char ** parsedpipe)
{
char * strpiped[2];
int piped = 0;
piped = parsePipe(str, strpiped);
if (piped) {
parseSpace(strpiped[0], parsed);
parseSpace(strpiped[1], parsedpipe);
} else {
parseSpace(str, parsed);
}
if (ownCmdHandler(parsed))
return 0;
else
return 1 + piped;
}
int main()
{
char inputString[MAXCOM], *parsedArgs[MAXLIST];
char * parsedArgsPiped[MAXLIST];
int execFlag = 0;
init_shell();
while (1) {
// print shell line
printDir();
// take input
if (takeInput(inputString))
continue ;
// process
execFlag = processString(inputString,
parsedArgs, parsedArgsPiped);
// execflag returns zero if there is no command
// or it is a builtin command,
// 1 if it is a simple command
// 2 if it is including a pipe.
// execute
if (execFlag == 1)
execArgs(parsedArgs);
if (execFlag == 2)
execArgsPiped(parsedArgs, parsedArgsPiped);
}
return 0;
}


要运行代码——

 gcc shell.c -lreadline
./a.out 

输出: 图片[2]-用C制作自己的Linux外壳-yiteyi-C++库 图片[3]-用C制作自己的Linux外壳-yiteyi-C++库

本文由 超级机器人 .如果你喜欢GeekSforgek,并想贡献自己的力量,你也可以使用 贡献极客。组织 或者把你的文章寄到contribute@geeksforgeeks.org.看到你的文章出现在Geeksforgeks主页上,并帮助其他极客。

如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请写下评论。

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享