当前位置:首页>Linux>手把手教学:Linux进程控制实战第五弹,从零打造你的微型Shell解释器

手把手教学:Linux进程控制实战第五弹,从零打造你的微型Shell解释器

  • 2026-02-27 01:01:57
手把手教学:Linux进程控制实战第五弹,从零打造你的微型Shell解释器

前面四篇文章,我们已经掌握了进程控制的 “全链路技能”:用fork创建子进程、exec替换程序、waitpid回收资源、exit终止进程。今天,我们将这些知识 “组装” 成一个能实际运行的工具 ——微型Shell命令行解释器(简称 “迷你 Shell”)。

今日文章阅读福利:《运维入门大礼包

扫码添加小助理,发送暗号运维,即可获取。

这个迷你Shell将支持:命令行提示符(如[user@host dir]#)、内建命令(cd/export/env/echo)、外部命令(ls/ps等)、环境变量管理(继承与导出),完全遵循Linux Shell的核心工作逻辑。通过亲手实现,你会彻底明白 “输入一条命令后,Shell到底在做什么”。

一、先搞懂:Shell的本质是 “命令管家”

在写代码前,我们先回归本质:Shell是一个 “命令管家”—— 它的核心工作是 “接收用户命令→解析命令→调度资源执行命令→反馈结果”,具体流程可拆解为一个无限循环:

  1. 展示提示符:打印[用户名@主机名 工作目录]$,告诉用户 “可以输入命令了”;

  2. 获取命令:读取用户输入的一行命令(如ls -l或cd /home);

  3. 解析命令:将命令拆分为 “命令名 + 参数”(如ls -l拆成["ls", "-l", NULL]);

  4. 执行命令:

  • 若为内建命令(如cd):Shell自己执行(需修改Shell进程自身状态,不能用子进程);
  • 若为外部命令(如ls):Shell创建子进程,子进程用exec替换为目标程序,Shell等待子进程退出;
  • 循环往复:回到第一步,等待用户输入下一条命令。

举个通俗的例子:Shell就像餐厅服务员 —— 提示符是 “请问需要点什么?”,用户输入是 “一份牛排”(命令),解析是 “牛排 + 七分熟”(命令 + 参数),执行是:

  • 内建命令:服务员自己给你倒杯水(不用叫后厨);

  • 外部命令:服务员叫后厨(子进程)做牛排,自己在旁边等(waitpid),做好后给你端过来(反馈结果)。

二、迷你Shell的核心模块设计

我们将迷你Shell拆解为5个核心模块,逐个实现并讲解,最后整合为完整代码。每个模块都对应Shell的一个关键功能,且能复用前面学的进程控制知识。

2.1模块1:命令行提示符 —— 给用户 “交互的入口”

命令行提示符(如[ubuntu@localhost myshell]$)的作用是 “提示用户输入命令”,它需要包含三个关键信息:用户名、主机名、当前工作目录。我们通过 Linux提供的函数获取这些信息:

  • 用户名:getenv("USER")(从环境变量中获取当前登录用户);

  • 主机名:getenv("HOSTNAME")(从环境变量中获取主机名);

  • 当前工作目录:getcwd()(获取当前进程的工作目录,比getenv("PWD")更准确,因为cd后PWD可能未更新)。

实现细节:

  • 工作目录简化:默认显示完整路径(如/home/ubuntu/myshell),我们可以简化为 “最后一级目录”(如myshell),更符合常用Shell的习惯;

  • 缓冲区刷新:printf默认是 “行缓冲”,若提示符不含\n,需用fflush(stdout)强制刷新,否则提示符会 “卡着不显示”。

代码实现(提示符模块):

c

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>// 全局变量:存储当前工作目录(避免频繁分配内存)#define BUF_SIZE 1024char g_pwd[BUF_SIZE] = {0};char g_last_pwd[BUF_SIZE] = {0};  // 存储上次工作目录,用于cd -// 1. 获取用户名staticcharget_username(){    char* username = getenv("USER");    return (username == NULL) ? "unknown" : username;}// 2. 获取主机名staticcharget_hostname(){    char* hostname = getenv("HOSTNAME");    return (hostname == NULL) ? "unknown-host" : hostname;}// 3. 获取当前工作目录(并更新PWD环境变量)staticcharget_current_dir(){    if (getcwd(g_pwd, BUF_SIZE) == NULL) {  // getcwd获取当前工作目录        perror("getcwd failed");        return "unknown-dir";    }    // 更新环境变量PWD(确保echo $PWD能显示正确路径)    static char pwd_env[BUF_SIZE] = {0};    snprintf(pwd_env, BUF_SIZE, "PWD=%s", g_pwd);    putenv(pwd_env);  // putenv修改当前进程的环境变量    return g_pwd;}// 4. 简化工作目录(只显示最后一级)staticcharsimplify_dir(char* full_dir){    if (full_dir == NULL || strcmp(full_dir, "/") == 0) {        return "/";  // 根目录直接返回/    }    // 逆序查找最后一个/(如/home/ubuntu/myshell → 找到最后一个/,返回myshell)    char* last_slash = strrchr(full_dir, '/');    return (last_slash == NULL) ? full_dir : (last_slash + 1);}// 5. 打印命令行提示符voidprint_prompt(){    char* username = get_username();    char* hostname = get_hostname();    char* full_dir = get_current_dir();    char* simple_dir = simplify_dir(full_dir);    // 格式:[用户名@主机名 简化目录]$     printf("[%s@%s %s]$ ", username, hostname, simple_dir);    fflush(stdout);  // 强制刷新缓冲区,确保提示符立即显示}

效果演示:

调用print_prompt()后,终端会显示类似:

plaintext

[ubuntu@localhost myshell]

2.2 模块2:命令获取与解析 —— 把 “字符串” 变成 “可执行指令”

用户输入的命令是 “字符串”(如ls -l /home),我们需要将其拆分为 “命令名 + 参数数组”(如["ls", "-l", "/home", NULL]),才能传给execvp执行。这一步分两个子任务:

子任务1:获取用户输入(用fgets而非scanf)

  • scanf的问题:遇到空格就停止读取,无法获取带空格的命令(如echo "hello world");

  • fgets的优势:读取一整行输入,包含空格,完美适配命令输入场景;

  • 处理细节:fgets会把用户输入的 “回车符\n” 也读入,需将其替换为字符串结束符\0;若输入为空行(只按回车),直接跳过。

子任务2:解析命令(用strtok按空格拆分)

strtok是C标准库的字符串拆分函数,能按指定分隔符(这里是空格)拆分字符串:

  • 第一次调用:strtok(command, " "),传入要拆分的命令字符串,返回第一个 “非空格” 的子串(命令名);

  • 后续调用:strtok(NULL, " "),传入NULL表示 “继续拆分上次的字符串”,直到返回NULL(拆分结束);

  • 注意:拆分后的参数数组最后必须加NULL,因为execvp要求参数数组以NULL结尾。

代码实现(命令获取与解析模块):

c

#include <ctype.h>  // 包含isspace函数// 全局变量:存储命令参数(命令名+参数,最后以NULL结尾)#define ARGV_MAX 64char* g_argv[ARGV_MAX] = {0};int g_argc = 0;  // 命令参数个数(包含命令名)// 1. 去除字符串前后的空格(避免空参数,如"  ls  -l  " → "ls -l")staticvoidtrim_space(char* str){    if (str == NULLreturn;    // 去除前面的空格    char* start = str;    while (isspace(*start)) start++;    // 去除后面的空格    char* end = str + strlen(str) - 1;    while (end >= start && isspace(*end)) end--;    // 给字符串加结束符    *(end + 1) = '\0';    // 移动字符串(覆盖前面的空格)    memmove(str, start, end - start + 2);  // +2:包含end+1的\0}// 2. 获取用户输入的命令intget_command(char* command_buf, int buf_size){    // 读取一行命令(fgets会读入\n)    if (fgets(command_buf, buf_size, stdin) == NULL) {        // 若用户按Ctrl+D(EOF),返回-1表示退出        printf("\n");        return -1;    }    // 去除换行符(将\n替换为\0)    command_buf[strcspn(command_buf, "\n")] = '\0';    // 去除前后空格    trim_space(command_buf);    // 处理空行(用户只按了回车)    if (strlen(command_buf) == 0) {        return 0;    }    return 1;  // 成功获取有效命令}// 3. 解析命令(拆分为g_argv数组)voidparse_command(char* command_buf){    // 重置全局变量(避免上次命令的残留)    memset(g_argv, 0sizeof(g_argv));    g_argc = 0;    // 第一次拆分:获取命令名    char* token = strtok(command_buf, " ");    while (token != NULL && g_argc < ARGV_MAX - 1) {  // 留一个位置放NULL        g_argv[g_argc++] = token;        // 后续拆分:获取参数        token = strtok(NULL" ");    }    g_argv[g_argc] = NULL;  // 参数数组必须以NULL结尾}// 调试用:打印解析后的参数(可选)voiddebug_print_argv(){    printf("解析结果:argc=%d\n", g_argc);    for (int i = 0; i < g_argc; i++) {        printf("argv[%d] = %s\n", i, g_argv[i]);    }}

效果演示:

用户输入ls -l /home ,解析后:

  • g_argc = 3;

  • g_argv = ["ls", "-l", "/home", NULL]。

2.3模块3:内建命令处理 ——Shell “自己动手” 的命令

内建命令(Built-in Command)是必须由Shell进程自身执行的命令,因为它们需要修改 Shell 的 “自身状态”(如cd修改工作目录、export修改环境变量)—— 若用子进程执行,修改的是子进程的状态,父进程(Shell)的状态不会变(进程具有独立性)。

迷你Shell将实现4个核心内建命令:cd、export、env、echo,我们逐个实现。

3.3.1 内建命令1:cd—— 切换工作目录

cd的核心是调用chdir系统函数修改当前进程的工作目录,但需要处理特殊场景:

  • cd(无参数):切换到用户家目录(getenv("HOME"));

  • cd ~:同无参数,切换到家目录;

  • cd -:切换到 “上次的工作目录”(需用g_last_pwd存储上次目录);

  • cd 目录路径:切换到指定目录(如cd /home)。

3.3.2内建命令2:export——导出环境变量

export的作用是 “将变量添加到当前进程的环境变量表中”,供后续执行的命令继承(如export MY_VAR=123):

  • 实现方式:用putenv函数(C 标准库),将 “KEY=VALUE” 格式的字符串添加到环境变量表;

  • 注意:export无参数时,可打印所有已导出的环境变量(可选功能)。

3.3.3内建命令3:env——打印所有环境变量

env的作用是 “打印当前进程的所有环境变量”,实现方式是遍历全局环境变量数组environ(extern char **environ),逐个打印每个环境变量(格式为 “KEY=VALUE”)。

3.3.4内建命令4:echo—— 打印内容或环境变量

echo支持三种场景:

  • echo文本:直接打印文本(如echo hello → 输出hello);

  • echo $环境变量:打印指定环境变量的值(如echo $PATH → 输出/bin:/usr/bin);

  • echo $?:打印 “上次命令的退出码”(需用全局变量g_last_code存储)。

代码实现(内建命令模块):

c

#include <sys/wait.h>// 全局变量:存储上次命令的退出码(供echo $?使用)int g_last_code = 0;// 全局变量:存储环境变量表(从父进程继承)extern char **environ;// 1. 判断是否为内建命令(返回1=是,0=否)intis_builtin_command(){    if (g_argc == 0 || g_argv[0] == NULLreturn 0;    // 支持的内建命令列表    const char* builtin_list[] = {"cd""export""env""echo"NULL};    for (int i = 0; builtin_list[i] != NULL; i++) {        if (strcmp(g_argv[0], builtin_list[i]) == 0) {            return 1;        }    }    return 0;}// 2. 执行内建命令cdstaticvoidexec_cd(){    char* target_dir = NULL;    // 保存当前目录(用于cd -)    strncpy(g_last_pwd, g_pwd, BUF_SIZE);    // 处理不同参数场景    if (g_argc == 1) {        // cd无参数 → 切换到家目录        target_dir = getenv("HOME");    } else if (strcmp(g_argv[1], "~") == 0) {        // cd ~ → 切换到家目录        target_dir = getenv("HOME");    } else if (strcmp(g_argv[1], "-") == 0) {        // cd - → 切换到上次目录        target_dir = g_last_pwd;        printf("%s\n", target_dir);  // 模仿bash,打印切换后的目录    } else {        // cd 目录路径 → 切换到指定目录        target_dir = g_argv[1];    }    // 调用chdir切换目录    if (chdir(target_dir) == -1) {        perror("cd failed");        g_last_code = 1;  // 退出码设为1(表示失败)    } else {        g_last_code = 0;  // 退出码设为0(表示成功)    }}// 3. 执行内建命令exportstaticvoidexec_export(){    if (g_argc != 2) {        // 参数错误(如export无参数或多参数)        fprintf(stderr, "用法:export KEY=VALUE\n");        g_last_code = 2;        return;    }    // 检查参数格式(必须包含=,如MY_VAR=123)    if (strchr(g_argv[1], '=') == NULL) {        fprintf(stderr, "错误:export参数必须包含'='\n");        g_last_code = 2;        return;    }    // 调用putenv添加环境变量    if (putenv(g_argv[1]) != 0) {        perror("export failed");        g_last_code = 1;    } else {        g_last_code = 0;    }}// 4. 执行内建命令envstaticvoidexec_env(){    // 遍历environ数组,打印所有环境变量    for (int i = 0; environ[i] != NULL; i++) {        printf("%s\n", environ[i]);    }    g_last_code = 0;}// 5. 执行内建命令echostaticvoidexec_echo(){    if (g_argc < 2) {        // echo无参数 → 打印空行        printf("\n");        g_last_code = 0;        return;    }    char* content = g_argv[1];    if (content[0] == '$') {        // 场景1:echo $变量(如$PATH、$?)        if (strcmp(content, "$?") == 0) {            // echo $? → 打印上次命令的退出码            printf("%d\n", g_last_code);        } else {            // echo $环境变量 → 打印环境变量的值            char* var_name = content + 1;  // 跳过$,取变量名(如$PATH → PATH)            char* var_value = getenv(var_name);            if (var_value != NULL) {                printf("%s\n", var_value);            }            // 变量不存在时,不打印(模仿bash)        }    } else {        // 场景2:echo 文本 → 打印文本(支持多参数,如echo hello world)        for (int i = 1; i < g_argc; i++) {            printf("%s ", g_argv[i]);        }        printf("\n");    }    g_last_code = 0;}// 6. 统一执行内建命令voidexec_builtin_command(){    if (strcmp(g_argv[0], "cd") == 0) {        exec_cd();    } else if (strcmp(g_argv[0], "export") == 0) {        exec_export();    } else if (strcmp(g_argv[0], "env") == 0) {        exec_env();    } else if (strcmp(g_argv[0], "echo") == 0) {        exec_echo();    }}

2.4模块4:外部命令执行 ——Shell “找帮手” 的命令

外部命令(如ls/ps/gcc)是 “独立的可执行程序”,需要Shell创建子进程执行(避免覆盖Shell自身代码),核心流程是:

  1. fork创建子进程;

  2. 子进程用execvp替换为目标程序(自动从PATH查找命令路径);

  3. 父进程用waitpid等待子进程退出,获取退出码(更新g_last_code);

  4. 若execvp失败(如命令不存在),子进程退出并设置错误退出码。

代码实现(外部命令模块):

c

voidexec_external_command(){    pid_t pid = fork();    if (pid == -1) {        // fork失败(如系统进程过多)        perror("fork failed");        g_last_code = 1;        return;    }    if (pid == 0) {        // 子进程:执行外部命令        execvp(g_argv[0], g_argv);        // 只有execvp失败时,才会执行到这里(成功则子进程代码被覆盖)        perror("command not found");  // 错误原因:命令不存在、权限不足等        exit(127);  // 退出码127:标准的“命令未找到”错误码    } else {        // 父进程:等待子进程退出,获取退出码        int status;        waitpid(pid, &status, 0);  // 阻塞等待子进程        // 解析子进程的退出状态,更新g_last_code        if (WIFEXITED(status)) {            // 正常退出:获取退出码            g_last_code = WEXITSTATUS(status);        } else if (WIFSIGNALED(status)) {            // 被信号终止(如Ctrl+C → SIGINT,kill -9 → SIGKILL)            g_last_code = 128 + WTERMSIG(status);  // 符合Linux标准(如128+2=130)        }    }}

效果演示:

用户输入ls -l,执行流程:

  1. Shell 判断ls是外部命令,fork子进程;

  2. 子进程调用execvp("ls", ["ls", "-l", NULL]),从PATH找到/bin/ls,替换为ls程序;

  3. ls执行完毕后退出,父进程waitpid获取退出码(0表示成功)

  4. g_last_code更新为0,下次执行echo $?会输出0。

2.5模块5:环境变量初始化 —— 继承父进程的 “配置”

Shell启动时,需要继承父进程的环境变量(如PATH/HOME/USER),这些环境变量存储在全局数组environ中(无需手动定义,只需用extern声明)。我们可以在Shell启动时,打印关键环境变量(可选),确保继承正常。

代码实现(环境变量初始化):

c

// 初始化环境变量(打印关键变量,验证继承)voidinit_env(){    printf("=== 迷你Shell启动 ===");    // 打印关键环境变量(可选,用于调试)    char* path = getenv("PATH");    char* home = getenv("HOME");    char* user = getenv("USER");    if (path != NULLprintf("\nPATH: %s", path);    if (home != NULLprintf("\nHOME: %s", home);    if (user != NULLprintf("\nUSER: %s", user);    printf("\n====================\n\n");    // 初始化上次工作目录(启动时的当前目录)    get_current_dir();    strncpy(g_last_pwd, g_pwd, BUF_SIZE);}
三、迷你Shell完整源码与运行演示

将上述模块整合为完整代码(mini_shell.c),添加main函数实现 “无限循环” 的核心逻辑,再编译运行验证功能。

3.1完整源码

c

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <ctype.h>#include <sys/types.h>#include <sys/wait.h>// 全局常量定义#define BUF_SIZE 1024    // 命令缓冲区大小#define ARGV_MAX 64      // 命令参数最大个数// 全局变量定义char g_pwd[BUF_SIZE] = {0};       // 当前工作目录char g_last_pwd[BUF_SIZE] = {0};  // 上次工作目录(用于cd -)char* g_argv[ARGV_MAX] = {0};     // 命令参数数组int g_argc = 0;                   // 命令参数个数int g_last_code = 0;              // 上次命令的退出码extern char **environ;            // 全局环境变量数组// ------------------------------ 模块1:命令行提示符 ------------------------------staticcharget_username(){    char* username = getenv("USER");    return (username == NULL) ? "unknown" : username;}staticcharget_hostname(){    char* hostname = getenv("HOSTNAME");    return (hostname == NULL) ? "unknown-host" : hostname;}staticcharget_current_dir(){    if (getcwd(g_pwd, BUF_SIZE) == NULL) {        perror("getcwd failed");        return "unknown-dir";    }    static char pwd_env[BUF_SIZE] = {0};    snprintf(pwd_env, BUF_SIZE, "PWD=%s", g_pwd);    putenv(pwd_env);    return g_pwd;}staticcharsimplify_dir(char* full_dir){    if (full_dir == NULL || strcmp(full_dir, "/") == 0) {        return "/";    }    char* last_slash = strrchr(full_dir, '/');    return (last_slash == NULL) ? full_dir : (last_slash + 1);}voidprint_prompt(){    char* username = get_username();    char* hostname = get_hostname();    char* full_dir = get_current_dir();    char* simple_dir = simplify_dir(full_dir);    printf("[%s@%s %s]$ ", username, hostname, simple_dir);    fflush(stdout);}// ------------------------------ 模块2:命令获取与解析 ------------------------------staticvoidtrim_space(char* str){    if (str == NULLreturn;    char* start = str;    while (isspace(*start)) start++;    char* end = str + strlen(str) - 1;    while (end >= start && isspace(*end)) end--;    *(end + 1) = '\0';    memmove(str, start, end - start + 2);}intget_command(char* command_buf, int buf_size){    if (fgets(command_buf, buf_size, stdin) == NULL) {        printf("\n");        return -1;    }    command_buf[strcspn(command_buf, "\n")] = '\0';    trim_space(command_buf);    if (strlen(command_buf) == 0) {        return 0;    }    return 1;}voidparse_command(char* command_buf){    memset(g_argv, 0sizeof(g_argv));    g_argc = 0;    char* token = strtok(command_buf, " ");    while (token != NULL && g_argc < ARGV_MAX - 1) {        g_argv[g_argc++] = token;        token = strtok(NULL" ");    }    g_argv[g_argc] = NULL;}// ------------------------------ 模块3:内建命令处理 ------------------------------intis_builtin_command(){    if (g_argc == 0 || g_argv[0] == NULLreturn 0;    const char* builtin_list[] = {"cd""export""env""echo"NULL};    for (int i = 0; builtin_list[i] != NULL; i++) {        if (strcmp(g_argv[0], builtin_list[i]) == 0) {            return 1;        }    }    return 0;}staticvoidexec_cd(){    char* target_dir = NULL;    strncpy(g_last_pwd, g_pwd, BUF_SIZE);    if (g_argc == 1) {        target_dir = getenv("HOME");    } else if (strcmp(g_argv[1], "~") == 0) {        target_dir = getenv("HOME");    } else if (strcmp(g_argv[1], "-") == 0) {        target_dir = g_last_pwd;        printf("%s\n", target_dir);    } else {        target_dir = g_argv[1];    }    if (chdir(target_dir) == -1) {        perror("cd failed");        g_last_code = 1;    } else {        g_last_code = 0;    }}staticvoidexec_export(){    if (g_argc != 2) {        fprintf(stderr, "用法:export KEY=VALUE\n");        g_last_code = 2;        return;    }    if (strchr(g_argv[1], '=') == NULL) {        fprintf(stderr, "错误:export参数必须包含'='\n");        g_last_code = 2;        return;    }    if (putenv(g_argv[1]) != 0) {        perror("export failed");        g_last_code = 1;    } else {        g_last_code = 0;    }}staticvoidexec_env(){    for (int i = 0; environ[i] != NULL; i++) {        printf("%s\n", environ[i]);    }    g_last_code = 0;}staticvoidexec_echo(){    if (g_argc < 2) {        printf("\n");        g_last_code = 0;        return;    }    char* content = g_argv[1];    if (content[0] == '$') {        if (strcmp(content, "$?") == 0) {            printf("%d\n", g_last_code);        } else {            char* var_name = content + 1;            char* var_value = getenv(var_name);            if (var_value != NULL) {                printf("%s\n", var_value);            }        }    } else {        for (int i = 1; i < g_argc; i++) {            printf("%s ", g_argv[i]);        }        printf("\n");    }    g_last_code = 0;}voidexec_builtin_command(){    if (strcmp(g_argv[0], "cd") == 0) {        exec_cd();    } else if (strcmp(g_argv[0], "export") == 0) {        exec_export();    } else if (strcmp(g_argv[0], "env") == 0) {        exec_env();    } else if (strcmp(g_argv[0], "echo") == 0) {        exec_echo();    }}// ------------------------------ 模块4:外部命令执行 ------------------------------voidexec_external_command(){    pid_t pid = fork();    if (pid == -1) {        perror("fork failed");        g_last_code = 1;        return;    }    if (pid == 0) {        execvp(g_argv[0], g_argv);        perror("command not found");        exit(127);    } else {        int status;        waitpid(pid, &status, 0);        if (WIFEXITED(status)) {            g_last_code = WEXITSTATUS(status);        } else if (WIFSIGNALED(status)) {            g_last_code = 128 + WTERMSIG(status);        }    }}// ------------------------------ 模块5:环境变量初始化 ------------------------------voidinit_env(){    printf("=== 迷你Shell启动 ===");    char* path = getenv("PATH");    char* home = getenv("HOME");    char* user = getenv("USER");    if (path != NULLprintf("\nPATH: %s", path);    if (home != NULLprintf("\nHOME: %s", home);    if (user != NULLprintf("\nUSER: %s", user);    printf("\n====================\n\n");    get_current_dir();    strncpy(g_last_pwd, g_pwd, BUF_SIZE);}// ------------------------------ 主函数(核心循环) ------------------------------intmain(){    char command_buf[BUF_SIZE] = {0};    init_env();  // 初始化环境变量    // Shell核心循环:获取命令→解析→执行→循环    while (1) {        print_prompt();          // 1. 打印提示符        int ret = get_command(command_buf, BUF_SIZE);  // 2. 获取命令        if (ret == -1) {            // 用户按Ctrl+D,退出Shell            printf("=== 迷你Shell退出 ===\n");            break;        } else if (ret == 0) {            // 空行,跳过            continue;        }        parse_command(command_buf);  // 3. 解析命令        // debug_print_argv();  // 调试用:打印解析结果        if (is_builtin_command()) {  // 4. 执行内建命令            exec_builtin_command();        } else {                     // 5. 执行外部命令            exec_external_command();        }    }    return 0;}

3.2编译与运行演示

步骤1:编译代码

在Linux终端中,执行编译命令:

bash

gcc mini_shell.c -o mini_shell -Wall
  • -o mini_shell:指定输出文件名为mini_shell;

  • -Wall:显示所有警告(避免潜在错误)。

步骤2:运行迷你Shell

bash

./mini_shell

启动后会显示初始化信息和提示符:

plaintext

=== 迷你Shell启动 ===PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binHOME/home/ubuntuUSER: ubuntu====================[ubuntu@localhost myshell]

步骤3:测试核心功能

1.测试内建命令cd:

bash

[ubuntu@localhost myshell]cd /home[ubuntu@localhost home]cd ubuntu[ubuntu@localhost ubuntu]cd -  # 切换到上次目录(/home)/home[ubuntu@localhost home]cd ~    # 切换到家目录(/home/ubuntu)[ubuntu@localhost ubuntu]

2.测试内建命令export与echo:

bash

[ubuntu@localhost ubuntu]$ export MY_VAR=hello_mini_shell[ubuntu@localhost ubuntu]$ echo $MY_VAR  # 打印环境变量hello_mini_shell[ubuntu@localhost ubuntu]$ echo $PATH    # 打印系统环境变量/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin[ubuntu@localhost ubuntu]$ echo hello world  # 打印文本hello world

3.内建命令env:

bash

[ubuntu@localhost ubuntu]$ env | grep MY_VAR  # 查看导出的环境变量MY_VAR=hello_mini_shell

4.测试外部命令ls与ps:

bash

[ubuntu@localhost ubuntu]$ ls -l  # 执行外部命令lstotal 4drwxrwxr-x 2 ubuntu ubuntu 4096 Oct  1 16:00 myshell[ubuntu@localhost ubuntu]$ ps  # 执行外部命令ps  PID TTY          TIME CMD 1234 pts/0    00:00:00 bash 5678 pts/0    00:00:00 mini_shell 5679 pts/0    00:00:00 ps

5.测试echo $?(查看退出码):

bash

[ubuntu@localhost ubuntu]ls -l  # 成功执行,退出码0[ubuntu@localhost ubuntu]echo $?0[ubuntu@localhost ubuntu]lss  # 命令不存在,退出码127command not found: lss[ubuntu@localhost ubuntu]echo $?127

6.退出迷你Shell:按Ctrl+D或输入exit(可扩展exit内建命令),Shell会退出:

bash

[ubuntu@localhost ubuntu]=== 迷你Shell退出 ===
四、进阶扩展:让迷你Shell更实用

当前的迷你Shell已实现核心功能,但还可以扩展以下进阶功能,使其更接近真实Shell(如bash):

4.1扩展1:支持命令别名(如ll=ls -l)

  • 实现思路:用哈希表(如struct alias_map)存储 “别名→原命令” 的映射;

  • 新增内建命令alias:alias ll='ls -l',将别名添加到哈希表;

  • 命令解析时:若命令是别名,替换为原命令(如ll→ls -l)。

4.2扩展2:支持命令补全(按Tab补全命令 / 路径)

  • 实现思路:监听Tab键(需关闭终端的 “行缓冲”,用tcsetattr修改终端属性);

  • 补全逻辑:若输入的是命令前缀(如l),遍历PATH目录,找出以l开头的命令(如ls/less);若输入的是路径前缀(如/hom),调用readdir遍历目录,补全为/home。

4.3扩展3:支持重定向(如ls > file.txt)

  • 实现思路:解析命令时识别>/<等重定向符号,拆分 “命令” 与 “重定向目标”;

  • 子进程执行前:调用open打开目标文件,用dup2重定向标准输出(stdout)或标准输入(stdin),再执行execvp。

4.4扩展4:支持管道(如ls | grep txt)

  • 实现思路:用pipe创建管道,fork两个子进程:

  1. 子进程1:执行ls,将标准输出重定向到管道写入端;

  2. 子进程2:执行grep txt,将标准输入重定向到管道读取端;父进程:等待两个子进程

  3. 退出,关闭管道两端。

五、总结:迷你Shell背后的 “进程控制逻辑”

这个迷你Shell虽然简单,但完全基于前面四篇文章的核心知识,是进程控制的 “集大成者”。我们可以用一张图总结其核心逻辑:

plaintext

用户输入 → 提示符(print_prompt)         ↓获取命令(get_command)→ 空行/EOF处理         ↓解析命令(parse_command)→ 生成g_argv数组         ↓判断命令类型:├─ 内建命令(is_builtin_command)→ Shell自身执行(如cd修改工作目录)└─ 外部命令 → fork子进程 → 子进程execvp替换 → 父进程waitpid回收         ↓更新退出码(g_last_code)→ 回到提示符,循环

通过这个实战,你应该能深刻理解:

  • 内建命令与外部命令的本质区别:是否需要修改Shell自身状态;

  • 进程控制的 “全链路” 应用:fork(创建子进程)→ exec(替换程序)→ waitpid(回收资源)→ exit(终止进程);

  • Linux Shell的工作原理:不是 “自己执行命令”,而是 “管理子进程执行命令” 的 “管家”。

至此,Linux进程控制系统系列文章已全部完成。从进程的创建、终止、等待、替换,到最终实现迷你Shell,我们走过了 “理论→实践” 的完整路径。希望你能亲手运行代码,修改扩展,真正将这些知识内化为自己的技能。

 文章声明 
本文部分素材整理自网络公开领域,版权归原作者所有,由LINUX工程师训练营排版成文,转载请注明出处,侵删。
新盟教育自2009年成立,至今已有16年的IT培训经验。在长期的发展过程中,我们始终秉持“以学生发展为宗旨,以教学质量为生命,以团队精神为法宝,以服务态度为基础”的理念,踏踏实实地开展教学工作。
新盟教育是华为HALP授权培训机构,也是腾讯课堂101认证机构,还曾与思科官方、阿里云官方有过合作。这些合作与授权,代表着行业对我们的认可。
在课程设置上,我们以华为、思科课程为主,同时也开设了Linux、红帽、K8s微服务等课程。为了让学员能更好地适应企业工作,我们还提供企业实操的选修知识讲座。通过这些课程,我们希望帮助学员掌握扎实的IT技能。
成立至今,我们已经为18万多名学员提供了IT技术教育和指导,向Cisco、Google、联想、方正等上百家知名企业输送了很多IT人才,在合作伙伴和学员中都收获了不错的评价。
如果你有志于在IT领域发展,新盟教育愿意成为你成长路上的助力,帮你实现职业目标。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-28 12:39:36 HTTP/2.0 GET : https://f.mffb.com.cn/a/476235.html
  2. 运行时间 : 0.275657s [ 吞吐率:3.63req/s ] 内存消耗:5,147.88kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=0ccad3630d4c80433f05df0222171d62
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000948s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001300s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.003413s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000663s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001337s ]
  6. SELECT * FROM `set` [ RunTime:0.000591s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001481s ]
  8. SELECT * FROM `article` WHERE `id` = 476235 LIMIT 1 [ RunTime:0.005746s ]
  9. UPDATE `article` SET `lasttime` = 1772253576 WHERE `id` = 476235 [ RunTime:0.026469s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 67 LIMIT 1 [ RunTime:0.004368s ]
  11. SELECT * FROM `article` WHERE `id` < 476235 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.006642s ]
  12. SELECT * FROM `article` WHERE `id` > 476235 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.008430s ]
  13. SELECT * FROM `article` WHERE `id` < 476235 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.019163s ]
  14. SELECT * FROM `article` WHERE `id` < 476235 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.011751s ]
  15. SELECT * FROM `article` WHERE `id` < 476235 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.011002s ]
0.279518s