涉及到的相关函数

获取用户名 :

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后父子进程的区别:

  1. fork的返回值不一样, pid不一样, ppid不一样;
  2. 未决信号和文件锁不继承
  3. 资源利用量清零;

注意 : 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相关推荐

  1. shell是什么语言?shell 语言的本质

    2019独角兽企业重金招聘Python工程师标准>>> shell是什么语言?shell 语言的本质 "Shell是Linux/Unix的一个外壳,你理解成衣服也行.它负责 ...

  2. c语言加密shell脚本,shell脚本加密

    如何保护自己编写的shell程序 要保护自己编写的shell脚本程序,方法有很多,最简单的方法有两种:1.加密 2.设定过期时间,下面以shc工具为例说明: 一.下载安装shc工具 shc是一个加密s ...

  3. 什么是脚本语言,shell脚本又是什么?

    脚本语言就是解释型语言: 脚本语言(Script language,scripting language,scripting programming language)是为了缩短传统的编写-编译-链接 ...

  4. 一天学会shell语言,shell教程,shell简单入门,shell中文文档

        shell语言是一门linux系统下的工具语言,主要用于写一些linux系统下的操作命令,实际上Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核.或者直接理解为shell命 ...

  5. c语言调用shell脚本或命令

    1.system(执行shell 命令) 相关函数 fork,execve,waitpid,popen 表头文件 #include<stdlib.h> 定义函数 int system(co ...

  6. c语言调用shell命令一 popen使用以及获取命令返回值

    产品升级,新增网卡,原先的产品是arm平台,新网卡是mips平台,需要开发网卡的配置程序,该程序原计划是以守护进程的形式后台执行,不过测试过程中发现系统不是特别稳定,导致程序时不时奔溃下,一时半会儿无 ...

  7. [shell]C语言调用shell脚本接口

    1) system(shell命令或shell脚本路径); 执行过程:system()会调用fork()产生子进程,由子进程来调用/bin/sh -c string来执行参数string字符串所代表的 ...

  8. linux下如何用c语言调用shell命令-转

    C程序调用shell脚本共有三种法子 :system().popen().exec系列函数 system()不用你自己去产生进程,它已经封装了,直接加入自己的命令exec 需要你自己 fork 进程, ...

  9. html怎么shell脚本语言,简单shell脚本例子

    为什么要使用Shell脚本 使用脚本编程语言的好处是,它们多半运行在比编译型语言还高的层级,能够轻易处理文件与目录之类的对象.缺点是:它们的效率通常不如编译型语言.不过权衡之下,通常使用脚本编程还是值 ...

最新文章

  1. CALayer( 一 )
  2. 用thinkphp进行微信开发的整体设计思考
  3. 《数据中心布线系统的设计与施工技术白皮书》目录
  4. php 方法求 的近似值,PHP中的等角近似
  5. 【Redis】13.Redis服务器配置redis.conf
  6. 敲代码括号技巧_理解代码块概念,养成良好编程习惯 | 亲子课堂 第 3 课
  7. Became Jane(成为简.奥斯丁)
  8. WPF读写config配置文件
  9. oracle patch下载地址
  10. MSSQL数据库初级到高级的学习资料整理
  11. PHP:编写标准体重计算器
  12. 在一起盗窃案中,法官对涉及到的四名犯罪嫌疑人A,B,C,D进行了审问.
  13. 1077E Thematic Contests 【二分答案】
  14. 【源码阅读】GAT:GRAPH ATTENTION NETWORKS
  15. 网络营销中的动态定价策略
  16. JNI详解---从不懂到理解
  17. 浅析群控系统的发展之路,云控和群控的巨大差别
  18. startactivity后App出现闪退问题情况分析
  19. “8 岁学编程,做了近 40 年程序员,我总结了 15 条经验宝典”
  20. Python实现一个总体的均值、比例、方差检验

热门文章

  1. 404未找到是什么意思_http404未找到怎么解决,404 未找到常见问题汇总
  2. 用 Java3D 写游戏
  3. IDEA JAVA文档注释和方法注释模板
  4. 【续】数学模型——人口增长模型
  5. pair类型 C++
  6. 如何快速书写文件的相对路径和绝对路径
  7. windows 下查看防火墙状态命令
  8. (转)BT下载不死!Magnet(磁力链接)开创网络BT2.0时代!!
  9. RANSAC算法(仅供学习使用)
  10. Linux系统安装开源版PyMOL