C语言实现shell
涉及到的相关函数
获取用户名 :
uid_t getuid(void);
struct passwd *getpwuid(uid_t uid);
//getpwuid 返回一个结构体指针,可以用来获取下列结构
struct passwd {char *pw_name; /* username */char *pw_passwd; /* user password */uid_t pw_uid; /* user ID */gid_t pw_gid; /* group ID */char *pw_gecos; /* user information */char *pw_dir; /* home directory */char *pw_shell; /* shell program */};
获取当前工作目录 :
char *getcwd(char *buf, size_t size);
getcwd可以将当前工作的绝对路径填充进buf指向的空间;
大小不超过size; 失败返回NULL指针, 并设置errno;
切换工作路径 :
int chdir(const char *path);
改变当前进程的工作路径,成功返回 0, 失败返回-1;
并设置errno;
The current working directory is the starting point for interpreting relative pathnames (those not starting with '/').
切换路径后修改命令行的提示
void swim_dir() {if (chdir(vec[1]) < 0){perror("chdir()");return;}has_ch_dir = 1;
}
重定位功能相关函数
复制文件描述符
int dup(int oldfd); int dup2(int oldfd, int newfd); //dup用未使用的最小文件file descriptor 复制当前oldfd,并返回; //dup2将之前打开的newfd用来复制oldfd, 它会前关闭之前的newfd再 //指向oldfd,如果oldfd是无效的,那么它啥事也不做; 失败返回:-1,并设置errno;
啥是文件描述符 ?
文件描述符是数组下标, 数组里存放了打开文件结构体的指针, 指向的结构体里又有指向inode的指针; 该数组存在在每个进程空间里, 系统会默认打开默认stream: stdin/stdout/stderr;
fork() 复制产生子进程
fork可以产生一个新进程,通过duplicate 当前进程
fork后父子进程的区别:
- fork的返回值不一样, pid不一样, ppid不一样;
- 未决信号和文件锁不继承
- 资源利用量清零;
注意 : fork()进行写时复制(谁改变原内容, 谁复制)
pipe() 实现IPC机制调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信, 有一个读端和写端, 通过 filedes参数传给用户程序的两个文件描述符, filedes[0]指向管道的读端, filedes[1]指向管道的写端;/* On Alpha, IA-64, MIPS, SuperH, and SPARC/SPARC64; see NOTES */
struct fd_pair {long fd[2];
};
struct fd_pair pipe();/* On all other architectures */
int pipe(int pipefd[2]);
管道用法的栗子
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>int main(){ pid_t pid;int fd[2];if (pipe(fd) < 0) {perror("pipe()");exit(1);} if ((pid = fork()) < 0){perror("fork()");exit(1);}int n;char buf[20];if (pid > 0) {close(fd[0]);write(fd[1], "hello pipe\n", 11);wait(NULL);}else{close(fd[1]);sleep(1);n = read(fd[0], buf, 20);write(1, buf, n);} return 0;
}
命令的执行相关函数
当进程调用一种exec函数时, 改函数的用户空间代码和数据完全被新程序替代, 从新程序的启动例程开始执行
//exec函数族
int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
int execle(const char *pathname, const char *arg, ... /*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
调用exec函数的调用规则:
一般父进程fork()后, 子进程调用exec函数族执行新的进程, 父进程调用wait()可以等待子进程退出
// fork()/ wait() / execl()
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main(){pid_t pid;puts("Begin!");fflush(NULL);
//注意在fork前刷新缓冲区;pid = fork();if (pid < 0) {perror("fork()");exit(1);}if (pid == 0) {//子进程执行replace成其他进程映像;execl("/bin/date", "date", "+%s", NULL);perror("execl()");exit(1);}wait(NULL);
//父进程隔这儿等着收尸puts("End!");return 0;
}
程序流程图
代码总览
/***************************************************************> File Name: fish.c> Author: Feiger> Mail:1162006607@qq.com > Created Time: Mon 24 May 2021 09:15:26 PM CST**************************************************************/
#include <pwd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <glob.h>
#include <sys/wait.h>
#include <sys/fcntl.h>
#include <time.h>#define MAX_N 1024
#define N 64
#define RED(msg) "\033[31m"#msg"\033[0m"
#define GOLD(msg) "\033[33m"#msg"\033[0m"
#define BLUE(msg) "\033[32m"#msg"\033[0m"
char *vec[MAX_N] = {0};
char *cmd[N][N] = {0};
char talk[][MAX_N] = {{"Blue... Blue!! Blue ?\n"},{"Are you teaching me how to do the job?\n"},{"I am a fish, not a shell, try some simple cmd!\n"},{"How dare you let me do so many homework?\n"},{"Can you say the language of fish ??\n"},{"Let me do some simple work!\n"}
};
int has_ch_dir = 1;
char path[MAX_N] = {0};
struct passwd *usr;void prompt() {char buf[MAX_N] = {0};if (has_ch_dir) {has_ch_dir = 0;if (getcwd(buf, MAX_N) == NULL) {perror("getcwd()");exit(1);}int cnt = 0;for (int i = 0; buf[i]; i++) {if (buf[i] == '/') {cnt = 0;continue;}path[cnt++] = buf[i];} path[cnt] = '\0';}fprintf(stdout, RED(%s) "@" GOLD(fish:), usr->pw_name);fprintf(stdout, BLUE(%s $), path);
}void analysis(char *str, char **vec) {char argu[MAX_N][MAX_N] = {0};memset(vec, 0, sizeof(char *) * MAX_N);int cnt = 0;for (int i = 0,j = 0,t = 1; str[i] && str[i] !='\n'; i++){if (str[i] == ' ') {if (t) {cnt++,j = 0, t = 0;}continue;}t = 1;argu[cnt][j++] = str[i];}if (argu[0][0] == 0) return;for (int i = 0; i <= cnt; i++) {vec[i] = strdup(argu[i]);}return;
}void my_execv() {pid_t cpid;if ((cpid = fork()) < 0) {perror("fork()");return;}if (cpid == 0) {execvp(vec[0], vec); perror("myfish");exit(1);}else{int t;if (wait(&t) < 0){exit(1);}}
}int redirctB() {int fd = open(cmd[1][0], O_WRONLY|O_CREAT, 0644);if (fd < 0) {perror("open()");return 1;}fflush(stdout);int out = dup(STDOUT_FILENO);if (dup2(fd, STDOUT_FILENO) < 0 || out < 0){perror("dup()");return 1;}pid_t pid = fork();if(pid < 0) {perror("fork()");return 1;}if (pid == 0) {execvp(cmd[0][0], cmd[0]);perror("execvp()");exit(1);} dup2(out, STDOUT_FILENO);close(fd); wait(NULL);return 0;
}int redirctL() {int fd = open(cmd[1][0], O_RDONLY);if (fd < 0) {perror("open()");return 1;}fflush(stdin);int infd = dup(STDIN_FILENO);if (dup2(fd, STDIN_FILENO) < 0 || infd < 0){perror("dup()");return 1;}pid_t pid = fork();if (pid < 0) {perror("fork()");return 1;}if (pid == 0) {execvp(cmd[0][0], cmd[0]);perror("execvp()");exit(1);}dup2(infd, STDIN_FILENO);close(infd);wait(NULL);return 0;
}int redirPip() {int fd[2];pid_t pid1, pid2;if (pipe(fd) < 0) {perror("pipe()");return 1;}fflush(stdout);pid1 = fork();if (pid1 < 0) {perror("fork()");return 1;}if (pid1 == 0) {close(fd[0]);dup2(fd[1], STDOUT_FILENO);execvp(cmd[0][0], cmd[0]);perror("execvp()");exit(1);}pid2 = fork();if (pid2 < 0){perror("fork()");return 1;}if (pid2 == 0){close(fd[1]);dup2(fd[0], STDIN_FILENO);execvp(cmd[1][0], cmd[1]);perror("execvp()");exit(1);}close(fd[0]);close(fd[1]);waitpid(pid1, NULL, 0);waitpid(pid2, NULL, 0);return 0;
}void swim_dir() {if (chdir(vec[1]) < 0){perror("chdir()");return;}has_ch_dir = 1;
}int redirect() {char *flag = NULL, cnt = 0;if (vec[0] == NULL) return 0; if (!strcmp(vec[0], "cd")) {swim_dir();return 0;}memset(cmd, 0, sizeof(cmd));for (int i = 0,t = 0; vec[i]; i++) {if (!strcmp(vec[i], "|")||!strcmp(vec[i], "<") || !strcmp(vec[i], ">")) {flag = strdup(vec[i]);cnt++;t = 0;continue;}cmd[cnt][t++] = vec[i];}if (strcmp(cmd[0][0], "blue") == 0) {printf("%s", talk[0]);return 0;}srand(time(0));if (cnt > 1) {printf("%s", talk[rand() % 6]);return 0;}if (flag == NULL) {my_execv();return -1;}if(strcmp(flag, ">") == 0) {redirctB();}else if (strcmp(flag, "<") == 0){redirctL();}else if (strcmp(flag, "|") == 0){redirPip();}return 0;
}int main(int argc, char *argv[]){char *buff = NULL;size_t buff_size = 0;fprintf(stdout, BLUE( Blue. Blue. Blue...\n));fprintf(stdout, "I'm a fish, nice to meet you!\n\n");usr = getpwuid(getuid());if (usr == NULL) {perror("getpwuid()");exit(1);}while (1) {prompt();if (getline(&buff, &buff_size, stdin) < 0){perror("getline()");break;}analysis(buff, vec);redirect();}return 0;
}
运行截图:
C语言实现shell相关推荐
- shell是什么语言?shell 语言的本质
2019独角兽企业重金招聘Python工程师标准>>> shell是什么语言?shell 语言的本质 "Shell是Linux/Unix的一个外壳,你理解成衣服也行.它负责 ...
- c语言加密shell脚本,shell脚本加密
如何保护自己编写的shell程序 要保护自己编写的shell脚本程序,方法有很多,最简单的方法有两种:1.加密 2.设定过期时间,下面以shc工具为例说明: 一.下载安装shc工具 shc是一个加密s ...
- 什么是脚本语言,shell脚本又是什么?
脚本语言就是解释型语言: 脚本语言(Script language,scripting language,scripting programming language)是为了缩短传统的编写-编译-链接 ...
- 一天学会shell语言,shell教程,shell简单入门,shell中文文档
shell语言是一门linux系统下的工具语言,主要用于写一些linux系统下的操作命令,实际上Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核.或者直接理解为shell命 ...
- c语言调用shell脚本或命令
1.system(执行shell 命令) 相关函数 fork,execve,waitpid,popen 表头文件 #include<stdlib.h> 定义函数 int system(co ...
- c语言调用shell命令一 popen使用以及获取命令返回值
产品升级,新增网卡,原先的产品是arm平台,新网卡是mips平台,需要开发网卡的配置程序,该程序原计划是以守护进程的形式后台执行,不过测试过程中发现系统不是特别稳定,导致程序时不时奔溃下,一时半会儿无 ...
- [shell]C语言调用shell脚本接口
1) system(shell命令或shell脚本路径); 执行过程:system()会调用fork()产生子进程,由子进程来调用/bin/sh -c string来执行参数string字符串所代表的 ...
- linux下如何用c语言调用shell命令-转
C程序调用shell脚本共有三种法子 :system().popen().exec系列函数 system()不用你自己去产生进程,它已经封装了,直接加入自己的命令exec 需要你自己 fork 进程, ...
- html怎么shell脚本语言,简单shell脚本例子
为什么要使用Shell脚本 使用脚本编程语言的好处是,它们多半运行在比编译型语言还高的层级,能够轻易处理文件与目录之类的对象.缺点是:它们的效率通常不如编译型语言.不过权衡之下,通常使用脚本编程还是值 ...
最新文章
- CALayer( 一 )
- 用thinkphp进行微信开发的整体设计思考
- 《数据中心布线系统的设计与施工技术白皮书》目录
- php 方法求 的近似值,PHP中的等角近似
- 【Redis】13.Redis服务器配置redis.conf
- 敲代码括号技巧_理解代码块概念,养成良好编程习惯 | 亲子课堂 第 3 课
- Became Jane(成为简.奥斯丁)
- WPF读写config配置文件
- oracle patch下载地址
- MSSQL数据库初级到高级的学习资料整理
- PHP:编写标准体重计算器
- 在一起盗窃案中,法官对涉及到的四名犯罪嫌疑人A,B,C,D进行了审问.
- 1077E Thematic Contests 【二分答案】
- 【源码阅读】GAT:GRAPH ATTENTION NETWORKS
- 网络营销中的动态定价策略
- JNI详解---从不懂到理解
- 浅析群控系统的发展之路,云控和群控的巨大差别
- startactivity后App出现闪退问题情况分析
- “8 岁学编程,做了近 40 年程序员,我总结了 15 条经验宝典”
- Python实现一个总体的均值、比例、方差检验