跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C

Linux 进程程序替换和 exec 函数族

Linux 进程通过 exec 系列函数实现程序替换,即当前进程加载新可执行文件覆盖原有代码段和数据段,而不创建新进程。通常配合 fork 使用,父进程 fork 子进程后,子进程调用 exec 运行新程序。exec 函数族包括 execl、execv 等变体,支持不同参数传递方式(列表或向量)及环境变量指定。成功调用 exec 不会返回,失败则返回 -1。环境变量在 fork 时继承,可通过 putenv 添加或使用 execle/execvpe 完全替换。

微码行者发布于 2026/3/16更新于 2026/4/2610 浏览
Linux 进程程序替换和 exec 函数族

进程程序替换和 exec 函数族

0. 前言

在 Linux 中,进程除了能通过 fork 创建子进程外,还可以通过 exec 系列函数进行进程替换。所谓替换,就是让一个正在运行的进程丢掉原来的程序映像,转而执行另一个可执行文件。

这正是命令行运行程序、Shell 调用脚本的底层机制。本文将通过实例,从单进程替换到 fork+exec 的组合,再到不同的 exec 接口,逐步剖析这一机制的原理与用法。


1. 单进程的进程替换

  • 先简单看一下单进程进程替换的现象,代码如下:

Linux 为我们提供了一些列系统调用,用于进行进程替换!

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    printf("before: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
    execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
    printf("after: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
    return 0;
}

注意:这里 execl("/usr/bin/ls", "ls", "-a", "-l", NULL); 是 exec 系列函数的标准写法,方便记忆

  • execl("/usr/bin/ls", "ls", "-a", "-l", NULL); :第一个参数是可执行文件的路径,中间是可变参数列表,用于指明程序执行的参数,最后一个参数必须是 NULL
  • 执行现象如下:

在这里插入图片描述

总结现象:

  • execl 参数中的命令和选项被执行了
  • 只执行了 execl 调用前的 printf("before ...") 函数,调用 execl 之后的 printf("after ...") 没有执行
  • 我们自己的进程,可以在运行时,运行其他路径的程序,这种现象就叫进程替换

2. 进程替换的原理

在 Linux 中,当我们在命令行运行一个程序时,本质上经历了以下几个步骤:

1. 进程的创建

  1. 用户在 bash 中输入一条命令。
  2. bash 进程调用 fork() 创建一个子进程,这个子进程就是将要执行用户命令的进程。
  3. 操作系统为该子进程分配必要的资源,包括:
    • PCB(进程控制块):保存进程的标识、状态、寄存器等信息。
    • 进程地址空间:包含代码段、数据段、堆、栈等区域。
    • 页表:建立虚拟地址与物理地址的映射关系。

2. 可执行程序的加载

当子进程被创建后,bash 会调用 exec 系列函数,加载用户指定的可执行程序。 在这一过程中:

  • 原进程在物理内存中的 代码段和数据段会被新程序替换,发生了写时拷贝。
  • 操作系统会 重新建立页表,将新程序的虚拟地址映射到新的物理内存页。
  • 虚拟地址空间本身的结构保持不变(依然分为代码段、数据段、堆、栈),但内容会根据新程序进行调整。
  • PCB 不会被替换,它仍然是原来的进程控制块,只是内部记录的信息被更新。

因此,调用 exec 系列函数不会创建新进程,而是让当前进程'脱胎换骨',从此开始运行新的程序。

3. 程序入口地址的确定

一个关键问题是:CPU 如何知道新程序应该从哪里开始执行?

答案在于 ELF(Executable and Linkable Format,可执行与可链接格式):

Linux 中形成的可执行程序,是有特定格式的,Linux 中的可执行程序的格式为 ELF

  • 在程序源代码编译生成 ELF 文件时,编译器会将程序的 入口地址(entry point) 写入到 ELF 文件的头部(表头),可执行程序的入口地址存在表头中。
  • 当 exec 将 ELF 加载到内存后,操作系统会读取 ELF 头部信息,从而得到程序的入口地址。
  • CPU 在调度该进程运行时,会将指令寄存器(IP/EIP/RIP)设置为入口地址,从这个位置开始执行程序代码。

4. 总结

在这里插入图片描述

  • fork() 用于创建子进程。
  • exec() 用于替换子进程的内存映像,加载并运行新程序。
    • exec 系列函数的做法十分简单粗暴
      • 调用 exec 系列函数时,直接用新程序的代码替换原来进程的代码,用新程序的数据,替换原来进程的数据,并让 CPU 执行新程序的代码开始
      • 该过程中没有创建新进程,原进程的 PCB、进程地址空间都保持不变,页表中的相应字段被更新
  • 进程替换后:
    • PCB 保留(进程还是原来的进程)。
    • 虚拟地址空间结构不变,但内容被新程序覆盖。
    • 页表更新,建立新的虚拟地址到物理地址的映射。
    • 入口地址由 ELF 文件头部提供,CPU 从此位置开始执行新程序。

最终,用户在命令行中输入的程序就能在 CPU 上开始运行。


3. 多进程的程序替换

  • 代码演示
// 多进程 程序替换
int main() {
    pid_t id = fork();
    if (id == 0) {
        // child
        printf("before: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        sleep(5);
        execl("/usr/bin/ls", "ls", "-a", "-l", NULL);
        printf("after: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        exit(0);
    }
    // father
    pid_t ret = waitpid(id, NULL, 0); // 等待子进程,暂不关心进程退出状态,阻塞等待
    if (ret > 0) {
        printf("wait success, father: %d, ret %d\n", getpid(), ret);
        sleep(5);
    }
    return 0;
}

在这里插入图片描述

问题与总结:

1. 子进程被替换会不会影响父进程?

不会。

  • 父子进程是独立的两个进程,它们各自拥有独立的虚拟地址空间。
  • 当子进程调用 exec 系列函数时,替换的只是该子进程的用户空间代码和数据,不会对父进程的执行造成影响。
  • 父进程依然可以通过 wait/waitpid 等方式监控子进程的退出状态。

2. 进程替换是否创建新进程?

进程替换不创建新进程,只进行进程代码和数据的替换

  • exec 并不会创建新进程,它只会在当前进程的上下文中加载并运行一个新的程序。
  • 因此:
    • 进程 ID(PID)保持不变。
    • PCB 仍然是原来的,只是其中的代码段、数据段、堆、栈等信息被新程序替换。
  • 所以,exec 本质上是'用另一个程序替换自己',而不是启动一个新的进程。

3. fork 与 exec 的关系

  • 调用 fork() 后,子进程和父进程最初运行的是相同的程序(代码空间一致,但地址空间独立)。
  • 在很多场景下,子进程会立刻调用 exec,以执行一个全新的程序。

这样形成了经典的模式:

父进程: 继续原有逻辑

子进程: fork → exec,替换为用户指定的程序


4. exec 调用后的代码执行情况

  • 替换成功时:
    • 进程的代码和数据完全被新程序替换。
    • CPU 的指令寄存器被设置为新程序的入口地址。
    • exec 调用点之后的代码不会被执行。
  • 替换失败时:
    • exec 会返回 -1,并设置 errno 表示错误原因。
    • 此时,调用 exec 之后的代码才有机会被执行。

5. exec 系列函数的返回值

  • exec没有成功返回值。
  • 如果成功执行了替换,原来的程序逻辑已经不存在,因此不可能返回到原调用点。
  • 只有在加载失败时,exec 才会返回 -1。

总结

  • 子进程的替换不会影响父进程,它们的执行空间相互独立。
  • 进程替换不创建新进程,只是在原有进程中加载新程序。
  • fork 与 exec 常常配合使用:fork 创建子进程,exec 让子进程运行新程序。
  • exec 成功执行后,调用点之后的代码不会被执行;只有失败时才会返回 -1 并继续向下执行,exec 系列函数无成功时的返回值。

4. 验证各种程序替换接口 exec

1. exec 系列函数概述

在 Linux 中,exec 系列函数用于 用一个新的程序替换当前进程的映像。

  • 调用成功后,当前进程的代码段、数据段、堆、栈都会被新程序替换,不会创建新进程(这点和 fork 不同)。
  • 调用成功时 不会返回;若调用失败,才会返回 -1。

它们主要定义在头文件:

#include <unistd.h>

在这里插入图片描述


2. exec 系列函数族

常见的 exec 系列库函数有:

// 以下为库函数
int execl(const char *path, const char *arg, ... /* (char *) NULL */);
int execlp(const char *file, const char *arg, ... /* (char *) NULL */);
int execle(const char *path, const char *arg, ... /* (char *) NULL, char *const envp[] */);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execve(const char *path, char *const argv[], char *const envp[]);
// 以下为系统调用
int execvpe(const char *file, char *const argv[], char *const envp[]);
  • 以上函数,如果调用成功则加载新的程序从启动代码开始执行,不再返回。
  • 如果调用出错则返回 -1
  • 所以 exec 函数只有出错的返回值,没有成功的返回值

命名规律总结:

exec 系列函数的后缀有规律:

后缀含义
l参数以列表(list)的形式传递(execl, execlp, execle),传参时直接写 "arg1", "arg2", ..., NULL,必须以NULL结尾。
v参数以向量(vector,数组)的形式传递(execv, execvp, execvpe),传入 char *argv[]。
p函数名中的 p:即 PATH。代表默认在 PATH 环境变量搜索可执行文件(execlp, execvp, execvpe),传参时无需传入路径,只需传入要执行的程序名
e允许指定新的环境变量 envp[](execle, execvpe)。

记忆口诀:l = list, v = vector, p = path, e = environment


3. 函数参数说明和使用

  • exec 系列所有函数:
    • 第一个参数:帮助函数找到该程序,因此传入程序的绝对路径或相对路径或程序名
    • 第二个参数:告诉程序如何执行,因此传入程序执行所需的参数
(1) path 和 file
  • path:需要给出 可执行文件的绝对路径或相对路径,例如 /usr/bin/ls。
  • file:只需要给出文件名,例如 ls,函数会在 PATH 环境变量 指定的路径中搜索程序。

例如:

execl("/usr/bin/ls", "ls", "-a", "-l", NULL); // 直接指定路径
execlp("ls", "ls", "-a", "-l", NULL); // 默认在环境变量 PATH 搜索,只需传入程序名

(2) argv / arg
  • argv 和 arg 都表示 传递给新程序的参数列表。
  • 以 NULL 结尾 表示参数结束。

示例:

char *const myargv[] = {"ls", "-a", "-l", NULL};
execv("/usr/bin/ls", myargv);

(3) envp
  • execle 和 execvpe 可以显式指定环境变量;
  • 其他函数(如 execl, execv, execlp, execvp)会默认继承调用进程的环境变量。

envp 表示 环境变量列表,即一个以 NULL 结尾的字符串数组:

// 自定义的环境变量
char *envp[] = {"PATH=/usr/bin", "USER=guest", NULL};
execle("/usr/bin/ls", "ls", "-a", "-l", NULL, envp);
char *myargv[] = {"ls", "-a", "-l", NULL};
execvpe("ls", myargv, envp);

基本用法:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main() {
    printf("Before exec...\n");
    // ... 进程替换的接口
    perror("exec failed");
    return 1;
}

execl:

// 方法 1: execl (指定路径 + 列表传参)
execl("/usr/bin/ls", "ls", "-a", "-l", NULL);

execlp:

// 方法 2: execlp (用 PATH 搜索)
execlp("ls", "ls", "-a", "-l", NULL);

execv:

// 方法 3: execv (指定路径 + 向量传参)
char *args[] = {"ls", "-a", "-l", NULL};
execv("/usr/bin/ls", args);

execle:

// 方法 4: execle (指定路径 + 参数 + 环境变量)
char *envp[] = {"PATH=/bin", NULL};
execle("/usr/bin/ls", "ls", "-a", "-l", NULL, envp);

总结:

  • 第一个参数 pathname/file:传入程序的绝对路径或相对路径或程序名
  • 后续如果是可变参数列表:命令行中怎么写,参数列表就怎么写,只不过空格分隔变成了逗号分隔,选项放在了双引号中
  • 根据需要可传入指定的环境变量
  • ls 以及其他程序是 C/C++ 写的程序,也有命令行参数,exec 系列函数调用时,会把后面参数列表或argv[] 中的选项和 envp[],形成命令行参数表和环境变量表,传递给 ls 或其他程序的 main 函数

在 Linux 中,所有进程都是由已有进程 fork 出来的子进程。命令行运行程序时,Bash 先 fork 出子进程,再用 exec 系列函数把目标程序加载进内存运行。

exec 的作用是清空当前进程占用的物理内存空间,把磁盘上的可执行文件读入内存,并让 CPU 从新程序的入口开始执行,相当于内核提供的'程序加载器'。

4. Makefile 一次编译多个可执行程序

.PHONY: all
all: otherExe mycommand

mycommand: mycommand.c
	gcc -o $@ $^ -std=c99

otherExe: otherExe.cpp
	g++ -o $@ $^ -std=c++11

.PHONY: clean
clean:
	rm -f mycommand otherExe
  • .PHONY: all
    • 声明 伪目标 all,表示 all 不是一个文件名,而是一个逻辑目标。
    • 如果没有声明 .PHONY,当目录下存在一个叫 all 的文件时,make all 会误认为已经生成了目标而不执行命令。
  • all: otherExe mycommand
    • all 目标依赖于 otherExe 和 mycommand。
    • 执行 make all 时,会先尝试生成 otherExe,再生成 mycommand(顺序由 make 自行决定,但通常按依赖书写顺序来执行)。

  • 总结依赖关系图
all
├── otherExe (依赖于 otherExe.cpp)
└── mycommand (依赖于 mycommand.c)
  • make 或 make all:同时编译 mycommand 和 otherExe。
  • make mycommand:只编译 mycommand。
  • make otherExe:只编译 otherExe。
  • make clean:清理编译产物。
跨语言调用
  • exec 接口调用我们自己的写的可执行程序以及调用其他语言形成的可执行程序:
// 执行我们自己写的程序 用 C 语言程序 调用 C++ 程序
execl("./otherExe", "otherExe", NULL);
// C 语言程序调用 shell 脚本
execl("/usr/bin/bash", "bash", "test.sh", NULL);
// C 语言程序调用 python 脚本
execl("/usr/bin/python3", "python3", "test.py", NULL);

'所有语言编写的程序,运行起来本质都是进程'。


为什么可执行程序或脚本能跨语言调用?

  • 程序运行后在操作系统看来都是 进程,无论是 C、Go 编译的二进制,还是 Python、Shell 脚本。
  • 跨语言调用的本质就是 一个进程调用或替换成另一个进程。
  • 操作系统不关心语言,只负责加载和运行进程。

👉 所以所有语言写的程序都能互相调用。

5. 一个程序调另一个程序验证命令行参数的传递

  • mycommand 程序向 otherExe 传递命令行参数
// mycommand.c
int main() {
    pid_t id = fork();
    if (id == 0) {
        // child
        printf("before: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        sleep(3);
        char *const myargv[] = {"otherExe", "-a", "-b", NULL};
        execv("./otherExe", myargv);
        printf("after: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        exit(0);
    }
    // father
    pid_t ret = waitpid(id, NULL, 0); // 等待子进程,暂不关心进程退出状态,阻塞等待
    if (ret > 0) {
        printf("wait success, father: %d, ret %d\n", getpid(), ret);
        sleep(3);
    }
    return 0;
}
// otherExe.cpp
#include <iostream>
using namespace std;

int main(int argc, char *argv[]) {
    cout << argv[0] << " begin running" << endl;
    for (int i = 0; argv[i]; ++i) {
        cout << i << " : " << argv[i] << endl;
    }
    cout << argv[0] << " otherExe stop running" << endl;
    return 0;
}

在这里插入图片描述

6. 一个程序调另一个程序验证环境变量的传递

  • mycommand 程序向 otherExe 传递环境变量
extern char **environ;
execle("./otherExe", "otherExe", "-a", "-b", NULL, environ); // 传递系统的环境变量
#include <iostream>
using namespace std;

int main(int argc, char *argv[], char *env[]) {
    cout << "这是命令行参数" << endl;
    cout << argv[0] << " begin running" << endl;
    for (int i = 0; argv[i]; ++i) {
        cout << i << " : " << argv[i] << endl;
    }
    cout << "这是环境变量" << endl;
    for (int i = 0; env[i]; ++i) {
        cout << i << " : " << env[i] << endl;
    }
    cout << argv[0] << " otherExe stop running" << endl;
    return 0;
}

在这里插入图片描述

7. 给子进程传递新的环境变量

传递新的环境变量有两种方式

  • 添加新的环境变量
  • 完全替换原来的环境变量
putenv 添加新的环境变量

putenv("MYPRIVATE_ENV=123456"); putenv 是为当前进程添加环境变量,不影响父进程中的环境变量

在这里插入图片描述

int main() {
    pid_t id = fork();
    putenv("MYPRIVATE_ENV=123456");
    if (id == 0) {
        // child
        printf("before: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        sleep(3);
        char *const myargv[] = {"otherExe", "-a", "-b", NULL};
        execv("./otherExe", myargv);
        printf("after: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        exit(0);
    }
    // father
    pid_t ret = waitpid(id, NULL, 0); // 等待子进程,暂不关心进程退出状态,阻塞等待
    if (ret > 0) {
        printf("wait success, father: %d, ret %d\n", getpid(), ret);
        sleep(3);
    }
    return 0;
}

在这里插入图片描述

完全替换掉从父进程继承下来的环境变量
int main() {
    pid_t id = fork();
    extern char **environ;
    if (id == 0) {
        // child
        printf("before: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        sleep(3);
        // execle("./otherExe", "otherExe", "-a", "-b", NULL, environ); // 传系统的环境变量
        // 传自定义的环境变量 会完全覆盖从系统继承下来的环境变量
        char *const myenv[] = {"MYVAL=123456", "MYPATH=/usr/bin/xxx", NULL};
        execle("./otherExe", "otherExe", "-a", "-b", NULL, myenv);
        printf("after: I am a process pid: %d, ppid: %d\n", getpid(), getppid());
        exit(0);
    }
    // father
    pid_t ret = waitpid(id, NULL, 0); // 等待子进程,暂不关心进程退出状态,阻塞等待
    if (ret > 0) {
        printf("wait success, father: %d, ret %d\n", getpid(), ret);
        sleep(3);
    }
    return 0;
}

在这里插入图片描述

  • 使用 exec 系列函数中带 e 的接口时(execle, execvpe),手动传入新的环境变量,会覆盖掉从父进程继承下来的环境变量
  • exec 系列函数所有接口的调用关系
    • execve 是系统调用,头文件为 <unistd.h>
    • 其他 exec 函数是库函数,头文件为 <stdlib.h>,底层调用 execve

在这里插入图片描述


5. 环境变量与进程的关系

1. 环境变量的本质

  • 环境变量也是数据,存放在进程的用户空间中。
  • 每个进程在运行时,都有一份属于自己的环境变量表。

2. 环境变量的继承

  • 当父进程通过 fork 创建子进程时,环境变量表会被一同复制到子进程的地址空间。
  • 因此,子进程天生就继承了父进程的环境变量。
  • 这意味着:环境变量是在 子进程创建阶段 就已经传递下去的,而不是运行后再赋值的。

3. 环境变量的访问方式

  • 在 main 函数中,可以通过第三个参数 char* envp[] 访问环境变量;
  • 即使不依赖 main 的参数,也可以通过 全局变量:
extern char **environ;

直接获取和操作当前进程的环境变量表。


✅ 总结一句话: 环境变量是进程运行时的一部分数据,创建子进程时会自动继承父进程的环境变量表;无论通过 main 参数还是 environ 变量,都可以访问和修改它。


6. 结语

进程替换并不会创建新进程,而是在原进程中装载新程序。配合 fork 使用,就形成了'父进程继续执行,子进程运行新程序'的经典模式。

理解 exec 系列函数,有助于把握 Linux 程序执行的本质:所有程序运行到最后都是进程,而进程既可以继承,也可以替换。这正是 Linux 灵活而高效的关键所在。

目录

  1. 进程程序替换和 exec 函数族
  2. 0. 前言
  3. 1. 单进程的进程替换
  4. 2. 进程替换的原理
  5. 1. 进程的创建
  6. 2. 可执行程序的加载
  7. 3. 程序入口地址的确定
  8. 4. 总结
  9. 3. 多进程的程序替换
  10. 1. 子进程被替换会不会影响父进程?
  11. 2. 进程替换是否创建新进程?
  12. 3. fork 与 exec 的关系
  13. 4. exec 调用后的代码执行情况
  14. 5. exec 系列函数的返回值
  15. 总结
  16. 4. 验证各种程序替换接口 exec
  17. 1. exec 系列函数概述
  18. 2. exec 系列函数族
  19. 3. 函数参数说明和使用
  20. (1) path 和 file
  21. (2) argv / arg
  22. (3) envp
  23. 4. Makefile 一次编译多个可执行程序
  24. 跨语言调用
  25. 5. 一个程序调另一个程序验证命令行参数的传递
  26. 6. 一个程序调另一个程序验证环境变量的传递
  27. 7. 给子进程传递新的环境变量
  28. putenv 添加新的环境变量
  29. 完全替换掉从父进程继承下来的环境变量
  30. 5. 环境变量与进程的关系
  31. 1. 环境变量的本质
  32. 2. 环境变量的继承
  33. 3. 环境变量的访问方式
  34. 6. 结语
  • 💰 8折买阿里云服务器限时8折了解详情
  • 💰 8折买阿里云服务器限时8折购买
  • 🦞 5分钟部署阿里云小龙虾了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 驱动的自动化运维机器人:从数字劳动力到智能协作者
  • Python 和 PyCharm 安装配置指南
  • 学习 Python 推荐的 10 个优质技术资源网站
  • 攻防世界 Web 题解:Lottery 与 ics-05 漏洞分析
  • AI 智能体驾驭工程(Harness Engineering)全解析
  • Python 常用库指南:数值计算、可视化、机器学习等 8 大领域
  • 基于腾讯云 CVM 部署 Clawdbot 并对接 Telegram 机器人
  • Dubbo 服务调试与管理实用命令指南
  • Java 与 LangGraph 学习路线
  • GitHub Copilot Workspace 100 万 Token 上下文评测
  • 基于多 AI 模型并行的内容生成与对比分析工作流
  • Ubuntu Datasophon 1.2.1 集成 ClickHouse 组件解决方案
  • Apache IoTDB 时序数据库核心特性与选型指南
  • Vue3 组件方法调用无效或提示不存在的排查与修复
  • 2024 年大模型方向求职面试经验总结与指南
  • 递归算法:原理、设计与经典例题解析
  • Ubuntu 远程桌面安装配置指南
  • Whisper 语音转文字本地化使用指南
  • GitHub 2FA 丢失 Microsoft Authenticator 恢复与重置指南
  • OpenClaw 进阶教程:记忆系统、定时任务、多模型切换与子代理配置

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online