写在前面
shell
作为一种与内核对话的一种方式,为我们使用操作系统服务,提供了很多便利。在我们使用Linux时,shell
是不得不接触的内容之一。为了学习和熟悉Linux
进程相关的内核函数,我们可以尝试着自己实现一个shell
。
源代码
src/Makefile
SOURCES = $(wildcard *.cpp)
OBJECTS = $(patsubst %.cpp, %.o, $(SOURCES))
CXXFLAG = -std=c++14 -I ../include -O2 -lreadline
psh: $(OBJECTS)
g++ $(CXXFLAG) -o psh $(OBJECTS)
$(OBJECTS): $(SOURCES)
g++ $(CXXFLAG) -c $(SOURCES)
.PHONY: clean
clean :
-rm $(OBJECTS)
include/psh.h
#pragma once
#ifndef _HEADER_PANGDA_SHELL__
#define _HEADER_PANGDA_SHELL__
#include<unistd.h>
#include<cstdio>
#include<string>
#include<vector>
#include<functional>
#include<map>
typedef std::vector<std::string> argument_t;
struct command_t {
int is_right_cmd = 0; //命令是否是正确的,若为0则说明是正确的,其他数字代表错误码
std::string execfile; //执行的文件名
argument_t arguments; //参数列表
bool is_redirect_stdin = false; //是否重定向了标准输入,即是否存在<语法元素
std::string filename_in; //新的标准输入名
bool is_redirect_stdout = false; //是否重定向了标准输出,即是否存在>语法元素
bool stdout_mode = false; //false表示截断输出,true表示追加输出
std::string filename_out; //新的标准输出文件名
bool is_background = false; //是否指定在后台运行,即是否存在&语法元素
bool is_pipe = false; //是否是一个管道,即是否存在|语法元素
std::string pipe_prompt[2]; //保存管道命令
};
command_t parse_command(std::string command);
std::string string_trim(std::string s);
std::string get_tip();
int exec_command(command_t &cmd);
int psh_error(int error);
int shellfunc_exit(command_t);
int shellfunc_logout(command_t);
int shellfunc_cd(command_t);
#endif
src/psh.cpp
#include<pangda/psh.h>
#include<readline/readline.h>
#include<readline/history.h>
#include<cstdlib>
#include<signal.h>
extern std::map<std::string, std::function<int(command_t)> > shell_commands;
int main(int argc, char *argv[], char **envp) {
//构建内建命令与实现函数的映射
shell_commands["exit"] = shellfunc_exit;
shell_commands["logout"] = shellfunc_logout;
shell_commands["cd"] = shellfunc_cd;
//阻断SIGINT SIGQUIT SIGSTOP SIGTSTP
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
while (true) {
std::string st = readline(get_tip().c_str()); //获得用户输入的内容
//若用户输入的不是全空格,则将这条命令保存在历史记录中。
//否则就不处理这条命令,直接获得下一条命令。
if (string_trim(st) == "")
continue;
else
add_history(st.c_str());
//解析命令
command_t cmd = parse_command(st);
//若命令是管道,则分别执行两条管道命令
if (cmd.is_pipe) {
command_t pipe1 = parse_command(cmd.pipe_prompt[0]);
command_t pipe2 = parse_command(cmd.pipe_prompt[1]);
if (exec_command(pipe1) != 0) //若管道的第一条命令就是错误的,不再执行第二条命令
continue;
exec_command(pipe2);
} else if (cmd.is_right_cmd) { //若解析命令之后发现命令存在错误,则进入错误处理程序
psh_error(cmd.is_right_cmd);
continue;
}
exec_command(cmd);