目录
一、mysell.cc代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cstdlib>
#include <unistd.h>
#include<sys/wait.h>
using namespace std;
//我的系统自定义的环境变量表
const int envnum=64;
char* genv[envnum];
//全局命令行参数表
const int gargnum=64;
char* gargv[gargnum];
int gargc;
//全局当前工作路径
const int basesize=1024;
//有没有PWD一定要区分开
//有PWD=,存入genv
char my_pwd[basesize];
//没有PWD=,得最后路径名
char pwd[basesize];
//上一命令错误返回值
int lastcode=0;
//得到user
string GetUser(){
string name=getenv("USER");
return name.empty()?"None":name;
}
//得到hostname
string GetHostName(){
string hostname=getenv("HOSTNAME");
return hostname.empty()?"None":hostname;
}
//得到pwd
string GetPwd(){
//getcwp不依赖环境变量的正确性
if(getcwd(pwd,sizeof(pwd))==nullptr)
//为空就不写入genv
return "None";
snprintf(my_pwd,basesize,"PWD=%s",pwd);
//注意在环境变量pwd可能为旧值(像cd后这种情况)要更新
//my_pwd出作用域之后栈内存会被销毁
//putenv(my_pwd);
//setenv自动处理内存管理
setenv("PWD",pwd,1);//1表示覆盖旧值
return pwd;
}
//得到最后文件夹名
string LastDir(){
string curr=GetPwd();
if(curr=="/"||curr=="None")
return curr;
size_t pos=curr.rfind('/');
if(pos==string::npos)
return curr;
return curr.substr(pos+1);
}
//打印指令行
void PrintCommand(){
printf("[%s@%s %s]#",
GetUser().c_str(),GetHostName().c_str(),LastDir().c_str());
fflush(stdout);
}
//得到命令行:存在局部变量command_buffer[size]
bool GetCommand(char* command_buffer,int size){
char* result=fgets(command_buffer,size,stdin);
if(!result)
return false;
//把\n换成0
command_buffer[strlen(command_buffer)-1]=0;
if(strlen(command_buffer)==0)
return false;
return true;
}
//语法解析命令行:分段存入全局的命令行变量表
void ParseCommand(char* command_buffer){
memset(gargv,0,gargnum);
gargc=0;
gargv[gargc++]=strtok(command_buffer," ");
while((bool)(gargv[gargc++]=strtok(nullptr," ")));
--gargc;
}
//初始化自己的全局环境变量表
void InitEnv(){
extern char** environ;
int index=0;
while(environ[index]&&index<envnum-1){
//先扩容再拷贝
genv[index]=(char*)malloc(strlen(environ[index])+1);
strncpy(genv[index],environ[index],strlen(environ[index])+1);
index++;
}
genv[index]=nullptr;
}
//销毁自己的环境变量表
void DeletEnv(){
for(int i=0;genv[i];i++){
free(genv[i]);
}
}
//添加环境变量
void AddEnv(const char* env){
int index=0;
while(genv[index]&&index<envnum-1){
index++;
}
if(index==envnum-1){
perror("环境变量已满\n");
}
genv[index]=(char*)malloc(strlen(env)+1);
strncpy(genv[index],env,strlen(env)+1);
genv[++index]=nullptr;
}
void ChDir(const string& dir){
if(chdir(dir.c_str())!=0){
perror("chdir failed\n");
}
//pwd和my_pwd都要更新
if(getcwd(pwd,sizeof(pwd))==nullptr){
perror("getcwd failed");
return;
}
snprintf(my_pwd,sizeof(my_pwd),"PWD=%s",pwd);
int index=0;
while(strncmp(genv[index],"PWD=",4)!=0&&index<envnum-1){
index++;
}
if(index==envnum-1){
perror("环境变量已满\n");
}
else if(genv[index]==nullptr){
genv[index+1]=nullptr;
}
else//是之前malloc过的要free掉
free(genv[index]);
genv[index]=(char*)malloc(strlen(my_pwd)+1);
strncpy(genv[index],my_pwd,strlen(my_pwd)+1);
}
//检查并执行内接命令
bool CheckAndBuiltCommand(){
//cd为内接命令是因为要改变的是当前进程的路径
if(strcmp(gargv[0],"cd")==0){
if(gargc==2){
ChDir(gargv[1]);
}
else{
lastcode=1;
}
return true;
}
else if(strcmp(gargv[0],"export")==0){
if(gargc==2){
AddEnv(gargv[1]);
}
else{
lastcode=2;
}
return true;
}
else if(strcmp(gargv[0],"env")==0){
for(int i=0;genv[i];i++){
printf("%s\n",genv[i]);
}
return true;
}
else if(strcmp(gargv[0],"echo")==0){
for(int i=1;i<gargc;i++){
if(gargv[i][0]=='$'){
if(gargv[i][1]=='?')
printf("%d\n",lastcode);
else{
char* var=getenv(gargv[i]+1);
printf("%s%s",(var?var:" "),(i<gargc-1?" ":""));
}
}
else{
printf("%s",gargv[i]);
}
}
return true;
}
return false;
}
//外接命令
bool ExecuteCommand(){
pid_t id=fork();
if(id<0){
perror("fork failed!");
return false;
}
else if(id==0){
execvpe(gargv[0],gargv,genv);
exit(1);
}
else{
int status=0;
pid_t chid=waitpid(id,&status,0);
if(chid<0){
perror("waitpid failed");
return false;
}
else if(chid>0){
if(WIFEXITED(status)){
lastcode=WEXITSTATUS(status);
}
else{
lastcode=100;
}
return true;
}
}
return false;
}
int main(){
char command_buffer[basesize];
InitEnv();
while(1){
PrintCommand();
if(!GetCommand(command_buffer,basesize))
continue;
ParseCommand(command_buffer);
if(CheckAndBuiltCommand())
continue;
ExecuteCommand();
}
DeletEnv();
return 0;
}
二、注意
1.分清楚my_pwd和pwd的区别
my_pwd有“PWD=”,用来存入genv
pwd没有,是从getcwd()中得到的
getcwd()函数不依赖环境变量的正确性
getenv()依赖
-->pwd由getcwd()得到,用来跟新my_pwd和genv
2.自己的ChDir内接命令(改变现在进程的地址)也要调用chdir()改变地址
注意此时环境变量的地址还没有改变,
所以要依次更新pwd,my_pwd,genv,环境变量
3.更新环境变量时putenv() vs. setenv()
不推荐putenv(const char* s),需要手动管理字符串生命周期
推荐setenv("name",value,1),自动管理
三、调试——cgdb的操作
1.char* result=fgets(command_buffer,size,stdin);
当输入流字符未满要关闭时,此处操作是r stdin<input.txt
其中input.txt是提前写好的输入文件
2.调试子进程
set follow-fork-mode [parent/child] 默认调试父进程,子进程继续运行……
set detach-on-fork on 默认只调试父进程
set detach-on-fork off 同时调试两个进程,被调试的进程暂停,另一个进程处于挂起状态
info inferiors 显示所有[被调试]进程
在detach-on-fork on的情况下不调试子进程
inferior num 切换到指定num编号的进程
四、初学者易混淆的函数
1.<cstring> strcmp VS. strncmp(const char* s1,const char* s2,size_t num),比较前num个
strcpy strncpy(const char* dest,const char*scr,size_t num),不含\0,自己注意手动加
<cstdio> sprintf snprintf(缓冲区,最大安全长度,格式,字符) ,自动添加\0
2.<cstdlib> setenv VS. putenv