要了解有关什么是外壳的更多信息,请单击此处。
我们都使用Linux发行版(例如Ubuntu,Fedora等)中的内置终端窗口。但是它们实际上如何工作?在本文中,我们将处理一些在外壳内部实际起作用的功能和算法。所有Linux操作系统都有一个终端窗口来写入命令。但是输入后如何正确执行?
另外,如何保留诸如保留命令历史记录和显示帮助之类的额外功能?所有这些都可以通过创建自己的shell来理解。
基础知识
输入命令后,将完成以下操作:
- 输入了命令,如果length不为null,则将其保留在历史记录中。
- 解析:解析是将命令分解为单个单词和字符串
- 检查特殊字符(例如管道等)是否已完成
- 检查是否要求内置命令。
- 如果有管道,请处理管道。
- 通过派生一个孩子并调用execvp来执行系统命令和库。
- 打印当前目录名称并要求下一次输入。
为了保留命令的历史记录,使用箭头键恢复历史记录以及使用Tab键处理自动完成功能,我们将使用GNU提供的readline库。
执行
要安装readline库,请打开终端窗口并编写
sudo apt-get install libreadline-dev
它将要求您输入密码。输入它。在下一步中按y。
- 可以使用getcwd完成目录的打印。
- 可以通过getenv(“ USER”)来获取用户名
- 可以使用strsep(“”)进行解析。它将基于空格分隔单词。始终跳过长度为零的单词,以避免存储多余的空格。
- 解析后,检查内置命令列表,如果有,请执行。如果不是,则将其作为系统命令执行。要检查内置命令,请将命令存储在字符指针数组中,然后将所有内容与strcmp()进行比较。
注意:“ cd”不能在execvp上本地运行,因此它是一个内置命令,使用chdir()执行。 - 为了执行系统命令,将创建一个新的子代,然后通过使用execvp来执行该命令,并等待其完成。
- 也可以使用strsep(“ |”)来检测管道。要处理管道,请首先将命令的第一部分与第二部分分开。然后,在解析每个部分之后,使用execvp在两个单独的新子代中调用这两个部分。管道传输意味着将第一个命令的输出作为第二个命令的输入。
- 声明一个大小为2的整数数组,用于存储文件描述符。文件描述符0用于读取,文件描述符1用于写入。
- 使用pipe()函数打开管道。
- 创建两个孩子。
- 在孩子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()
- 在孩子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()
- 等待两个孩子在父母中完成。
// C Program to design a shell in Linux
#include
#include
#include
#include
#include
#include
#include
#include
#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("\033[H\033[J")
// Greeting shell during startup
void init_shell()
{
clear();
printf("\n\n\n\n******************"
"************************");
printf("\n\n\n\t****MY SHELL****");
printf("\n\n\t-USE AT YOUR OWN RISK-");
printf("\n\n\n\n*******************"
"***********************");
char* username = getenv("USER");
printf("\n\n\nUSER is: @%s", username);
printf("\n");
sleep(1);
clear();
}
// Function to take input
int takeInput(char* str)
{
char* buf;
buf = readline("\n>>> ");
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("\nDir: %s", cwd);
}
// Function where the system command is executed
void execArgs(char** parsed)
{
// Forking a child
pid_t pid = fork();
if (pid == -1) {
printf("\nFailed forking child..");
return;
} else if (pid == 0) {
if (execvp(parsed[0], parsed) < 0) {
printf("\nCould 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("\nPipe could not be initialized");
return;
}
p1 = fork();
if (p1 < 0) {
printf("\nCould 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("\nCould not execute command 1..");
exit(0);
}
} else {
// Parent executing
p2 = fork();
if (p2 < 0) {
printf("\nCould 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("\nCould not execute command 2..");
exit(0);
}
} else {
// parent executing, waiting for two children
wait(NULL);
wait(NULL);
}
}
}
// Help command builtin
void openHelp()
{
puts("\n***WELCOME TO MY SHELL HELP***"
"\nCopyright @ Suprotik Dey"
"\n-Use the shell at your own risk..."
"\nList of Commands supported:"
"\n>cd"
"\n>ls"
"\n>exit"
"\n>all other general commands available in UNIX shell"
"\n>pipe handling"
"\n>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("\nGoodbye\n");
exit(0);
case 2:
chdir(parsed[1]);
return 1;
case 3:
openHelp();
return 1;
case 4:
username = getenv("USER");
printf("\nHello %s.\nMind that this is "
"not a place to play around."
"\nUse help to know more..\n",
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
输出:
想要从精选的最佳视频中学习和练习问题,请查看《基础知识到高级C的C基础课程》。