[{"content":"前言 作为 C/C++ 开发者，你手上最强大的代码审查工具不是某个第三方软件，而是你每天都在用的编译器本身。GCC 和 Clang 都内置了完整的静态分析和动态分析能力，零成本、零额外依赖。\n本文聚焦两类工具：\n静态分析（不运行程序）：编译器警告、GCC -fanalyzer、Clang Static Analyzer (--analyze / scan-build)、clang-tidy 动态分析（运行程序时捕获问题）：AddressSanitizer (ASan)、ThreadSanitizer (TSan)、MemorySanitizer (MSan)、UndefinedBehaviorSanitizer (UBSan) cppcheck、Valgrind 等第三方工具仅在文末作对比参考。\n第一部分：静态分析（编译器自带） 1.1 编译器警告：零成本第一道防线 警告不是可选的，-Werror 是 CI 和本地开发的底线（发布构建可以酌情关闭，避免新编译器版本引入的新警告中断发布流程）。\n推荐的最小警告集（GCC 和 Clang 通用） # 所有构建必开 -Wall -Wextra -Wpedantic -Werror # 推荐再加这些 # 注意：GCC 中 -Wconversion 不包含有符号/无符号隐式转换，需单独加 -Wsign-conversion # Clang 中 -Wconversion 默认包含两者 -Wshadow -Wconversion -Wsign-conversion -Wnull-dereference -Wformat=2 -Wundef -Wunused -Wcast-align -Wcast-qual -Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast GCC 特有警告 选项 起始版本 说明 -Weffc++ 很早 Effective C++ 规则集 -Wstringop-overflow=4 GCC 11 字符串操作溢出（需 -O2 才触发，级别 1-4，数字越大越严格） -Wdangling-reference GCC 13 悬垂引用检测 -Wstrict-overflow=5 GCC 4.x 有符号整数溢出假设（需 -O2 才触发，级别 1-5） -Warray-bounds 很早 数组越界（需 -O2 才触发） Clang 特有警告 选项 说明 -Weverything 启用所有警告（不要在 CI 用，新警告会断构建） -Wno-c++98-compat 忽略 C++98 兼容性（现代项目通常不需要） -Wdocumentation Doxygen 文档注释格式检查 -Wthread-safety 线程安全注解检查（代码中需加注解） 1.2 GCC -fanalyzer：路径敏感静态分析 GCC 10 引入，GCC 11 完全重写。基于符号执行，能发现跨函数、跨路径的深度 Bug。\n# GCC 10+ 支持。版本号可选，直接 gcc 也可以 gcc-14 -fanalyzer -O0 -g -c test.c gcc -fanalyzer -O1 -g -c test.c # -O1 分析效果更好，-O0 也能用 它能发现普通警告抓不到的问题：\n#include \u0026lt;stdio.h\u0026gt; void process(int flag, FILE *f) { if (flag) fclose(f); // ... 其他逻辑 fclose(f); // flag 为真时 double-fclose！-Wall 不会报 } 主要检查项（Wanalyzer-*） 警告 检测内容 -Wanalyzer-double-free 双重释放 -Wanalyzer-use-after-free 释放后使用 -Wanalyzer-malloc-leak malloc 泄漏 -Wanalyzer-file-leak FILE* 泄漏 -Wanalyzer-null-dereference 空指针解引用 -Wanalyzer-tainted-array-index 污点数据用作数组索引 -Wanalyzer-mismatching-deallocation new/delete 与 malloc/free 不匹配 架构师提示 慢：比普通编译慢 5-20 倍，不要本地每次都开 C 支持好，C++ 有限（不支持异常处理） CI 每日构建或发布前跑即可 1.3 Clang Static Analyzer：深度路径分析 Clang 的路径敏感分析器，比 GCC -fanalyzer 更成熟（发展早了 10 年），C++ 支持完整。\n使用方式一：clang --analyze（单文件，不生成目标文件） # Clang 3.x+ 就支持。版本号可选，直接 clang 也可以 clang-18 --analyze -Xanalyzer -analyzer-output=text test.c clang --analyze test.c 使用方式二：scan-build（拦截整个构建过程） # make 项目 scan-build make -j4 # CMake 项目：配置阶段不需要 scan-build（不编译） cmake -B build -S . # 只有构建阶段需要 scan-build 拦截编译器调用 scan-build cmake --build build -j4 # CI 中用：发现 Bug 返回非零退出码 scan-build --status-bugs make 主要 Checker 分类 分类 示例 core.* 空指针、除零、未初始化值 cplusplus.* new/delete 不匹配、placement new 问题 unix.* malloc/free 匹配、字符串操作错误 security.* 不安全 API（gets、strcpy）、浮点循环计数器 alpha.* 实验性：污点传播、pthread 锁误用 架构师提示 比 -fanalyzer 更慢（5-30x），但误报率更低 C++ 支持完整，报告质量好（HTML 可视化 + 完整路径轨迹） 每日构建深度扫描必开 1.4 clang-tidy：AST 级语义分析 基于 Clang 编译器前端，直接操作 AST。和前面两个路径敏感分析器不同，clang-tidy 是语法/语义级模式匹配，快得多。\n# 需要 compile_commands.json run-clang-tidy -p build -j$(nproc) # 单个文件 clang-tidy test.c -p build 核心 Checker 分类 模块前缀 用途 bugprone-* 易错模式（易被忽视的 Bug） modernize-* 现代 C++ 迁移（auto、range-for、smart ptr） performance-* 性能问题 readability-* 可读性 cppcoreguidelines-* C++ 核心准则 clang-analyzer-* 复用 Clang Static Analyzer 的部分 checker 架构师提示 快：接近编译速度，本地每次编译和 CI 每次提交都能跑 自动修复：约 50% 检查项支持 -fix 自动修正（⚠️ 风险提示：modernize-*、readability-* 等重构类 checker 在复杂代码上可能引入语义错误，自动修复后务必人工审查） 注意拼写：是 clang-tidy（中间有连字符），不是 clangtidy 静态分析结果的抑制方式 实际项目中总有少数情况工具会误报，或因历史遗留问题暂时无法修复。以下是各工具的抑制方式：\n1. clang-tidy：使用 NOLINT 注释\n// 抑制下一行的所有 clang-tidy 警告 char *p = malloc(1024); // NOLINT // 抑制指定的 checker char *p = malloc(1024); // NOLINT(cppcoreguidelines-no-malloc) // 抑制一个代码块 // NOLINTBEGIN void legacy_func() { /* ... */ } // NOLINTEND // 抑制指定 checker 的一个代码块 // NOLINTBEGIN(cppcoreguidelines-no-malloc, readability-identifier-naming) void legacy_func() { /* ... */ } // NOLINTEND 2. GCC/Clang 编译器警告：使用 #pragma\n// 临时禁用某个警告（GCC 和 Clang 通用语法） #pragma GCC diagnostic push #pragma GCC diagnostic ignored \u0026#34;-Wdeprecated-declarations\u0026#34; void legacy_func(); // 这里不会报 deprecated 警告 #pragma GCC diagnostic pop // Clang 专用（更精确） #pragma clang diagnostic push #pragma clang diagnostic ignored \u0026#34;-Wunused-variable\u0026#34; int x; // 这里不会报未使用变量 #pragma clang diagnostic pop 3. Clang Static Analyzer：使用 #ifndef __clang_analyzer__\n#ifndef __clang_analyzer__ // 这段代码 Clang SA 分析不到，可用于隐藏已知误报 #else // 也可以在这里放为了分析器而写的桩代码 #endif 和 Sanitizer 抑制一样的原则：每加一条抑制，都应该在旁边用注释写清楚为什么要抑制，并在 Bug 系统中记录对应的清理任务。\n1.5 静态工具关系图 速度（快 → 慢）: 编译器警告 → clang-tidy → GCC -fanalyzer → Clang Static Analyzer (0成本) (AST模式) (路径敏感+符号执行) (路径敏感+符号执行) | | | | 必开 必开 每日构建 每日构建 1.6 与 cppcheck 的对比（参考） 维度 clang-tidy GCC -fanalyzer Clang SA cppcheck 分析基础 Clang AST GCC GIMPLE IR Clang CFG 自建分析器 需编译上下文 ✅ ✅ ✅ ❌ 速度 编译 1-2x 编译 5-20x 编译 5-30x 快 C++ 支持 优秀 有限 优秀 部分 误报率 很低 中 低 中 结论：cppcheck 适合没有编译数据库或第三方代码的快速扫描，主力还是编译器自带工具。\n第二部分：动态分析（Sanitizer） Sanitizer 是编译器插桩的动态分析工具，在程序运行时捕获问题。编译时加 -fsanitize=xxx，然后正常运行即可。\n2.1 四大 Sanitizer 速查 工具 编译选项 检测内容 慢化 内存膨胀 AddressSanitizer (ASan) -fsanitize=address 越界、use-after-free、double-free、泄漏 ~2x ~3x ThreadSanitizer (TSan) -fsanitize=thread 数据竞争、死锁、锁误用 5x-15x 5x-10x MemorySanitizer (MSan) -fsanitize=memory 未初始化内存读取 ~3x 大 UndefinedBehaviorSanitizer (UBSan) -fsanitize=undefined 有符号溢出、非法移位、除零、对齐违规 \u0026lt;10% 很小 注意：ASan、TSan、MSan 三者互斥——它们都要对内存访问做全局插桩，且各自使用不同的 Shadow Memory 映射方案（ASan 用 1:8，TSan 需要额外的线程状态追踪），无法在同一个二进制中共存。UBSan 可以和 ASan 或 TSan 组合（它只在特定操作点插桩，不做通用内存访问插桩）。\n2.2 AddressSanitizer (ASan)：最常用的内存检查器 # 编译（UBSan 一起开，几乎零额外成本） gcc/clang -fsanitize=address,undefined -g -O0 -o asan_demo asan_demo.c # 运行（和正常运行一样） ./asan_demo 触发代码示例（asan_demo.c）：\n#include \u0026lt;stdlib.h\u0026gt; int main(void) { int *p = malloc(sizeof(int) * 3); p[0] = 1; free(p); p[0] = 42; // use-after-free！ASan 会抓到 return 0; } 编译运行后 ASan 会直接 abort 并打印：\n==ERROR: AddressSanitizer: heap-use-after-free on address 0x... WRITE of size 4 at 0x... thread T0 #0 0x... in main asan_demo.c:8 freed by thread T0 here: #0 0x... in free #1 0x... in main asan_demo.c:7 ASan 检测能力 错误类型 检测 堆/栈/全局数组越界 ✅ use-after-free ✅ use-after-scope ✅ double-free ✅ 内存泄漏（LSan） ✅（Linux 默认包含在 ASan 中，macOS 需单独 -fsanitize=leak。LSan 也可独立使用：-fsanitize=leak，无内存插桩，速度接近普通程序） 未初始化读取 ❌（用 MSan） ASan 常见错误模式 错误标识 触发代码 说明 heap-use-after-free int *p=malloc(4); free(p); *p=1; 释放后堆内存读写 heap-buffer-overflow int *p=malloc(4); p[2]=1; 堆缓冲区越界 stack-buffer-overflow int a[2]; a[5]=1; 栈缓冲区越界 global-buffer-overflow int a[2]; int main(){a[5]=1;} 全局变量越界 double-free int *p=malloc(4); free(p); free(p); 双重释放 alloc-dealloc-mismatch int *p=new int; free(p); new/delete 与 malloc/free 混用 use-after-scope int *p; {int x=1; p=\u0026amp;x;} *p=2; 栈变量离开作用域后使用 stack-use-after-return int *f(){int x=1; return \u0026amp;x;} 函数返回后使用栈地址（需显式开启：ASAN_OPTIONS=detect_stack_use_after_return=1，默认关闭以避免性能开销） detected-leaks (LSan) int *p=malloc(4); /* 不 free */ 内存泄漏（进程退出时检测） ASan 典型错误输出样例 以下是 ASan 报告各种错误时的实际输出片段（省略了部分调用栈细节）：\n1. heap-buffer-overflow（堆缓冲区越界）\n==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000014 at pc ... READ of size 4 at 0x602000000014 thread T0 #0 0x... in main demo.c:5 0x602000000014 is located 4 bytes to the right of 8-byte region [0x602000000010,0x602000000018) allocated by thread T0 here: #0 0x... in malloc 2. stack-buffer-overflow（栈缓冲区越界）\n==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffd... at pc ... WRITE of size 4 at 0x7ffd... thread T0 #0 0x... in main demo.c:4 Address 0x7ffd... is located in stack of thread T0 at offset 36 in frame #0 0x... in main demo.c:1 This frame has 1 object(s): [32, 40) \u0026#39;a\u0026#39; \u0026lt;== Memory access at offset 36 partially overflows this variable 3. double-free（双重释放）\n==ERROR: AddressSanitizer: attempting double-free on 0x602000000010 in thread T0: #0 0x... in free #1 0x... in main demo.c:6 0x602000000010 is located 0 bytes inside of 8-byte region [0x602000000010,0x602000000018) freed by thread T0 here: #0 0x... in free #1 0x... in main demo.c:5 previously allocated by thread T0 here: #0 0x... in malloc 4. use-after-scope（作用域后使用）\n==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffd... at pc ... WRITE of size 4 at 0x7ffd... thread T0 #0 0x... in main demo.c:7 Address 0x7ffd... is located in stack of thread T0 at offset 32 in frame #0 0x... in main demo.c:1 This frame has 2 object(s): [32, 36) \u0026#39;x\u0026#39; (line 3) \u0026lt;== Memory access at offset 32 is inside this variable 5. detected memory leaks（LSan 泄漏检测）\n==ERROR: LeakSanitizer: detected memory leaks Direct leak of 8 byte(s) in 1 object(s) allocated from: #0 0x... in malloc #1 0x... in main demo.c:3 SUMMARY: AddressSanitizer: 8 byte(s) leaked in 1 allocation(s). ⚠️ 重要风险提示 Sanitizer 二进制绝不能用于生产环境：插桩会导致 2-3x 性能下降和 3x 内存膨胀，且 Shadow Memory 的固定映射会降低地址空间随机性，削弱 ASLR 防护 与自定义分配器冲突：ASan 会拦截 malloc/free，如果代码链接了 jemalloc、tcmalloc 等自定义分配器，可能导致检测失效或崩溃 进程崩溃时泄漏检测不执行：LSan 在进程正常退出时才扫描泄漏，如果 ASan 先因为其他错误 abort，不会报告泄漏。可设置 ASAN_OPTIONS=abort_on_error=0 让程序继续运行到退出 各 Sanitizer 常用环境变量速查 以下是所有 Sanitizer 的常用环境变量汇总，放在一起便于查阅：\n# ASan 常用选项 export ASAN_OPTIONS=detect_leaks=1:halt_on_error=1 # detect_leaks=1 检测泄漏（Linux 默认开启） # halt_on_error=1 遇到第一个错误就 abort（默认继续运行） # fast_unwind_on_malloc=0 获得完整调用栈（更慢，但信息全） # detect_stack_use_after_return=1 检测栈变量返回后使用 # TSan 常用选项 export TSAN_OPTIONS=halt_on_error=1:second_deadlock_stack=1 # halt_on_error=1 遇到第一个错误就 abort # second_deadlock_stack=1 死锁检测时记录两端的调用栈 # MSan 常用选项 export MSAN_OPTIONS=halt_on_error=1:poison_in_dtor=1 # halt_on_error=1 遇到第一个错误就 abort # poison_in_dtor=1 在对象析构时标记内存为无效（防止析构后访问） # UBSan 常用选项 export UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 # print_stacktrace=1 打印完整调用栈（默认只打印行号） 2.3 ThreadSanitizer (TSan)：并发问题杀手 gcc/clang -fsanitize=thread -g -O1 -o tsan_demo tsan_demo.c ./tsan_demo 触发代码示例（tsan_demo.c）：\n#include \u0026lt;pthread.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; int shared = 0; void *worker(void *arg) { (void)arg; for (int i = 0; i \u0026lt; 10000; i++) { shared++; // 无锁并发写 → 数据竞争！ } return NULL; } int main(void) { pthread_t t1, t2; pthread_create(\u0026amp;t1, NULL, worker, NULL); pthread_create(\u0026amp;t2, NULL, worker, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); printf(\u0026#34;shared = %d\\n\u0026#34;, shared); return 0; } 编译运行后 TSan 会报告数据竞争：\nWARNING: ThreadSanitizer: data race (pid=12345) Write of size 4 at 0x... by thread T1: #0 thread_func /path/to/program.c:25 #1 start_thread Previous write of size 4 at 0x... by thread T2: #0 thread_func /path/to/program.c:25 #1 start_thread 架构师提示 TSan 和 ASan 不能同时用（两者都要对内存访问做全局插桩，Shadow Memory 映射方案冲突） 推荐 -O1 及以上（不推荐 -O0）：TSan 依赖编译期的内存访问分析 pass 来识别变量读写模式，-O0 下这些 pass 不执行会导致大量误报。技术上 -O0 也能运行，但实战不建议 非常慢（5-15x），CI 每日构建跑并发测试就行 ⚠️ 只能检测已执行路径：数据竞争必须在运行时被实际触发才会被检测到。如果测试覆盖率不足或竞争窗口未被触发，TSan 会漏报。不要因为 TSan 没报警就认为没有数据竞争 ⚠️ fork() 后失效：TSan 在 fork() 后的子进程中状态不正确，子进程中的竞争可能无法被检测 TSan 常见错误模式 错误标识 触发代码 说明 data race 两线程同时 shared++（无锁） 最常见：无同步的并发内存访问 lock-order-inversion 线程1: lock(A)→lock(B); 线程2: lock(B)→lock(A) 潜在死锁：锁获取顺序不一致 use-after-free 线程A free 后，线程B继续访问 跨线程的释放后使用 double-lock 同一线程对非递归 mutex 连续 lock 两次 重复加锁导致死锁 mutex-unlock-of-mutex-not-owned-by-thread 线程A lock，线程B unlock 解锁非本线程持有的锁 signal-unsafe-call 信号处理函数中调用 malloc/printf 信号处理函数中调用了非异步信号安全的函数 TSan 典型错误输出样例 1. data race（数据竞争）\nWARNING: ThreadSanitizer: data race (pid=12345) Write of size 4 at 0x... by thread T1: #0 worker /path/to/program.c:10 #1 start_thread Previous write of size 4 at 0x... by thread T2: #0 worker /path/to/program.c:10 #1 start_thread Location is global \u0026#39;shared\u0026#39; of size 4 at 0x... (program+0x...) Thread T1 (tid=..., running) created by main thread at: #0 pthread_create #1 main /path/to/program.c:18 SUMMARY: ThreadSanitizer: data race /path/to/program.c:10 in worker 2. lock-order-inversion（锁顺序反转/潜在死锁）\nWARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) (pid=...) Cycle in lock order graph: M1 (...) --\u0026gt; M2 (...) --\u0026gt; M1 (...) Mutex M1 acquired here while holding Mutex M2 in thread T1: #0 pthread_mutex_lock #1 thread1_func /path/to/program.c:25 Mutex M2 previously acquired by the same thread here: #0 pthread_mutex_lock #1 thread1_func /path/to/program.c:23 Mutex M2 acquired here while holding Mutex M1 in thread T2: #0 pthread_mutex_lock #1 thread2_func /path/to/program.c:38 SUMMARY: ThreadSanitizer: lock-order-inversion (potential deadlock) 3. double-lock（重复加锁）\nWARNING: ThreadSanitizer: double-lock of mutex M0 (pid=...) Mutex M0 is already held by thread T1: #0 pthread_mutex_lock #1 worker /path/to/program.c:15 And is being locked again: #0 pthread_mutex_lock #1 worker /path/to/program.c:17 SUMMARY: ThreadSanitizer: double-lock of mutex M0 4. mutex-unlock-of-mutex-not-owned-by-thread（解锁非本线程锁）\nWARNING: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread) (pid=...) #0 pthread_mutex_unlock #1 thread2_func /path/to/program.c:42 Mutex M0 was created at: #0 pthread_mutex_init #1 main /path/to/program.c:8 SUMMARY: ThreadSanitizer: unlock of an unlocked mutex (or by a wrong thread) 2.4 MemorySanitizer (MSan)：未初始化读取的终极武器 # 注意：只有 Clang 支持！GCC 从未实现 MSan clang++ -fsanitize=memory -stdlib=libc++ -g -O1 -o msan_demo msan_demo.cpp 触发代码示例（msan_demo.cpp）：\n#include \u0026lt;cstdio\u0026gt; int main() { int value; // 未初始化 if (value \u0026gt; 10) { // MSan 会抓到这里读取了未初始化内存 printf(\u0026#34;big\\n\u0026#34;); } else { printf(\u0026#34;small\\n\u0026#34;); } return 0; } 重要限制：MSan 通过 Shadow Memory 追踪每个字节的\u0026quot;有效值位\u0026quot;，因此所有代码包括依赖库都必须用 MSan 重新编译，否则未被插桩的库函数返回值都会被当作\u0026quot;未初始化\u0026quot;从而产生海量误报。这也是为什么必须用 -stdlib=libc++——系统自带的 libstdc++ 没有被 MSan 插桩过。这也是 MSan 没有 ASan 普及的最主要原因。\n⚠️ 额外提示 Origin Tracking 默认关闭：MSan 默认只报告\u0026quot;哪里读取了未初始化值\u0026quot;，不报告\u0026quot;这个值最初是从哪里来的\u0026quot;。需要加 -fsanitize-memory-track-origins=2（约 1.5x-2x 额外开销）才能看到完整的来源链 MSan 常见错误模式 错误标识 触发代码 说明 use-of-uninitialized-value int a; if(a\u0026gt;5){} 读取未初始化的栈/堆变量 uninitialized-condition int a; if(a){} 未初始化值用作条件判断 uninitialized-memory-copy int a; memcpy(\u0026amp;b,\u0026amp;a,4); 从未初始化内存拷贝数据 use-of-uninitialized-value (origin) fread(\u0026amp;a,4,1,fp); if(a\u0026gt;5){} 加 -fsanitize-memory-track-origins=2 时会显示来源链 MSan 典型错误输出样例 1. use-of-uninitialized-value（读取未初始化值）\n==ERROR: MemorySanitizer: use-of-uninitialized-value #0 0x... in main msan_demo.cpp:5 #1 0x... in __libc_start_main Uninitialized value was stored to memory at #0 0x... in main msan_demo.cpp:4 SUMMARY: MemorySanitizer: use-of-uninitialized-value msan_demo.cpp:5 in main 2. uninitialized-condition（未初始化值用作条件）\n==WARNING: MemorySanitizer: use-of-uninitialized-value #0 0x... in main msan_demo.cpp:5 (main) #1 0x... in __libc_start_main Uninitialized value was created by an allocation of \u0026#39;a\u0026#39; in the stack frame of function \u0026#39;main\u0026#39; #0 0x... in main msan_demo.cpp:3 SUMMARY: MemorySanitizer: use-of-uninitialized-value 3. 带 Origin Tracking 的输出（-fsanitize-memory-track-origins=2）\n==ERROR: MemorySanitizer: use-of-uninitialized-value #0 0x... in main msan_demo.cpp:8 #1 0x... in __libc_start_main Uninitialized value was stored to memory at #0 0x... in main msan_demo.cpp:6 Memory was marked as uninitialized #0 0x... in __interceptor_fread #1 0x... in main msan_demo.cpp:6 #2 0x... in __libc_start_main SUMMARY: MemorySanitizer: use-of-uninitialized-value msan_demo.cpp:8 in main 2.5 UndefinedBehaviorSanitizer (UBSan) # 和 ASan 一起开（推荐） gcc/clang -fsanitize=address,undefined -g -O0 -o ubsan_demo ubsan_demo.c # 单独开 gcc/clang -fsanitize=undefined -g -O0 -o ubsan_demo ubsan_demo.c 触发代码示例（ubsan_demo.c）：\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;limits.h\u0026gt; int main(void) { int a = INT_MAX; int b = a + 1; // 有符号整数溢出 → UB！UBSan 会抓到 printf(\u0026#34;INT_MAX + 1 = %d\\n\u0026#34;, b); int c = 1 \u0026lt;\u0026lt; 32; // 移位超界（int 通常 32 位）→ UB！ printf(\u0026#34;1 \u0026lt;\u0026lt; 32 = %d\\n\u0026#34;, c); return 0; } 运行时 UBSan 会输出：\nubsan_demo.c:7:15: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type \u0026#39;int\u0026#39; ubsan_demo.c:10:15: runtime error: shift exponent 32 is too large for 32-bit type \u0026#39;int\u0026#39; 常用子项（除标注外均包含在 undefined 里）：\n子项 检测内容 signed-integer-overflow 有符号整数溢出（UB！） shift 移位超界、负移位 integer-divide-by-zero 整数除零 float-divide-by-zero 浮点除零（不在 undefined 组，需显式加 -fsanitize=float-divide-by-zero；IEEE 754 定义了结果为 Inf，但某些场景仍需检测） null 空指针解引用 alignment 对齐违规 float-cast-overflow float 转 int 溢出（超出目标类型范围） vptr 虚表指针损坏（C++ 专用） bool 向 bool 类型加载非 0/1 的值（不在 undefined 组，需 -fsanitize=bool） enum 向枚举类型加载不在枚举范围内的值（不在 undefined 组，需 -fsanitize=enum） UBSan 常见错误模式 错误标识 触发代码 说明 signed integer overflow INT_MAX + 1 有符号整数溢出（最常见 UB） shift exponent 32 is too large 1 \u0026lt;\u0026lt; 32（32位 int） 移位位数 ≥ 类型宽度或为负 division by zero int a=1,b=0; a/b 整数除零 null pointer dereference int *p=NULL; *p=1 空指针解引用 member access within null pointer struct S*p=NULL; p-\u0026gt;x=1 通过空指针访问成员 misaligned address int *p=(int*)0x1; *p=1 指针未按类型对齐 value 256 is outside the range (unsigned char)256 不越界，但 float 转 int 超范围 浮点转整型溢出 downcast of address 基类指针指向基类对象却转派生类 非法虚表/对象类型不匹配（vptr） load of value 2 is not a valid value for type 'bool' bool b; memcpy(\u0026amp;b, \u0026quot;\\x02\u0026quot;, 1); if(b){} 向 bool 加载非 0/1 的值（需 -fsanitize=bool） load of value 5 is not a valid value for type 'enum E' enum E{A=1,B=2}; enum E e; memcpy(\u0026amp;e, \u0026quot;\\x05\u0026quot;, 1); 向枚举加载不在范围内的值（需 -fsanitize=enum） UBSan 典型错误输出样例 1. signed integer overflow（有符号整数溢出）\nubsan_demo.c:7:15: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type \u0026#39;int\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:7:15 in 2. shift exponent is too large（移位超界）\nubsan_demo.c:10:15: runtime error: shift exponent 32 is too large for 32-bit type \u0026#39;int\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:10:15 in 3. division by zero（除零）\nubsan_demo.c:5:10: runtime error: division by zero SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:5:10 in 4. null pointer dereference（空指针解引用）\nubsan_demo.c:6:5: runtime error: store to null pointer of type \u0026#39;int\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:6:5 in 5. member access within null pointer（通过空指针访问成员）\nubsan_demo.cpp:12:10: runtime error: member access within null pointer of type \u0026#39;struct S\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.cpp:12:10 in 6. misaligned address（对齐违规）\nubsan_demo.c:8:14: runtime error: store to misaligned address 0x... for type \u0026#39;int\u0026#39;, which requires 4 byte alignment 0x...: note: pointer points here SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:8:14 in 7. value outside the range of representable values（浮点转整型溢出）\nubsan_demo.c:9:25: runtime error: value 256.5 is outside the range of representable values of type \u0026#39;unsigned char\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:9:25 in 8. invalid vptr（非法虚表指针，C++ 专用）\nubsan_demo.cpp:15:12: runtime error: downcast of address 0x... which does not point to an object of type \u0026#39;Derived\u0026#39; 0x...: note: object is of type \u0026#39;Base\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.cpp:15:12 in 9. invalid bool value（非法 bool 值，需 -fsanitize=bool）\nubsan_demo.c:8:7: runtime error: load of value 2, which is not a valid value for type \u0026#39;_Bool\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:8:7 in 10. invalid enum value（非法枚举值，需 -fsanitize=enum）\nubsan_demo.c:10:10: runtime error: load of value 5, which is not a valid value for type \u0026#39;enum E\u0026#39; SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_demo.c:10:10 in # 环境变量 export UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 ⚠️ 重要提醒 默认不终止程序：UBSan 默认只打印警告后继续运行。如果程序输出量大，警告很容易被淹没。务必设置 halt_on_error=1 或编译时加 -fno-sanitize-recover=undefined，让 UB 发生时直接 abort 2.5.1 Sanitizer 抑制机制（Suppressions） 实际项目中经常遇到第三方库的已知问题或暂时无法修复的历史遗留问题，这时候不能让 Sanitizer 一直报警。所有 Sanitizer 都支持通过抑制文件（suppression file）来屏蔽指定位置的报错。\n抑制文件格式（asan_suppressions.txt）：\n# 格式：匹配规则类型:匹配模式 # 匹配规则类型： # interceptor_name — 按函数名匹配（函数名中包含的关键字） # source_file — 按源文件路径匹配（支持正则） # module — 按模块/库名匹配（如 libcurl.so） # 具体错误类型 — 如 heap-use-after-free、heap-buffer-overflow、leak 等 # 屏蔽第三方库 libcurl 中所有的泄漏 leak:libcurl.so # 屏蔽某个文件中的所有 use-after-free heap-use-after-free:legacy_code.cpp # 屏蔽某个函数中的所有报错（函数名匹配） interceptor_name:problematic_third_party_func # 屏蔽某个源文件路径下的所有问题 source_file:third_party/.* 使用方法：\n# ASan / LSan export ASAN_OPTIONS=suppressions=asan_suppressions.txt # TSan export TSAN_OPTIONS=suppressions=tsan_suppressions.txt # UBSan（注意：UBSan 抑制需要编译时指定 ignorelist） # Clang: -fsanitize-ignorelist=ubsan_suppressions.txt # GCC 12+: 同样支持 -fsanitize-ignorelist=ubsan_suppressions.txt # 更早的 GCC: 不支持 ignorelist，只能用 __attribute__((no_sanitize(\u0026#34;undefined\u0026#34;))) 标记函数 clang++ -fsanitize=undefined -fsanitize-ignorelist=ubsan_suppressions.txt ... gcc-12 -fsanitize=undefined -fsanitize-ignorelist=ubsan_suppressions.txt ... UBSan 抑制文件格式（ubsan_suppressions.txt）：\n# 屏蔽某个文件中的所有 UB src:legacy/old_code.c # 屏蔽某个函数中的所有 UB fun:problematic_func # 屏蔽某种类型的 UB（可选） unsigned-integer-overflow:legacy/.* 架构师提示：抑制文件是技术债务，每加一条都应该在 Bug 跟踪系统中记录对应的清理任务。不要让抑制文件变成藏污纳垢的地方。\n2.6 Sanitizer 推荐组合 场景 编译命令 日常开发/单元测试 -fsanitize=address,undefined -g -O0 并发测试 -fsanitize=thread -g -O1 发布前深度检查 (Clang) -fsanitize=memory,undefined -stdlib=libc++ -g -O1 2.7 GCC 与 Clang 的 Sanitizer 支持差异 工具 GCC Clang 备注 ASan ✅ 4.8+ ✅ 3.1+ 一致 TSan ✅ 4.8+ ✅ 3.2+ 一致 MSan ❌ 不支持 ✅ 3.3+ Clang 独占 UBSan ✅ 4.9+ ✅ 3.3+ 子项有差异（见下） DFSan ❌ ✅ 3.5+ Clang 独占 HWASan ✅ 13+ ✅ 12+ ARM64 UBSan 子项差异 子项 GCC Clang unsigned-integer-overflow ❌ ✅ pointer-overflow ❌ ✅ function（函数指针类型不匹配） ❌ ✅ 宏定义差异（跨编译器检测） // AddressSanitizer #if defined(__clang__) # if __has_feature(address_sanitizer) # define HAS_ASAN 1 # endif #elif defined(__GNUC__) \u0026amp;\u0026amp; defined(__SANITIZE_ADDRESS__) # define HAS_ASAN 1 #endif // ThreadSanitizer #if defined(__clang__) # if __has_feature(thread_sanitizer) # define HAS_TSAN 1 # endif #elif defined(__GNUC__) \u0026amp;\u0026amp; defined(__SANITIZE_THREAD__) # define HAS_TSAN 1 #endif // MemorySanitizer — 只有 Clang 有 #if defined(__clang__) \u0026amp;\u0026amp; __has_feature(memory_sanitizer) # define HAS_MSAN 1 #endif 2.8 与 Valgrind 的对比（参考） 维度 ASan TSan MSan Valgrind Memcheck 需重编译 ✅ ✅ ✅ ❌ 慢化 ~2x 5-15x ~3x 10-50x 内存膨胀 ~3x 5-10x 大 ~10x 未初始化读取 ❌ ❌ ✅ ✅ 栈越界 ✅ — — ❌ 平台 全平台 Linux/macOS Linux 全平台 结论：Sanitizer 日常必用，Valgrind 只在拿不到源码或 MSan 跑不起来时用。\n2.9 附录：CMake 项目集成指南 实际项目中几乎没人手动敲编译命令，以下是直接可用的 CMake 配置片段。\n2.9.1 编译器警告 + clang-tidy # CMakeLists.txt cmake_minimum_required(VERSION 3.16) project(my_project C CXX) # 生成 compile_commands.json（clang-tidy 必需） set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # 推荐警告集（GCC 和 Clang 通用） add_compile_options( -Wall -Wextra -Wpedantic -Werror -Wshadow -Wconversion -Wsign-conversion -Wnull-dereference -Wformat=2 -Wundef -Wunused -Wcast-align -Wcast-qual ) # C++ 专用警告（只对 C++ 编译生效，避免 C 编译器报错） if(CMAKE_CXX_COMPILER_ID MATCHES \u0026#34;GNU|Clang\u0026#34;) add_compile_options($\u0026lt;$\u0026lt;COMPILE_LANGUAGE:CXX\u0026gt;:-Wnon-virtual-dtor,-Woverloaded-virtual,-Wold-style-cast\u0026gt;) endif() # clang-tidy 集成（可选，通过 -DENABLE_CLANG_TIDY=ON 开启） option(ENABLE_CLANG_TIDY \u0026#34;Enable clang-tidy during build\u0026#34; OFF) if(ENABLE_CLANG_TIDY) find_program(CLANG_TIDY_EXE NAMES clang-tidy clang-tidy-18 clang-tidy-17) if(CLANG_TIDY_EXE) set(CMAKE_CXX_CLANG_TIDY \u0026#34;${CLANG_TIDY_EXE};-checks=bugprone-*,modernize-*,performance-*,cppcoreguidelines-*\u0026#34;) set(CMAKE_C_CLANG_TIDY \u0026#34;${CLANG_TIDY_EXE};-checks=bugprone-*,performance-*\u0026#34;) endif() endif() 2.9.2 Sanitizer 配置方案 # 通过 -DSANITIZE=address|thread|memory|undefined 切换 set(SANITIZE \u0026#34;\u0026#34; CACHE STRING \u0026#34;Sanitizer to enable: address, thread, memory, undefined\u0026#34;) if(SANITIZE STREQUAL \u0026#34;address\u0026#34;) add_compile_options(-fsanitize=address,undefined -g -O0) add_link_options(-fsanitize=address,undefined) elseif(SANITIZE STREQUAL \u0026#34;thread\u0026#34;) add_compile_options(-fsanitize=thread -g -O1) add_link_options(-fsanitize=thread) elseif(SANITIZE STREQUAL \u0026#34;memory\u0026#34;) # MSan 只有 Clang 支持，且需要 libc++（同时检查 C 和 C++ 编译器） if(NOT (CMAKE_CXX_COMPILER_ID MATCHES \u0026#34;Clang\u0026#34; OR CMAKE_C_COMPILER_ID MATCHES \u0026#34;Clang\u0026#34;)) message(FATAL_ERROR \u0026#34;MSan only supported with Clang\u0026#34;) endif() add_compile_options(-fsanitize=memory,undefined -stdlib=libc++ -g -O1 -fsanitize-memory-track-origins=2) add_link_options(-fsanitize=memory,undefined -stdlib=libc++) elseif(SANITIZE STREQUAL \u0026#34;undefined\u0026#34;) add_compile_options(-fsanitize=undefined -fno-sanitize-recover=undefined -g -O0) add_link_options(-fsanitize=undefined) endif() 使用方法：\n# ASan + UBSan（日常开发） cmake -B build -S . -DSANITIZE=address cmake --build build -j$(nproc) # TSan（并发测试） cmake -B build-tsan -S . -DSANITIZE=thread cmake --build build-tsan -j$(nproc) # MSan（发布前，仅 Clang） CXX=clang++ cmake -B build-msan -S . -DSANITIZE=memory cmake --build build-msan -j$(nproc) 2.9.3 GCC -fanalyzer 配置 option(ENABLE_FANALYZER \u0026#34;Enable GCC -fanalyzer\u0026#34; OFF) if(ENABLE_FANALYZER AND CMAKE_C_COMPILER_ID STREQUAL \u0026#34;GNU\u0026#34;) add_compile_options(-fanalyzer -O1) endif() 常见问题 FAQ Q1：Sanitizer 报告的错误一定是真的吗？会不会有误报？ ASan、TSan、UBSan 的误报率极低（接近 0%），因为它们是基于编译期插桩 + 运行时状态追踪的，检测逻辑非常严谨。如果这些工具报了错，极大概率是真的有问题，不要轻易忽略。\nMSan 误报率相对较高，但根本原因通常不是工具本身，而是部分依赖库没有用 MSan 重新编译，导致库函数返回值被当作\u0026quot;未初始化\u0026quot;。这也是为什么 MSan 要求所有代码包括标准库都必须重新编译。\n静态分析工具（-fanalyzer、Clang SA、clang-tidy）存在一定误报，因为它们不运行程序，只能做近似推理。遇到可疑的报告，人工确认后用抑制方式标注即可。\nQ2：ASan 检测到了内存错误，但程序在正常环境下运行得好好的，需要修吗？ 必须修！ Sanitizer 检测到的内存错误（use-after-free、越界等）属于未定义行为（UB），\u0026ldquo;正常运行\u0026quot;只是因为：\n刚好越界访问的内存是你进程合法拥有的（没触发页错误） 释放后的内存还没被操作系统回收，里面的数据还\u0026quot;凑巧\u0026quot;正确 当前编译器版本/优化级别下，代码生成刚好没出问题 换一个编译器版本、换一个优化级别、换一个操作系统、甚至只是程序运行久一点，随时可能崩溃。 内存错误是 C/C++ 程序最难调试的 Bug 类型，Sanitizer 已经帮你定位到了，不修真的对不起工具。\nQ3：可以在发布版本中启用 Sanitizer 吗？ 绝对不可以！ 原因有三个：\n性能开销太大：ASan 慢 2x、TSan 慢 5-15x、内存膨胀 3-10x 安全性反而降低：Sanitizer 需要固定映射 Shadow Memory，这会削弱 ASLR 地址空间随机化的防护效果 错误信息会暴露给用户：Sanitizer 报错时会打印完整的调用栈和内存布局 Sanitizer 是开发和测试工具，不是生产环境运行时防护。\nQ4：MSan 太麻烦了（要重新编所有库），有没有替代方案？ 有两个选择，但各有局限：\n方案 优点 缺点 Valgrind Memcheck 不需要重编译，直接跑 慢 10-50x，检测能力不如 MSan（栈越界检测不到） ASan + 编译器初始化填充 ASan 已经在跑了，零额外成本 只能检测部分未初始化问题（比如通过编译器把栈变量初始化为 0xAA，越界时更显眼） 实际项目中建议：测试覆盖率高的核心模块用 MSan，其他模块用 Valgrind 做兜底。\nQ5：Sanitizer 和 gdb/lldb 调试器可以一起用吗？ 可以！ 而且非常推荐。当 Sanitizer 报了错，你往往需要在调试器中查看当时的完整状态。\n最简单的方法：\n# 方法一：直接用 gdb 运行（推荐，最简单） gdb --args ./my_program # 在 gdb 中设置环境变量，让 Sanitizer 遇到错误直接 abort # (gdb) set environment ASAN_OPTIONS=abort_on_error=1 # (gdb) set environment UBSAN_OPTIONS=halt_on_error=1 # (gdb) run # 报错后 gdb 会自动停在出错位置，可以 bt 看调用栈、p 看变量 # 方法二：先设置环境变量再运行（需要让信号交给调试器） export ASAN_OPTIONS=abort_on_error=1 gdb --args ./my_program Sanitizer 默认会在报错时打印调用栈，但调试器能让你看到所有线程的状态、所有局部变量的值，对复杂 Bug 的定位帮助极大。\nQ6：开启 -Werror 后，第三方库的警告导致编译不过怎么办？ 不要全局关闭 -Werror！ 正确的做法是只对第三方库的代码关闭警告或 -Werror。\nCMake 中可以这样配置：\n# 为第三方库目标单独关闭警告 target_compile_options(third_party_lib PRIVATE -w) # 关闭所有警告 # 或者只关闭 -Werror，但仍然显示警告 target_compile_options(third_party_lib PRIVATE -Wno-error) 如果你是直接 #include 第三方头文件，可以用 #pragma 包起来：\n#pragma GCC diagnostic push #pragma GCC diagnostic ignored \u0026#34;-Wdeprecated\u0026#34; #pragma GCC diagnostic ignored \u0026#34;-Wsign-conversion\u0026#34; #include \u0026#34;third_party_lib.h\u0026#34; #pragma GCC diagnostic pop 总结：生产级 C/C++ 代码审查工具链 静态（按使用频率） 每次编译: -Wall -Wextra -Wpedantic -Werror + 推荐警告集 每次提交: clang-tidy 每日构建: GCC -fanalyzer (GCC 构建) + scan-build (Clang 构建) 动态（按使用频率） 每次运行: ASan + UBSan (-fsanitize=address,undefined) 每日构建: TSan (-fsanitize=thread) 发布前: MSan (-fsanitize=memory, 仅 Clang) GCC vs Clang 选择建议 场景 推荐 发布构建 GCC 日常开发/动态分析 Clang（MSan 独占 + Sanitizer 子项更多） 静态分析 两者都跑（警告和分析器互补） 一句话：你不需要买任何第三方工具，用好 GCC 和 Clang 自带的分析能力，就已经超过了 90% 的 C/C++ 项目。\n","permalink":"https://youdward.com/posts/c/c-%E4%BB%A3%E7%A0%81%E5%AE%A1%E6%9F%A5%E7%BC%96%E8%AF%91%E5%99%A8%E9%9D%99%E6%80%81%E4%B8%8E%E5%8A%A8%E6%80%81%E5%88%86%E6%9E%90%E5%B7%A5%E5%85%B7%E5%AE%9E%E8%B7%B5/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e作为 C/C++ 开发者，你手上最强大的代码审查工具不是某个第三方软件，而是\u003cstrong\u003e你每天都在用的编译器本身\u003c/strong\u003e。GCC 和 Clang 都内置了完整的静态分析和动态分析能力，零成本、零额外依赖。\u003c/p\u003e\n\u003cp\u003e本文聚焦两类工具：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e静态分析\u003c/strong\u003e（不运行程序）：编译器警告、GCC \u003ccode\u003e-fanalyzer\u003c/code\u003e、Clang Static Analyzer (\u003ccode\u003e--analyze\u003c/code\u003e / \u003ccode\u003escan-build\u003c/code\u003e)、clang-tidy\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e动态分析\u003c/strong\u003e（运行程序时捕获问题）：AddressSanitizer (ASan)、ThreadSanitizer (TSan)、MemorySanitizer (MSan)、UndefinedBehaviorSanitizer (UBSan)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003ecppcheck、Valgrind 等第三方工具\u003cstrong\u003e仅在文末作对比参考\u003c/strong\u003e。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"第一部分静态分析编译器自带\"\u003e第一部分：静态分析（编译器自带）\u003c/h2\u003e\n\u003ch3 id=\"11-编译器警告零成本第一道防线\"\u003e1.1 编译器警告：零成本第一道防线\u003c/h3\u003e\n\u003cp\u003e警告不是可选的，\u003cstrong\u003e\u003ccode\u003e-Werror\u003c/code\u003e 是 CI 和本地开发的底线\u003c/strong\u003e（发布构建可以酌情关闭，避免新编译器版本引入的新警告中断发布流程）。\u003c/p\u003e\n\u003ch4 id=\"推荐的最小警告集gcc-和-clang-通用\"\u003e推荐的最小警告集（GCC 和 Clang 通用）\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 所有构建必开\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e-Wall -Wextra -Wpedantic -Werror\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 推荐再加这些\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 注意：GCC 中 -Wconversion 不包含有符号/无符号隐式转换，需单独加 -Wsign-conversion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# Clang 中 -Wconversion 默认包含两者\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e-Wshadow -Wconversion -Wsign-conversion -Wnull-dereference\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e-Wformat\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003e\u003cspan style=\"color:#ae81ff\"\u003e2\u003c/span\u003e -Wundef -Wunused -Wcast-align -Wcast-qual\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e-Wnon-virtual-dtor -Woverloaded-virtual -Wold-style-cast\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch4 id=\"gcc-特有警告\"\u003eGCC 特有警告\u003c/h4\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e选项\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e起始版本\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Weffc++\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e很早\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eEffective C++ 规则集\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Wstringop-overflow=4\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eGCC 11\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e字符串操作溢出（\u003cstrong\u003e需 \u003ccode\u003e-O2\u003c/code\u003e 才触发\u003c/strong\u003e，级别 1-4，数字越大越严格）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Wdangling-reference\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eGCC 13\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e悬垂引用检测\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Wstrict-overflow=5\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eGCC 4.x\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e有符号整数溢出假设（\u003cstrong\u003e需 \u003ccode\u003e-O2\u003c/code\u003e 才触发\u003c/strong\u003e，级别 1-5）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Warray-bounds\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e很早\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e数组越界（\u003cstrong\u003e需 \u003ccode\u003e-O2\u003c/code\u003e 才触发\u003c/strong\u003e）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch4 id=\"clang-特有警告\"\u003eClang 特有警告\u003c/h4\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e选项\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Weverything\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e启用所有警告（\u003cstrong\u003e不要在 CI 用\u003c/strong\u003e，新警告会断构建）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Wno-c++98-compat\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e忽略 C++98 兼容性（现代项目通常不需要）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Wdocumentation\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eDoxygen 文档注释格式检查\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e-Wthread-safety\u003c/code\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e线程安全注解检查（代码中需加注解）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"12-gcc--fanalyzer路径敏感静态分析\"\u003e1.2 GCC \u003ccode\u003e-fanalyzer\u003c/code\u003e：路径敏感静态分析\u003c/h3\u003e\n\u003cp\u003eGCC 10 引入，GCC 11 完全重写。基于符号执行，能发现跨函数、跨路径的深度 Bug。\u003c/p\u003e","title":"C/C++ 代码审查：编译器静态与动态分析工具实践"},{"content":"什么是 Shell Shell 是用户与操作系统内核之间的接口程序。当你在终端中输入命令时，Shell 会解析你的输入并调用相应的程序执行。\n为什么要学习 Shell 场景 说明 自动化 编写脚本自动完成重复性任务 远程管理 SSH 远程连接服务器时只能使用命令行 效率 熟练使用命令行比图形界面更快 编程基础 理解文件系统、进程管理等概念 常见的 Shell Shell 说明 Bash Bourne Again Shell，最常用的 Shell Zsh Z Shell，功能更强大，兼容 Bash Fish Friendly Interactive Shell，语法更简单 文件系统操作 查看当前目录 pwd pwd -P # 显示物理路径（解析符号链接） pwd 是 print working directory 的缩写，显示当前所在的目录路径。\n列出目录内容 ls ls -l # 详细列表 ls -la # 显示所有文件（包括隐藏文件） ls -lh # 显示文件大小（人类可读格式） ls -la /home/edward # 指定目录 ls -la --sort=size # 按大小排序 ls -la --sort=time # 按时间排序 示例输出：\ntotal 24 drwxr-xr-x 2 edward edward 4096 Jul 12 10:00 documents -rw-r--r-- 1 edward edward 128 Jul 12 09:00 hello.txt drwxr-xr-x 3 edward edward 4096 Jul 12 08:00 projects 切换目录 cd /home/edward # 切换到绝对路径 cd documents # 切换到相对路径 cd .. # 返回上级目录 cd ../.. # 返回上两级目录 cd ~ # 切换到用户主目录 cd - # 返回上一次所在的目录 cd ~/projects # 快捷方式，用户主目录下的 projects 创建目录 mkdir new_folder # 创建单个目录 mkdir -p projects/myapp # 创建嵌套目录（-p 参数自动创建父目录） mkdir -m 755 secure_folder # 创建目录并设置权限 创建文件 touch hello.txt # 创建空文件 touch -t 202401010000 file.txt # 创建文件并设置时间戳 echo \u0026#34;Hello World\u0026#34; \u0026gt; hello.txt # 创建文件并写入内容 printf \u0026#34;Name: %s\\nAge: %d\\n\u0026#34; \u0026#34;Tom\u0026#34; 25 \u0026gt; info.txt # 格式化输出 查看文件内容 cat hello.txt # 显示文件全部内容 cat -n hello.txt # 显示行号 head -n 10 hello.txt # 显示前 10 行 tail -n 10 hello.txt # 显示后 10 行 tail -f log.txt # 实时显示文件末尾（用于查看日志） tail -F log.txt # 实时显示，文件被替换后仍能追踪 less hello.txt # 分页查看文件内容（按 q 退出） more hello.txt # 分页查看（空格键翻页） 复制与移动 cp source.txt destination.txt # 复制文件 cp -r folder1 folder2 # 复制目录（-r 表示递归） cp -i source.txt dest.txt # 交互式复制（覆盖前提示） cp -a folder1 folder2 # 保留属性复制（归档模式） cp -u source.txt dest.txt # 仅复制更新的文件 mv file.txt new_location/file.txt # 移动文件 mv old_name.txt new_name.txt # 重命名文件 mv -i file.txt dest.txt # 交互式移动 删除文件 rm file.txt # 删除文件 rm -i file.txt # 交互式删除（删除前确认） rm -r folder # 删除目录（-r 表示递归） rm -rf folder # 强制删除目录（慎用！） rm -rf -- folder # 删除以 - 开头的文件 警告：rm -rf 命令非常危险，会直接删除文件而不提示，误操作可能导致数据丢失！\n文件权限 权限表示法 -rw-r--r-- 1 edward edward 128 Jul 12 09:00 hello.txt 位置 说明 - 文件类型（- 普通文件，d 目录，l 符号链接） rw- 所有者权限（读、写、无执行） r-- 组权限（读、无写、无执行） r-- 其他用户权限（读、无写、无执行） 修改权限 chmod +x script.sh # 添加执行权限 chmod -x script.sh # 移除执行权限 chmod 755 script.sh # 设置权限为 rwxr-xr-x chmod 644 hello.txt # 设置权限为 rw-r--r-- chmod u+x script.sh # 只为所有者添加执行权限 chmod g+w file.txt # 只为组添加写权限 chmod o-r file.txt # 移除其他用户的读权限 chmod -R 755 folder/ # 递归修改目录权限 修改所有者 chown edward file.txt # 修改所有者 chown edward:users file.txt # 修改所有者和组 chown -R edward folder/ # 递归修改目录所有者 chgrp users file.txt # 只修改组 数字权限说明：\n数字 权限 说明 7 rwx 读、写、执行 6 rw- 读、写 5 r-x 读、执行 4 r\u0026ndash; 只读 3 -wx 写、执行 2 -w- 只写 1 \u0026ndash;x 只执行 0 \u0026mdash; 无权限 管道与重定向 管道（Pipe） 管道符 | 用于将一个命令的输出作为另一个命令的输入：\nls -la | grep \u0026#34;.txt\u0026#34; # 列出所有 txt 文件 cat log.txt | grep \u0026#34;error\u0026#34; # 在日志中查找包含 error 的行 cat log.txt | grep \u0026#34;error\u0026#34; | wc -l # 统计 error 出现的次数 cat log.txt | grep -v \u0026#34;info\u0026#34; | head -20 # 排除 info 行，显示前 20 行 重定向 echo \u0026#34;Hello\u0026#34; \u0026gt; file.txt # 覆盖写入文件 echo \u0026#34;World\u0026#34; \u0026gt;\u0026gt; file.txt # 追加写入文件 cat file.txt \u0026gt; output.txt # 将文件内容复制到另一个文件 command \u0026gt; /dev/null # 将输出丢弃（不显示） command 2\u0026gt; error.log # 将错误输出重定向到文件 command \u0026amp;\u0026gt; log.txt # 将标准输出和错误输出都重定向到文件 command \u0026gt;\u0026gt; log.txt 2\u0026gt;\u0026amp;1 # 追加输出，错误也追加 输入重定向 wc -l \u0026lt; file.txt # 统计文件行数（通过输入重定向） cat \u0026gt; file.txt \u0026lt;\u0026lt; EOF # 多行输入（Here Document） 第一行 第二行 第三行 EOF Git 版本控制 初始化仓库 git init # 初始化本地仓库 git init --bare repo.git # 创建裸仓库（用于服务器） git clone \u0026lt;repo-url\u0026gt; # 克隆远程仓库 git clone --depth 1 \u0026lt;repo-url\u0026gt; # 浅克隆（只获取最近一次提交） git clone -b \u0026lt;branch\u0026gt; \u0026lt;repo-url\u0026gt; # 克隆指定分支 基础操作 git add . # 将所有修改添加到暂存区 git add filename.txt # 添加指定文件 git add -p filename.txt # 交互式添加（逐块选择） git commit -m \u0026#34;commit message\u0026#34; # 提交到本地仓库 git commit -am \u0026#34;message\u0026#34; # 跳过暂存，直接提交已跟踪文件 git commit --amend # 修改最后一次提交 git status # 查看工作区状态 git status -s # 简洁状态输出 git log # 查看提交历史 git log --oneline # 简洁的提交历史 git log --oneline -5 # 最近 5 次提交 git log --graph --oneline # 图形化显示提交历史 git log --all --oneline --graph # 显示所有分支 git diff # 查看未暂存的修改 git diff --staged # 查看已暂存的修改 git diff HEAD # 查看所有修改 分支管理 git branch # 列出所有分支 git branch -v # 显示分支最后提交 git branch -a # 显示所有分支（包括远程） git branch new-branch # 创建新分支 git switch new-branch # 切换到新分支（推荐，替代 git checkout） git switch -c feature # 创建并切换到新分支（推荐，替代 git checkout -b） git merge feature # 合并分支 git merge --no-ff feature # 强制创建合并提交 git branch -d feature # 删除分支 git branch -D feature # 强制删除分支（未合并时） git branch -m old-name new-name # 重命名分支 远程操作 git remote -v # 查看远程仓库 git remote add origin \u0026lt;url\u0026gt; # 添加远程仓库 git remote rename origin github # 重命名远程仓库 git remote remove origin # 删除远程仓库 git push -u origin main # 推送到远程仓库（第一次） git push # 推送到默认远程分支 git push origin feature # 推送指定分支 git push -d origin feature # 删除远程分支 git pull origin main # 拉取远程更新并合并 git pull --rebase origin main # 拉取并变基 git fetch origin # 获取远程更新（不合并） git fetch origin feature:feature # 获取远程分支到本地 git rebase origin/main # 将本地提交变基到远程 git reset --hard origin/main # 强制重置到远程状态 撤销操作 git restore file.txt # 撤销工作区修改（推荐） git restore --staged file.txt # 取消暂存（推荐） git restore --source=HEAD~1 file.txt # 恢复到上一次提交的版本 git reset HEAD file.txt # 取消暂存（传统方式） git reset --hard HEAD # 重置所有修改（慎用！） git reset --hard HEAD~1 # 回退到上一次提交 git revert \u0026lt;commit-hash\u0026gt; # 撤销提交（创建新提交） git revert HEAD # 撤销最后一次提交 git checkout \u0026lt;commit\u0026gt; -- file.txt # 从特定提交恢复文件 暂存操作 git stash # 暂存工作区修改 git stash save \u0026#34;message\u0026#34; # 暂存并添加说明 git stash list # 查看暂存列表 git stash apply # 应用最近的暂存 git stash apply stash@{1} # 应用指定的暂存 git stash pop # 应用并删除暂存 git stash drop stash@{0} # 删除指定暂存 git stash clear # 清空所有暂存 变基与合并 git rebase main # 在 main 分支基础上重新提交 git rebase -i HEAD~3 # 交互式变基（修改最近 3 次提交） git cherry-pick \u0026lt;commit-hash\u0026gt; # 复制指定提交到当前分支 git cherry-pick --no-commit \u0026lt;commit-hash\u0026gt; # 复制但不提交 git merge-base main feature # 查找两个分支的共同祖先 git log main..feature # 查看 feature 上独有的提交 awk 文本处理 基本语法 awk \u0026#39;{print $1}\u0026#39; file.txt # 打印第一列 awk \u0026#39;{print $1, $3}\u0026#39; file.txt # 打印第一列和第三列 awk -F \u0026#39;:\u0026#39; \u0026#39;{print $1}\u0026#39; /etc/passwd # 使用冒号作为分隔符 awk -F \u0026#39;[,:]\u0026#39; \u0026#39;{print $1}\u0026#39; file.txt # 使用多个分隔符 内置变量 变量 说明 $0 整行内容 $1-$NF 第 1 到最后一列 NF 列数 NR 当前行号 FS 字段分隔符（默认空格） OFS 输出字段分隔符（默认空格） RS 记录分隔符（默认换行） ORS 输出记录分隔符（默认换行） awk \u0026#39;{print NR, NF, $0}\u0026#39; file.txt # 打印行号、列数、整行 awk \u0026#39;{print $1, $NF}\u0026#39; file.txt # 打印第一列和最后一列 条件筛选 awk \u0026#39;$3 \u0026gt; 100\u0026#39; file.txt # 第三列大于 100 的行 awk \u0026#39;$3 \u0026gt; 100 \u0026amp;\u0026amp; $4 == \u0026#34;yes\u0026#34;\u0026#39; file.txt # 多条件筛选 awk \u0026#39;/pattern/ {print}\u0026#39; file.txt # 匹配模式的行 awk \u0026#39;!/pattern/ {print}\u0026#39; file.txt # 不匹配模式的行 awk \u0026#39;NR \u0026gt; 5 \u0026amp;\u0026amp; NR \u0026lt; 10 {print}\u0026#39; file.txt # 第 6-9 行 BEGIN/END 块 awk \u0026#39;BEGIN {print \u0026#34;Start\u0026#34;} {print $1} END {print \u0026#34;End\u0026#34;}\u0026#39; file.txt awk \u0026#39;BEGIN {FS=\u0026#34;:\u0026#34;} {print $1}\u0026#39; /etc/passwd # 在处理前设置分隔符 awk \u0026#39;{sum += $2} END {print \u0026#34;Total:\u0026#34;, sum}\u0026#39; file.txt # 计算总和 计算统计 awk \u0026#39;{sum += $2} END {print sum}\u0026#39; file.txt # 计算第二列总和 awk \u0026#39;{count++} END {print count}\u0026#39; file.txt # 统计行数 awk \u0026#39;{max = ($1 \u0026gt; max) ? $1 : max} END {print max}\u0026#39; file.txt # 最大值 awk \u0026#39;{sum += $2; count++} END {print sum/count}\u0026#39; file.txt # 平均值 awk \u0026#39;{if ($1 \u0026gt; max) {max=$1; line=$0}} END {print max, line}\u0026#39; file.txt 格式化输出 awk \u0026#39;{printf \u0026#34;%-10s %5d %8.2f\\n\u0026#34;, $1, $2, $3}\u0026#39; file.txt # 格式化输出 awk -v OFS=\u0026#34;\\t\u0026#34; \u0026#39;{print $1, $2}\u0026#39; file.txt # 使用制表符分隔 实用示例 # 统计访问日志中每个 IP 的访问次数 awk \u0026#39;{count[$1]++} END {for (ip in count) print ip, count[ip]}\u0026#39; access.log | sort -rnk 2 # 计算 CSV 文件中某列的总和 awk -F \u0026#39;,\u0026#39; \u0026#39;{sum += $3} END {print \u0026#34;Total:\u0026#34;, sum}\u0026#39; data.csv # 提取 JSON 中的某个字段（简单情况） curl -s https://api.github.com/users/octocat | awk -F \u0026#39;\u0026#34;\u0026#39; \u0026#39;/\u0026#34;login\u0026#34;/ {print $4}\u0026#39; curl 网络请求 基本用法 curl https://example.com # 获取网页内容 curl -o output.html https://example.com # 保存到指定文件 curl -O https://example.com/file.zip # 自动保存文件名 curl -L https://example.com # 跟随重定向 curl -k https://example.com # 忽略 SSL 证书验证 请求参数 curl -X POST https://api.example.com/data # POST 请求 curl -X PUT https://api.example.com/data # PUT 请求 curl -X DELETE https://api.example.com/data/1 # DELETE 请求 curl -d \u0026#34;name=test\u0026amp;value=123\u0026#34; https://api.example.com # 发送表单数据 curl -d @data.json https://api.example.com # 从文件读取数据 curl -H \u0026#34;Content-Type: application/json\u0026#34; -d \u0026#39;{\u0026#34;key\u0026#34;:\u0026#34;value\u0026#34;}\u0026#39; https://api.example.com # 发送 JSON curl -H \u0026#34;Authorization: Bearer token\u0026#34; https://api.example.com # 添加认证头 curl -H \u0026#34;User-Agent: MyApp/1.0\u0026#34; https://api.example.com # 自定义 User-Agent 文件上传 curl -F \u0026#34;file=@path/to/file\u0026#34; https://api.example.com/upload # 上传文件 curl -F \u0026#34;file=@/path/to/image.jpg;type=image/jpeg\u0026#34; https://api.example.com/upload # 指定 MIME 类型 curl -F \u0026#34;name=test\u0026#34; -F \u0026#34;file=@file.txt\u0026#34; https://api.example.com/upload # 多个字段 认证 curl -u username:password https://api.example.com # 基本认证 curl -u username https://api.example.com # 交互式输入密码 curl --oauth2-bearer token https://api.example.com # OAuth2 认证 curl --cert cert.pem --key key.pem https://api.example.com # 客户端证书 Cookie curl -c cookies.txt https://example.com # 保存 Cookie 到文件 curl -b cookies.txt https://example.com # 使用 Cookie 文件 curl -b \u0026#34;session=abc123\u0026#34; https://example.com # 直接设置 Cookie curl -b cookies.txt -c cookies.txt https://example.com # 读写 Cookie 代理 curl -x http://proxy.example.com:8080 https://example.com # HTTP 代理 curl -x socks5://proxy.example.com:1080 https://example.com # SOCKS5 代理 curl --noproxy localhost https://example.com # 不走代理的地址 查看响应信息 curl -I https://example.com # 只显示响应头 curl -v https://example.com # 详细输出（调试用） curl -s https://example.com # 静默模式（不显示进度） curl -w \u0026#34;%{http_code}\u0026#34; https://example.com # 自定义输出格式 curl -w \u0026#34;\\nTotal time: %{time_total}s\\n\u0026#34; https://example.com # 显示耗时 下载文件 curl -C - -O https://example.com/large-file.zip # 断点续传 curl -L -O https://example.com/file # 跟随重定向并保存 curl -s https://example.com/api | jq . # 静默模式 + JSON 解析 curl --limit-rate 100K https://example.com/large-file.zip # 限速下载 SSH 远程连接 基本连接 ssh user@hostname # 连接远程服务器 ssh user@192.168.1.100 # 使用 IP 地址连接 ssh -p 2222 user@hostname # 指定端口连接 ssh hostname # 使用默认用户连接 ssh -o ConnectTimeout=10 user@hostname # 设置连接超时 SSH 密钥认证 ssh-keygen -t ed25519 # 生成密钥对（推荐算法） ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_myserver # 指定文件名 ssh-keygen -t rsa -b 4096 # RSA 算法（兼容性更好） ssh-copy-id user@hostname # 将公钥复制到服务器 ssh-copy-id -i ~/.ssh/id_ed25519.pub user@hostname # 指定公钥文件 ssh user@hostname # 使用密钥登录（无需密码） 端口转发 ssh -L 8080:localhost:3000 user@hostname # 本地端口转发（访问本机 8080 → 远程 3000） ssh -L 8080:remote-server:3000 user@jump-host # 跳板机端口转发 ssh -R 3000:localhost:8080 user@hostname # 远程端口转发（访问远程 3000 → 本机 8080） ssh -D 1080 user@hostname # SOCKS 代理 ssh -L 8080:localhost:3000 -N user@hostname # 只转发端口，不执行命令 远程执行命令 ssh user@hostname \u0026#34;ls -la\u0026#34; # 在远程执行命令 ssh user@hostname \u0026#34;cat /var/log/syslog\u0026#34; # 查看远程日志 ssh user@hostname \u0026#34;cd /home \u0026amp;\u0026amp; ls\u0026#34; # 执行多个命令 ssh user@hostname \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; # 执行多行命令（Here Document） cd /home ls -la echo \u0026#34;Done\u0026#34; EOF 文件传输（scp） scp localfile.txt user@hostname:/remote/path # 上传文件 scp user@hostname:/remote/path/file.txt . # 下载文件 scp -r localfolder/ user@hostname:/remote/path # 上传目录 scp -r user@hostname:/remote/folder/ . # 下载目录 scp -P 2222 file.txt user@hostname:/path # 指定端口 scp -i ~/.ssh/id_ed25519 file.txt user@hostname:/path # 指定密钥 文件传输（rsync） rsync -av localfile.txt user@hostname:/remote/path # 同步文件 rsync -av localfolder/ user@hostname:/remote/path/ # 同步目录 rsync -av --delete localfolder/ user@hostname:/remote/path/ # 同步并删除多余文件 rsync -avz localfolder/ user@hostname:/remote/path/ # 压缩传输 rsync -av --exclude=\u0026#34;*.log\u0026#34; localfolder/ user@hostname:/remote/path/ # 排除文件 SSH 配置文件 编辑 ~/.ssh/config：\nHost myserver HostName 192.168.1.100 User edward Port 2222 IdentityFile ~/.ssh/id_ed25519 ForwardAgent yes ConnectTimeout 10 Host github HostName github.com User git IdentityFile ~/.ssh/id_ed25519_github 然后使用：\nssh myserver # 使用配置连接 scp file.txt myserver:/path # scp 也支持配置 压缩与解压 tar tar -cvf archive.tar files/ # 创建 tar 归档 tar -czvf archive.tar.gz files/ # 创建 gzip 压缩归档 tar -cjvf archive.tar.bz2 files/ # 创建 bzip2 压缩归档 tar -cJvf archive.tar.xz files/ # 创建 xz 压缩归档 tar -tvf archive.tar # 查看归档内容 tar -xvf archive.tar # 解压归档 tar -xzvf archive.tar.gz # 解压 gzip 归档 tar -xjvf archive.tar.bz2 # 解压 bzip2 归档 tar -xJvf archive.tar.xz # 解压 xz 归档 tar -xvf archive.tar -C /dest/path # 解压到指定目录 zip/unzip zip archive.zip file1.txt file2.txt # 创建 zip 压缩包 zip -r archive.zip folder/ # 递归压缩目录 unzip archive.zip # 解压 zip 包 unzip archive.zip -d /dest/path # 解压到指定目录 unzip -l archive.zip # 查看 zip 包内容 gzip/gunzip gzip file.txt # 压缩文件（生成 file.txt.gz） gzip -k file.txt # 压缩但保留原文件 gunzip file.txt.gz # 解压文件 gzip -d file.txt.gz # 解压文件（同上） 进程管理 ps ps # 查看当前终端进程 ps aux # 查看所有进程（常用） ps aux | grep \u0026#34;python\u0026#34; # 查找指定进程 ps aux --sort=-%mem | head -10 # 按内存占用排序 ps aux --sort=-%cpu | head -10 # 按 CPU 占用排序 ps -ef # 查看进程树 ps -p \u0026lt;pid\u0026gt; -o %cpu,%mem,cmd # 查看指定进程详情 top top # 实时查看进程（按 q 退出） top -u username # 只显示指定用户的进程 top -p \u0026lt;pid\u0026gt; # 只显示指定进程 top -d 2 # 每 2 秒刷新一次 top 快捷键：\n快捷键 功能 P 按 CPU 排序 M 按内存排序 T 按时间排序 k 终止进程 q 退出 htop（推荐替代 top） htop # 更友好的进程查看工具 htop -u username # 过滤用户进程 kill kill \u0026lt;pid\u0026gt; # 终止进程（发送 SIGTERM） kill -9 \u0026lt;pid\u0026gt; # 强制终止进程（发送 SIGKILL） kill -15 \u0026lt;pid\u0026gt; # 优雅终止（SIGTERM） kill -HUP \u0026lt;pid\u0026gt; # 重启进程（SIGHUP） pkill process_name # 按进程名终止 killall process_name # 终止所有同名进程 后台运行 command \u0026amp; # 在后台运行命令 nohup command \u0026amp; # 后台运行，退出终端后仍继续执行 setsid command # 创建新会话运行命令 screen # 创建屏幕会话 tmux # 创建 tmux 会话（推荐） jobs # 查看后台任务 fg %1 # 将后台任务 1 切换到前台 bg %1 # 将前台任务 1 切换到后台 磁盘管理 df df # 查看磁盘使用情况 df -h # 人类可读格式 df -h / # 查看指定挂载点 df -i # 查看 inode 使用情况 du du # 查看目录大小 du -h # 人类可读格式 du -h -s folder/ # 查看目录总大小 du -h folder/ | sort -rh | head -20 # 按大小排序，显示前 20 du -h --max-depth=2 folder/ # 限制深度 free free # 查看内存使用情况 free -h # 人类可读格式 free -m # 以 MB 为单位 free -s 2 # 每 2 秒刷新一次 lsblk lsblk # 查看磁盘分区信息 lsblk -f # 显示文件系统类型 文本处理进阶 sed sed \u0026#39;s/old/new/\u0026#39; file.txt # 替换每行第一个匹配 sed \u0026#39;s/old/new/g\u0026#39; file.txt # 全局替换 sed -i \u0026#39;s/old/new/g\u0026#39; file.txt # 原地修改文件 sed \u0026#39;s/old/new/g\u0026#39; -i.bak file.txt # 修改并备份原文件 sed \u0026#39;/pattern/d\u0026#39; file.txt # 删除匹配的行 sed \u0026#39;/pattern/!d\u0026#39; file.txt # 删除不匹配的行 sed \u0026#39;2,5d\u0026#39; file.txt # 删除第 2-5 行 sed \u0026#39;3a new line\u0026#39; file.txt # 在第 3 行后添加 sed \u0026#39;3i new line\u0026#39; file.txt # 在第 3 行前插入 sed -n \u0026#39;2,5p\u0026#39; file.txt # 显示第 2-5 行 sort sort file.txt # 按字母排序 sort -n file.txt # 按数字排序 sort -rn file.txt # 按数字降序排序 sort -t \u0026#39;,\u0026#39; -k 2 file.txt # 按逗号分隔的第 2 列排序 sort -u file.txt # 去重排序 sort file1.txt file2.txt # 合并排序 uniq sort file.txt | uniq # 去重（需要先排序） sort file.txt | uniq -c # 统计重复次数 sort file.txt | uniq -d # 只显示重复行 sort file.txt | uniq -u # 只显示不重复行 wc wc -l file.txt # 统计行数 wc -w file.txt # 统计单词数 wc -c file.txt # 统计字节数 wc -L file.txt # 统计最长行长度 wc file.txt # 显示所有统计信息 diff diff file1.txt file2.txt # 比较两个文件 diff -u file1.txt file2.txt # 统一格式显示 diff -r folder1/ folder2/ # 递归比较目录 diff -q folder1/ folder2/ # 只显示差异存在 sdiff file1.txt file2.txt # 并排显示差异 comm comm file1.txt file2.txt # 比较两个已排序文件 comm -1 file1.txt file2.txt # 不显示只在 file1 中的行 comm -2 file1.txt file2.txt # 不显示只在 file2 中的行 comm -3 file1.txt file2.txt # 只显示共同行 查找与搜索 find find . -name \u0026#34;*.txt\u0026#34; # 在当前目录下查找所有 txt 文件 find /home -type f -name \u0026#34;*.log\u0026#34; # 在 /home 目录下查找所有 log 文件 find . -size +10M # 查找大于 10MB 的文件 find . -size -1k # 查找小于 1KB 的文件 find . -mtime -7 # 查找最近 7 天内修改的文件 find . -atime -1 # 查找最近 1 天内访问的文件 find . -user edward # 查找指定用户的文件 find . -type d -name \u0026#34;__pycache__\u0026#34; -exec rm -rf {} + # 删除所有 __pycache__ 目录 find . -name \u0026#34;*.pyc\u0026#34; -delete # 删除所有 pyc 文件 find . -exec grep -l \u0026#34;pattern\u0026#34; {} \\; # 在找到的文件中搜索模式 grep grep \u0026#34;pattern\u0026#34; file.txt # 在文件中查找模式 grep -r \u0026#34;pattern\u0026#34; . # 递归查找目录中的所有文件 grep -i \u0026#34;pattern\u0026#34; file.txt # 忽略大小写 grep -n \u0026#34;pattern\u0026#34; file.txt # 显示匹配行的行号 grep -v \u0026#34;pattern\u0026#34; file.txt # 显示不匹配的行 grep -c \u0026#34;pattern\u0026#34; file.txt # 统计匹配行数 grep -A 3 \u0026#34;pattern\u0026#34; file.txt # 显示匹配行及后面 3 行 grep -B 3 \u0026#34;pattern\u0026#34; file.txt # 显示匹配行及前面 3 行 grep -C 3 \u0026#34;pattern\u0026#34; file.txt # 显示匹配行及前后 3 行 grep -l \u0026#34;pattern\u0026#34; *.txt # 只显示匹配的文件名 grep -E \u0026#34;pattern1|pattern2\u0026#34; file.txt # 使用扩展正则表达式 which/whereis/locate which python # 查找命令的位置（可执行文件） whereis python # 查找命令的二进制、源码和手册 locate \u0026#34;*.txt\u0026#34; # 快速查找文件（基于数据库） updatedb # 更新 locate 数据库 其他常用命令 wget wget https://example.com/file.zip # 下载文件 wget -O output.zip https://example.com/file.zip # 指定文件名 wget -c https://example.com/large.zip # 断点续传 wget -r https://example.com/docs/ # 递归下载（镜像网站） wget -np https://example.com/docs/ # 不下载父目录 wget -q https://example.com/file.zip # 静默模式 head/tail head -n 10 file.txt # 显示前 10 行 head -c 100 file.txt # 显示前 100 字节 tail -n 10 file.txt # 显示后 10 行 tail -f log.txt # 实时显示文件末尾 tail -F log.txt # 实时显示（文件被替换后仍能追踪） touch touch file.txt # 创建空文件 touch -t 202401010000 file.txt # 设置文件时间戳 touch -a file.txt # 只修改访问时间 touch -m file.txt # 只修改修改时间 ln ln -s /path/to/target linkname # 创建符号链接（软链接） ln /path/to/target linkname # 创建硬链接 ls -la linkname # 查看链接指向 readlink linkname # 查看符号链接目标 date date # 显示当前时间 date +\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34; # 自定义格式 date -d \u0026#34;yesterday\u0026#34; # 显示昨天的日期 date -d \u0026#34;next week\u0026#34; # 显示下周的日期 date -s \u0026#34;2024-01-01 12:00:00\u0026#34; # 设置系统时间（需要 root） cal cal # 显示当前月份日历 cal 2024 # 显示全年日历 cal 12 2024 # 显示指定月份 环境变量 查看与设置 echo $PATH # 查看 PATH 环境变量 env # 查看所有环境变量 printenv # 查看所有环境变量（更简洁） export MY_VAR=\u0026#34;hello\u0026#34; # 设置临时环境变量（仅当前会话有效） export PATH=\u0026#34;$PATH:/my/custom/path\u0026#34; # 添加到 PATH MY_VAR=\u0026#34;value\u0026#34; command # 为单个命令设置环境变量 永久设置 编辑 ~/.bashrc 或 ~/.zshrc 文件，添加：\nexport PATH=\u0026#34;$PATH:/my/custom/path\u0026#34; export EDITOR=\u0026#34;vim\u0026#34; export LANG=\u0026#34;zh_CN.UTF-8\u0026#34; 然后执行：\nsource ~/.bashrc # 使配置生效 常用环境变量 变量 说明 HOME 用户主目录 PATH 命令搜索路径 USER 当前用户名 SHELL 当前 Shell EDITOR 默认编辑器 LANG 语言设置 PWD 当前工作目录 实用技巧 命令历史 history # 查看命令历史 history | grep \u0026#34;git\u0026#34; # 搜索历史命令 history -c # 清空命令历史 !123 # 执行历史中的第 123 条命令 !! # 执行上一条命令 !$ # 引用上一条命令的最后一个参数 !git # 执行最近一条 git 命令 Ctrl + R # 反向搜索命令历史 Tab 补全 按 Tab 键可以自动补全命令、文件名、目录名等：\nls docu\u0026lt;Tab\u0026gt; # 自动补全为 documents cd proj\u0026lt;Tab\u0026gt; # 自动补全为 projects git che\u0026lt;Tab\u0026gt; # 自动补全为 checkout/cherry-pick 等 快捷键 快捷键 功能 Ctrl + C 终止当前命令 Ctrl + \\ 终止进程并生成 core dump（发送 SIGQUIT 信号） Ctrl + D 退出 Shell（等价于 exit） Ctrl + L 清屏（等价于 clear） Ctrl + A 移动到命令行开头 Ctrl + E 移动到命令行结尾 Ctrl + U 删除光标前的所有字符 Ctrl + K 删除光标后的所有字符 Ctrl + W 删除光标前的一个单词 Ctrl + Y 粘贴上次删除的内容 Ctrl + R 反向搜索命令历史 Ctrl + Z 暂停当前命令（放入后台） Ctrl + T 交换光标前后两个字符 命令别名 编辑 ~/.bashrc 添加别名：\nalias ll=\u0026#39;ls -la\u0026#39; alias gs=\u0026#39;git status\u0026#39; alias ga=\u0026#39;git add\u0026#39; alias gc=\u0026#39;git commit -m\u0026#39; alias gp=\u0026#39;git push\u0026#39; 然后执行 source ~/.bashrc 使别名生效。\n命令替换 echo \u0026#34;Current date: $(date)\u0026#34; # 命令替换（推荐） echo \u0026#34;Current date: `date`\u0026#34; # 命令替换（传统方式） files=$(ls *.txt) # 将命令输出赋值给变量 for file in $(ls *.txt); do echo $file; done 总结 文件操作命令 命令 用途 pwd 查看当前目录 ls 列出目录内容 cd 切换目录 mkdir 创建目录 touch 创建文件 cat 查看文件内容 cp 复制文件 mv 移动/重命名文件 rm 删除文件 ln 创建链接 权限管理命令 命令 用途 chmod 修改文件权限 chown 修改文件所有者 chgrp 修改文件组 Git 命令 命令 用途 git init 初始化仓库 git add 添加到暂存区 git commit 提交到本地仓库 git push 推送到远程仓库 git pull 拉取远程更新 git branch 分支管理 git switch 切换分支 git merge 合并分支 git rebase 变基 git stash 暂存修改 git restore 恢复文件 文本处理命令 命令 用途 grep 文本搜索 awk 文本处理和数据提取 sed 流编辑 sort 排序 uniq 去重 wc 统计 diff 文件比较 查找命令 命令 用途 find 文件查找 which 命令位置 whereis 命令位置（含手册） locate 快速文件查找 网络命令 命令 用途 curl 发送 HTTP 请求 wget 下载文件 ssh 远程连接服务器 scp 安全文件传输 rsync 同步文件 进程管理命令 命令 用途 ps 查看进程 top 实时进程监控 htop 更友好的进程监控 kill 终止进程 磁盘管理命令 命令 用途 df 磁盘使用情况 du 目录大小 free 内存使用情况 压缩命令 命令 用途 tar 创建/解压 tar 归档 zip/unzip 创建/解压 zip gzip/gunzip 压缩/解压 gzip 管道与重定向 符号 用途 ` ` \u0026gt; 输出重定向（覆盖） \u0026gt;\u0026gt; 输出重定向（追加） 2\u0026gt; 错误输出重定向 2\u0026gt;\u0026amp;1 错误重定向到标准输出 \u0026lt; 输入重定向 通过这一课的学习，你已经掌握了 Shell 和常用命令行工具的基本操作。这些工具是高效开发的基础，熟练使用它们可以极大提升你的工作效率。继续学习，探索更多强大的命令行技巧！\n","permalink":"https://youdward.com/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%95%99%E8%82%B2%E7%BC%BA%E5%A4%B1%E7%9A%84%E4%B8%80%E8%AF%BE1.shell-%E4%B8%8E%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7/","summary":"\u003ch2 id=\"什么是-shell\"\u003e什么是 Shell\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eShell\u003c/strong\u003e 是用户与操作系统内核之间的接口程序。当你在终端中输入命令时，Shell 会解析你的输入并调用相应的程序执行。\u003c/p\u003e\n\u003ch3 id=\"为什么要学习-shell\"\u003e为什么要学习 Shell\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e场景\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e自动化\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e编写脚本自动完成重复性任务\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e远程管理\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eSSH 远程连接服务器时只能使用命令行\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e效率\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e熟练使用命令行比图形界面更快\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e编程基础\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e理解文件系统、进程管理等概念\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"常见的-shell\"\u003e常见的 Shell\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eShell\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eBash\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eBourne Again Shell，最常用的 Shell\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eZsh\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eZ Shell，功能更强大，兼容 Bash\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eFish\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eFriendly Interactive Shell，语法更简单\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"文件系统操作\"\u003e文件系统操作\u003c/h2\u003e\n\u003ch3 id=\"查看当前目录\"\u003e查看当前目录\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epwd\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003epwd -P                  \u003cspan style=\"color:#75715e\"\u003e# 显示物理路径（解析符号链接）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003ccode\u003epwd\u003c/code\u003e 是 \u003ccode\u003eprint working directory\u003c/code\u003e 的缩写，显示当前所在的目录路径。\u003c/p\u003e\n\u003ch3 id=\"列出目录内容\"\u003e列出目录内容\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -l                   \u003cspan style=\"color:#75715e\"\u003e# 详细列表\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -la                  \u003cspan style=\"color:#75715e\"\u003e# 显示所有文件（包括隐藏文件）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -lh                  \u003cspan style=\"color:#75715e\"\u003e# 显示文件大小（人类可读格式）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -la /home/edward     \u003cspan style=\"color:#75715e\"\u003e# 指定目录\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -la --sort\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003esize      \u003cspan style=\"color:#75715e\"\u003e# 按大小排序\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003els -la --sort\u003cspan style=\"color:#f92672\"\u003e=\u003c/span\u003etime      \u003cspan style=\"color:#75715e\"\u003e# 按时间排序\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e示例输出：\u003c/strong\u003e\u003c/p\u003e","title":"计算机教育缺失的一课：1.Shell 与常用命令行工具"},{"content":"命令行环境概述 命令行环境不仅仅是输入命令的地方，它是一个强大的工作空间。通过合理配置命令行环境，可以极大提升工作效率。\n什么是命令行环境 命令行环境包括：\nShell 配置文件：定义环境的行为和外观 环境变量：存储系统级别的配置信息 别名和函数：自定义快捷命令 命令历史：记录之前执行的命令 作业控制：管理前台和后台进程 终端复用器：在单个终端中管理多个会话 Shell 配置文件 Bash 配置文件加载顺序详解 当 Bash 启动时，会按照以下顺序查找并加载配置文件：\n/etc/profile # 系统级配置（所有用户共享） ↓ ~/.bash_profile # 用户登录配置（优先） ↓ 如果不存在 ~/.bash_login # 备用登录配置 ↓ 如果不存在 ~/.profile # 备用登录配置（适用于多种 Shell） ↓ ~/.bashrc # 交互式非登录 Shell 配置 ↓ /etc/bash.bashrc # 系统级交互式配置 加载规则：\n登录 Shell（如 ssh user@host、login 命令）：\n首先执行 /etc/profile（系统级） 然后依次查找 ~/.bash_profile → ~/.bash_login → ~/.profile，只执行第一个找到的 通常会在登录配置中通过 source ~/.bashrc 加载交互式配置 非登录 Shell（如终端窗口、直接输入 bash）：\n只执行 ~/.bashrc 脚本执行（如 bash script.sh）：\n默认不加载任何配置文件 使用 bash -l script.sh 强制作为登录 Shell 执行 登录 Shell vs 非登录 Shell 类型 启动方式 加载配置 用途 登录 Shell ssh user@host、login、su - user .bash_profile → .bashrc 远程登录、系统启动 非登录 Shell 终端窗口、bash、su user .bashrc 日常操作、本地开发 典型配置模式：\n在 ~/.bash_profile 中：\n# 如果 .bashrc 存在，则加载它 if [ -f ~/.bashrc ]; then source ~/.bashrc fi # 登录时执行的一次性操作 echo \u0026#34;Welcome back, $USER!\u0026#34; 这样无论是登录还是非登录 Shell，都会加载 .bashrc 的配置。\n配置文件分离策略 将配置按功能分离可以提高可维护性：\nmkdir -p ~/.bash.d # 创建功能模块 touch ~/.bash.d/aliases # 别名 touch ~/.bash.d/functions # 函数 touch ~/.bash.d/path # 路径配置 touch ~/.bash.d/prompt # 提示符配置 touch ~/.bash.d/git # Git 相关配置 touch ~/.bash.d/tmux # tmux 相关配置 在 ~/.bashrc 中统一加载：\n# 加载所有配置模块 for file in ~/.bash.d/*.bash; do [ -r \u0026#34;$file\u0026#34; ] \u0026amp;\u0026amp; source \u0026#34;$file\u0026#34; done Zsh 配置文件 /etc/zshrc # 系统级配置 ~/.zshrc # 用户级配置（主要，交互式） ~/.zprofile # 登录 Shell 配置（类似 .bash_profile） ~/.zlogin # 登录 Shell 配置（在 .zprofile 之后） ~/.zshenv # 所有 Shell 都会加载（包括脚本） ~/.zlogout # 退出登录时执行 Zsh 配置推荐：\n# ~/.zshenv - 所有 Shell 都会加载，设置关键环境变量 export PATH=\u0026#34;/usr/local/bin:$PATH\u0026#34; export EDITOR=\u0026#34;vim\u0026#34; # ~/.zprofile - 登录 Shell 配置 if [ -f ~/.zshrc ]; then source ~/.zshrc fi # ~/.zshrc - 交互式配置 autoload -Uz compinit \u0026amp;\u0026amp; compinit setopt autocd extendedglob Fish Shell 配置 ~/.config/fish/config.fish # Fish 主配置文件 ~/.config/fish/functions/ # 自定义函数目录 ~/.config/fish/completions/ # 自定义补全目录 配置文件调试 # 查看配置加载过程 bash -x -l -c \u0026#39;echo \u0026#34;loaded\u0026#34;\u0026#39; # 检查配置文件语法 bash -n ~/.bashrc # 临时禁用配置 bash --norc # 查看当前 Shell 类型 echo $0 # 显示 Shell 路径 shopt login_shell # Bash：检查是否为登录 Shell echo $SHLVL # 显示 Shell 嵌套层级 环境变量 查看环境变量 env # 查看所有环境变量（较详细） printenv # 查看所有环境变量（更简洁） echo $PATH # 查看特定环境变量 export # 查看当前导出的变量及其值 declare -x # 查看所有导出的变量（Bash） 设置环境变量 export MY_VAR=\u0026#34;hello\u0026#34; # 设置临时环境变量（仅当前会话有效） export PATH=\u0026#34;$PATH:/my/custom/path\u0026#34; # 添加到 PATH MY_VAR=\u0026#34;value\u0026#34; command # 为单个命令设置环境变量 declare MY_VAR=\u0026#34;hello\u0026#34; # 声明变量但不导出（Bash） 环境变量作用域 # 局部变量（仅当前 Shell） local_var=\u0026#34;local\u0026#34; # 函数内使用 MY_VAR=\u0026#34;hello\u0026#34; # 当前 Shell 内可用 # 导出变量（子进程可见） export MY_VAR=\u0026#34;hello\u0026#34; # 子进程可以访问 # 全局变量（所有会话） # 需要写入配置文件 环境变量继承 # 父进程设置的环境变量会传递给子进程 export MESSAGE=\u0026#34;hello\u0026#34; bash -c \u0026#39;echo $MESSAGE\u0026#39; # 输出: hello # 子进程修改的环境变量不会影响父进程 bash -c \u0026#39;export MESSAGE=\u0026#34;world\u0026#34;\u0026#39; echo $MESSAGE # 输出: hello 永久设置环境变量 按场景选择配置文件：\n场景 推荐文件 说明 用户交互式配置 ~/.bashrc 别名、函数、提示符 用户登录配置 ~/.bash_profile PATH、环境变量 系统级配置 /etc/profile 所有用户共享 所有 Shell（包括脚本） ~/.zshenv (Zsh) 关键环境变量 编辑 ~/.bashrc 或 ~/.zshrc：\nexport PATH=\u0026#34;$PATH:/my/custom/path\u0026#34; export EDITOR=\u0026#34;vim\u0026#34; export LANG=\u0026#34;zh_CN.UTF-8\u0026#34; export TERM=\u0026#34;xterm-256color\u0026#34; export HISTCONTROL=\u0026#34;ignoredups:ignorespace\u0026#34; 然后执行：\nsource ~/.bashrc # 使配置生效 常用环境变量详解 变量 说明 示例值 HOME 用户主目录 /home/edward PATH 命令搜索路径 /usr/bin:/bin USER 当前用户名 edward SHELL 当前 Shell /bin/bash EDITOR 默认编辑器 vim LANG 语言设置 zh_CN.UTF-8 PWD 当前工作目录 /home/edward PS1 命令提示符格式 \\u@\\h \\w $ HISTFILE 命令历史文件路径 ~/.bash_history HISTSIZE 内存中保存的历史条数 1000 HISTFILESIZE 文件中保存的历史条数 10000 TERM 终端类型 xterm-256color LD_LIBRARY_PATH 动态库搜索路径 /usr/local/lib PYTHONPATH Python 模块搜索路径 ~/projects JAVA_HOME Java 安装目录 /usr/lib/jvm/java-11 GOPATH Go 工作目录 ~/go NODE_PATH Node.js 模块搜索路径 /usr/lib/node_modules MANPATH 手册页搜索路径 /usr/share/man DISPLAY X Window 显示服务器 :0 SSH_AUTH_SOCK SSH 代理套接字 /tmp/ssh-agent.sock TZ 时区设置 Asia/Shanghai 自定义命令提示符 PS1 转义字符：\n字符 含义 \\u 当前用户名 \\h 主机名（不含域名） \\H 完整主机名 \\w 当前工作目录（完整路径） \\W 当前工作目录（仅目录名） \\d 日期（周几 月 日） \\t 时间（时:分:秒） \\T 时间（12小时制） \\@ 时间（12小时制 AM/PM） \\$ 普通用户显示 $，root 显示 # \\[ 开始非打印字符序列 \\] 结束非打印字符序列 简单的彩色提示符：\nexport PS1=\u0026#34;\\[\\e[32m\\]\\u@\\h \\[\\e[34m\\]\\w\\[\\e[0m\\] $ \u0026#34; 带 Git 分支信息的提示符：\nparse_git_branch() { git branch 2\u0026gt;/dev/null | sed -e \u0026#39;/^[^*]/d\u0026#39; -e \u0026#39;s/* \\(.*\\)/ (\\1)/\u0026#39; } export PS1=\u0026#34;\\[\\e[32m\\]\\u@\\h \\[\\e[34m\\]\\w\\[\\e[31m\\]\\$(parse_git_branch)\\[\\e[0m\\] $ \u0026#34; 带时间和颜色的高级提示符：\nexport PS1=\u0026#34;\\[\\e[1;36m\\]\\t \\[\\e[1;32m\\]\\u@\\h \\[\\e[1;34m\\]\\w\\[\\e[0m\\]\\[\\e[1;31m\\]\\$(parse_git_branch)\\[\\e[0m\\] \\$ \u0026#34; 多 Shell 共享环境变量 方案一：使用 .profile 文件（所有 Shell 通用）\n# ~/.profile - 所有 Shell 都会读取 export PATH=\u0026#34;$PATH:/my/custom/path\u0026#34; export EDITOR=\u0026#34;vim\u0026#34; export LANG=\u0026#34;zh_CN.UTF-8\u0026#34; 方案二：创建共享配置文件\n# 创建共享配置 mkdir -p ~/.config/shell touch ~/.config/shell/env # 在各个 Shell 配置文件中加载 # ~/.bashrc if [ -f ~/.config/shell/env ]; then source ~/.config/shell/env fi # ~/.zshrc if [ -f ~/.config/shell/env ]; then source ~/.config/shell/env fi # ~/.config/fish/config.fish if test -f ~/.config/shell/env source ~/.config/shell/env end 环境变量实战技巧 # 添加多个路径到 PATH export PATH=\u0026#34;/opt/node/bin:/opt/python/bin:$PATH\u0026#34; # 临时修改 PATH（不影响当前会话） PATH=\u0026#34;/tmp/bin:$PATH\u0026#34; command # 从文件加载环境变量 source .env # 加载 .env 文件 # 将环境变量保存到文件 env \u0026gt; ~/.env.backup # 保存所有环境变量 # 检查环境变量是否存在 if [ -z \u0026#34;$MY_VAR\u0026#34; ]; then echo \u0026#34;MY_VAR 未设置\u0026#34; fi # 设置默认值 MY_VAR=\u0026#34;${MY_VAR:-default_value}\u0026#34; # 如果未设置则使用默认值 # 条件设置环境变量 [ -d ~/go ] \u0026amp;\u0026amp; export GOPATH=\u0026#34;$HOME/go\u0026#34; 命令历史 查看历史 history # 查看命令历史 history | grep \u0026#34;git\u0026#34; # 搜索历史命令 history | tail -20 # 查看最近 20 条命令 history -n 10 # 只显示最近 10 条 执行历史命令 !123 # 执行历史中的第 123 条命令 !! # 执行上一条命令 !$ # 引用上一条命令的最后一个参数 !git # 执行最近一条 git 命令 !git:p # 打印但不执行最近一条 git 命令 !-2 # 执行倒数第二条命令 !* # 引用上一条命令的所有参数 命令替换 ^old^new^ # 替换上一条命令中的第一个 old 为 new !!:gs/old/new/ # 替换上一条命令中的所有 old 为 new !git:s/add/commit/ # 替换最近 git 命令中的 add 为 commit 反向搜索 Ctrl + R # 反向搜索命令历史 输入关键词后：\n按 Ctrl + R 继续搜索更早的匹配 按 Enter 执行命令 按 Ctrl + G 退出搜索 按 Tab 或 Ctrl + Y 将匹配内容粘贴到命令行 历史配置 export HISTSIZE=10000 # 内存中保存的历史条数 export HISTFILESIZE=100000 # 文件中保存的历史条数 export HISTCONTROL=\u0026#34;ignoredups:ignorespace\u0026#34; # 忽略重复和空格开头的命令 export HISTIGNORE=\u0026#34;ls:ll:cd\u0026#34; # 忽略特定命令 export HISTTIMEFORMAT=\u0026#34;%Y-%m-%d %H:%M:%S \u0026#34; # 显示时间戳 export HISTFILE=~/.bash_history # 指定历史文件路径 export HISTCMD # 当前命令在历史中的编号 HISTCONTROL 选项：\n选项 说明 ignorespace 忽略以空格开头的命令 ignoredups 忽略连续重复的命令 ignoreboth 同时启用 ignorespace 和 ignoredups erasedups 删除历史中所有重复的命令 管理历史文件 ~/.bash_history # Bash 历史文件 ~/.zsh_history # Zsh 历史文件 history -c # 清空当前会话历史 history -w # 立即写入历史文件 history -a # 将当前会话历史追加到文件 history -r # 从文件读取历史到当前会话 rm ~/.bash_history # 删除历史文件 历史记录安全 # 禁止保存包含密码的命令 export HISTIGNORE=\u0026#34;*password*:*secret*:*token*\u0026#34; # 使用空格开头隐藏敏感命令 ls -la # 这条命令不会被保存 # 加密历史文件（高级） gpg -c ~/.bash_history # 加密 gpg -d ~/.bash_history.gpg \u0026gt; ~/.bash_history # 解密 跨终端同步历史 Bash 配置：\n# ~/.bashrc - 实时同步历史 export PROMPT_COMMAND=\u0026#34;history -a; history -c; history -r; $PROMPT_COMMAND\u0026#34; Zsh 配置：\n# ~/.zshrc - 实时同步历史 setopt inc_append_history setopt share_history 历史记录高级技巧 # 搜索包含特定参数的命令 history | grep \u0026#34;ssh\u0026#34; | grep \u0026#34;server\u0026#34; # 统计最常用的命令 history | awk \u0026#39;{print $1}\u0026#39; | sort | uniq -c | sort -rn | head -10 # 删除历史中的特定命令 history -d 123 # 删除第 123 条命令 # 过滤历史记录 history | grep -v \u0026#34;^[0-9]* ls\u0026#34; # 排除 ls 命令 # 将历史记录导出到文件 history \u0026gt; ~/history_backup.txt # 从文件导入历史记录 cat ~/history_backup.txt \u0026gt;\u0026gt; ~/.bash_history history -r # 使用 fzf 模糊搜索历史 history | fzf --tac # 按时间倒序显示 别名和函数 别名 alias ll=\u0026#39;ls -la\u0026#39; # 列出详细信息 alias ls=\u0026#39;ls --color=auto\u0026#39; # 带颜色的 ls alias la=\u0026#39;ls -la\u0026#39; # 列出所有文件（包括隐藏） alias l=\u0026#39;ls -CF\u0026#39; # 简洁列表 alias gs=\u0026#39;git status\u0026#39; # Git 状态 alias ga=\u0026#39;git add\u0026#39; # Git 添加 alias gc=\u0026#39;git commit -m\u0026#39; # Git 提交 alias gp=\u0026#39;git push\u0026#39; # Git 推送 alias gl=\u0026#39;git log --oneline\u0026#39; # Git 日志 alias gd=\u0026#39;git diff\u0026#39; # Git 差异 alias ..=\u0026#39;cd ..\u0026#39; # 上级目录 alias ...=\u0026#39;cd ../..\u0026#39; # 上两级目录 alias ....=\u0026#39;cd ../../..\u0026#39; # 上三级目录 alias cls=\u0026#39;clear\u0026#39; # 清屏 alias grep=\u0026#39;grep --color=auto\u0026#39; # 带颜色的 grep alias egrep=\u0026#39;egrep --color=auto\u0026#39; # 带颜色的 egrep alias fgrep=\u0026#39;fgrep --color=auto\u0026#39; # 带颜色的 fgrep alias df=\u0026#39;df -h\u0026#39; # 人类可读的磁盘使用情况 alias du=\u0026#39;du -h\u0026#39; # 人类可读的目录大小 alias free=\u0026#39;free -h\u0026#39; # 人类可读的内存使用情况 alias ps=\u0026#39;ps aux\u0026#39; # 显示所有进程 条件别名 # 只有在特定条件下才定义别名 if command -v exa \u0026amp;\u0026gt; /dev/null; then alias ls=\u0026#39;exa\u0026#39; alias ll=\u0026#39;exa -l\u0026#39; alias la=\u0026#39;exa -la\u0026#39; fi # 根据操作系统定义不同别名 if [[ \u0026#34;$OSTYPE\u0026#34; == \u0026#34;darwin\u0026#34;* ]]; then alias ls=\u0026#39;ls -G\u0026#39; # macOS 颜色 else alias ls=\u0026#39;ls --color=auto\u0026#39; # Linux 颜色 fi 函数 # 创建目录并进入 mkcd() { mkdir -p \u0026#34;$1\u0026#34; \u0026amp;\u0026amp; cd \u0026#34;$1\u0026#34; } # 提取各种压缩文件 extract() { if [ -f \u0026#34;$1\u0026#34; ] ; then case \u0026#34;$1\u0026#34; in *.tar.bz2) tar xvjf \u0026#34;$1\u0026#34; ;; *.tar.gz) tar xvzf \u0026#34;$1\u0026#34; ;; *.bz2) bunzip2 \u0026#34;$1\u0026#34; ;; *.rar) unrar x \u0026#34;$1\u0026#34; ;; *.gz) gunzip \u0026#34;$1\u0026#34; ;; *.tar) tar xvf \u0026#34;$1\u0026#34; ;; *.tbz2) tar xvjf \u0026#34;$1\u0026#34; ;; *.tgz) tar xvzf \u0026#34;$1\u0026#34; ;; *.zip) unzip \u0026#34;$1\u0026#34; ;; *.Z) uncompress \u0026#34;$1\u0026#34; ;; *.7z) 7z x \u0026#34;$1\u0026#34; ;; *) echo \u0026#34;无法识别的格式: $1\u0026#34; ;; esac else echo \u0026#34;$1 不是有效的文件\u0026#34; fi } # 快速查找文件并编辑 fe() { find . -type f -name \u0026#34;$1\u0026#34; -exec vim {} \\; } # 查找包含特定文本的文件 ff() { find . -type f -exec grep -l \u0026#34;$1\u0026#34; {} \\; } # 快速创建 Python 虚拟环境 venv() { python3 -m venv .venv source .venv/bin/activate echo \u0026#34;虚拟环境已激活\u0026#34; } # 退出虚拟环境并删除 deactivate_rm() { deactivate rm -rf .venv echo \u0026#34;虚拟环境已删除\u0026#34; } # 统计目录大小（带进度） duh() { du -h --max-depth=1 \u0026#34;$1\u0026#34; | sort -rh } # 查看端口占用情况 port() { lsof -i :\u0026#34;$1\u0026#34; 2\u0026gt;/dev/null || ss -tuln | grep \u0026#34;$1\u0026#34; } # 杀死占用指定端口的进程 killport() { kill -9 $(lsof -ti:\u0026#34;$1\u0026#34;) 2\u0026gt;/dev/null || echo \u0026#34;端口 $1 未被占用\u0026#34; } # 创建临时目录并进入 tmpcd() { local tmpdir=$(mktemp -d) cd \u0026#34;$tmpdir\u0026#34; echo \u0026#34;进入临时目录: $tmpdir\u0026#34; } # 显示最近修改的文件 recent() { ls -lt \u0026#34;${1:-.}\u0026#34; | head -\u0026#34;${2:-20}\u0026#34; } # 查看文件类型并决定打开方式 openfile() { local file=\u0026#34;$1\u0026#34; if [ -f \u0026#34;$file\u0026#34; ]; then case \u0026#34;$file\u0026#34; in *.txt|*.md|*.py|*.js|*.json) vim \u0026#34;$file\u0026#34; ;; *.pdf) xdg-open \u0026#34;$file\u0026#34; ;; *.jpg|*.png|*.gif) xdg-open \u0026#34;$file\u0026#34; ;; *) echo \u0026#34;未知文件类型\u0026#34; ;; esac else echo \u0026#34;文件不存在\u0026#34; fi } 函数进阶技巧 # 使用参数默认值 backup() { local src=\u0026#34;${1:-.}\u0026#34; local dest=\u0026#34;${2:-~/backup/$(date +%Y%m%d)}\u0026#34; mkdir -p \u0026#34;$dest\u0026#34; rsync -av --delete \u0026#34;$src\u0026#34; \u0026#34;$dest\u0026#34; echo \u0026#34;备份完成: $src -\u0026gt; $dest\u0026#34; } # 使用选项参数 mycommand() { local verbose=false local output=\u0026#34;output.txt\u0026#34; while [[ \u0026#34;$#\u0026#34; -gt 0 ]]; do case $1 in -v|--verbose) verbose=true ;; -o|--output) output=\u0026#34;$2\u0026#34;; shift ;; *) echo \u0026#34;未知选项: $1\u0026#34;; return 1 ;; esac shift done if $verbose; then echo \u0026#34;详细模式已开启\u0026#34; echo \u0026#34;输出文件: $output\u0026#34; fi } # 使用 read 交互输入 confirm() { read -p \u0026#34;$1 (y/n): \u0026#34; -n 1 -r echo [[ $REPLY =~ ^[Yy]$ ]] } # 使用 trap 清理临时文件 temp_example() { local tmpfile=$(mktemp) trap \u0026#34;rm -f $tmpfile\u0026#34; EXIT echo \u0026#34;临时文件: $tmpfile\u0026#34; # 执行操作... } # 返回值处理 check_file() { if [ -f \u0026#34;$1\u0026#34; ]; then return 0 else return 1 fi } 动态别名 # 根据当前目录设置别名 case \u0026#34;$PWD\u0026#34; in /home/*/projects/*) alias build=\u0026#39;npm run build\u0026#39; alias dev=\u0026#39;npm run dev\u0026#39; ;; /home/*/go/*) alias run=\u0026#39;go run .\u0026#39; alias build=\u0026#39;go build\u0026#39; ;; esac # 使用函数替代复杂别名 gpush() { git push origin \u0026#34;$(git branch --show-current)\u0026#34; } gpull() { git pull origin \u0026#34;$(git branch --show-current)\u0026#34; } 别名和函数的区别 特性 别名 函数 复杂度 适合简单命令替换 适合复杂逻辑 参数处理 不支持参数 支持参数 控制流 无 支持 if/for/while 性能 稍快 稍慢 可读性 适合短命令 适合长逻辑 保存别名和函数 将别名和函数添加到 ~/.bashrc 或 ~/.zshrc 中，这样每次启动 Shell 时都会自动加载。\n分离配置推荐：\n# ~/.bashrc source ~/.bash_aliases source ~/.bash_functions 检查别名和函数：\nalias # 查看所有别名 unalias ll # 删除别名 declare -f # 查看所有函数 declare -f mkcd # 查看特定函数 unset -f mkcd # 删除函数 作业控制 后台运行 command \u0026amp; # 在后台运行命令 nohup command \u0026amp; # 后台运行，退出终端后仍继续执行 setsid command # 创建新会话运行命令 (command \u0026amp;) # 在子 Shell 中后台运行 查看后台作业 jobs # 查看后台任务 jobs -l # 查看后台任务及 PID jobs -p # 只显示 PID jobs -r # 只显示运行中的任务 jobs -s # 只显示暂停的任务 切换作业 fg %1 # 将后台任务 1 切换到前台 fg %- # 将最近的后台任务切换到前台 fg %string # 将名称包含 string 的任务切换到前台 bg %1 # 将暂停的任务 1 放到后台继续 bg # 将最近暂停的任务放到后台继续 暂停和恢复 Ctrl + Z # 暂停当前前台任务（放入后台） kill -CONT \u0026lt;pid\u0026gt; # 恢复暂停的进程 kill -STOP \u0026lt;pid\u0026gt; # 暂停进程 kill -CONT %1 # 恢复作业 1 kill -STOP %1 # 暂停作业 1 终止作业 kill %1 # 终止后台任务 1（发送 SIGTERM） kill -9 %1 # 强制终止后台任务 1（发送 SIGKILL） kill -15 %1 # 优雅终止（SIGTERM） killall process_name # 终止所有同名进程 pkill process_name # 按名称终止进程 信号详解 # 查看所有信号 kill -l # 常用信号 kill -1 \u0026lt;pid\u0026gt; # SIGHUP - 挂起（重新加载配置） kill -2 \u0026lt;pid\u0026gt; # SIGINT - 中断（Ctrl + C） kill -3 \u0026lt;pid\u0026gt; # SIGQUIT - 退出（Ctrl + \\） kill -9 \u0026lt;pid\u0026gt; # SIGKILL - 强制终止 kill -15 \u0026lt;pid\u0026gt; # SIGTERM - 优雅终止 kill -19 \u0026lt;pid\u0026gt; # SIGSTOP - 暂停（不能被捕获） kill -18 \u0026lt;pid\u0026gt; # SIGCONT - 继续 常用信号表：\n信号 编号 说明 SIGHUP 1 终端断开连接 SIGINT 2 用户中断（Ctrl + C） SIGQUIT 3 用户退出（Ctrl + \\），产生 core dump SIGKILL 9 强制终止（不可捕获） SIGTERM 15 优雅终止（默认） SIGSTOP 19 暂停进程（不可捕获） SIGCONT 18 继续暂停的进程 SIGUSR1 10 用户自定义信号 1 SIGUSR2 12 用户自定义信号 2 进程组 # 查看进程组 ps -o pgid= \u0026lt;pid\u0026gt; # 查看进程所属进程组 ps axj # 查看进程组信息 # 创建进程组 (set -m; command1 \u0026amp; command2 \u0026amp;) # 在子 Shell 中创建新进程组 # 向进程组发送信号 kill -TERM -\u0026lt;pgid\u0026gt; # 向整个进程组发送信号 作业控制高级技巧 # 查看作业状态 jobs -l # 示例输出: [1]+ Running sleep 100 \u0026amp; # [2]- Stopped vim file.txt # 作业状态符号: # + : 当前作业（最近放入后台的） # - : 前一个作业 # Running : 运行中 # Stopped : 已暂停 # 切换到特定作业 fg %1 # 切换到作业 1 fg %vim # 切换到 vim 作业 # 查看进程树 pstree \u0026lt;pid\u0026gt; # 查看进程树 pstree -p \u0026lt;pid\u0026gt; # 显示 PID # 查看进程详细信息 ps aux | grep process_name # 查看进程 ps -fp \u0026lt;pid\u0026gt; # 查看进程详细信息 # 查看进程资源使用 top -p \u0026lt;pid\u0026gt; # 实时查看进程资源 htop -p \u0026lt;pid\u0026gt; # 更友好的进程查看 # 查看进程打开的文件 lsof -p \u0026lt;pid\u0026gt; # 查看进程打开的文件 lsof -i :8080 # 查看占用端口的进程 示例：后台运行长时间任务 # 后台运行备份脚本 nohup ./backup.sh \u0026gt; backup.log 2\u0026gt;\u0026amp;1 \u0026amp; # 查看进度 tail -f backup.log # 切换到前台 fg %1 # 暂停任务 Ctrl + Z # 放到后台继续 bg %1 # 查看作业状态 jobs -l # 终止任务 kill %1 使用 systemd 管理长期运行的服务 # 创建服务文件 sudo vim /etc/systemd/system/myapp.service # 服务文件内容 [Unit] Description=My Application After=network.target [Service] Type=simple User=edward WorkingDirectory=/home/edward/app ExecStart=/home/edward/app/start.sh Restart=always RestartSec=5 [Install] WantedBy=multi-user.target # 启用服务 sudo systemctl daemon-reload sudo systemctl enable myapp sudo systemctl start myapp # 查看状态 sudo systemctl status myapp sudo journalctl -u myapp -f 终端复用器 终端复用器允许你在单个终端窗口中管理多个会话，是高效开发的必备工具。\ntmux（推荐） 安装 sudo apt install tmux # Debian/Ubuntu sudo yum install tmux # CentOS/RHEL brew install tmux # macOS 基本操作 tmux # 启动 tmux tmux new -s session_name # 创建命名会话 tmux attach -t session_name # 连接到会话 tmux ls # 列出所有会话 tmux kill-session -t session_name # 杀死会话 tmux kill-server # 杀死所有会话 会话管理 # 创建会话 tmux new -s work # 创建名为 work 的会话 tmux new -s project -c ~/projects # 在指定目录创建会话 # 列出会话 tmux ls # 连接会话 tmux attach -t work # 连接到 work 会话 tmux a -t work # 简写 # 分离会话 tmux detach # 分离当前会话 Ctrl + B 然后 d # 快捷键分离 # 切换会话 tmux switch -t work # 切换到 work 会话 tmux switch -n # 切换到下一个会话 tmux switch -p # 切换到上一个会话 # 重命名会话 tmux rename-session -t old new # 重命名会话 Ctrl + B 然后 $ # 快捷键重命名 # 杀死会话 tmux kill-session -t work # 杀死 work 会话 tmux kill-session -a # 杀死除当前外的所有会话 tmux kill-session -a -t work # 杀死除 work 外的所有会话 窗口管理 # 创建窗口 tmux new-window # 创建新窗口 tmux new-window -n editor # 创建名为 editor 的窗口 tmux new-window -c ~/projects # 在指定目录创建窗口 Ctrl + B 然后 c # 快捷键创建 # 切换窗口 tmux select-window -t 0 # 切换到窗口 0 tmux select-window -n # 切换到下一个窗口 tmux select-window -p # 切换到上一个窗口 tmux select-window -t :editor # 切换到名为 editor 的窗口 Ctrl + B 然后 n # 快捷键切换到下一个 Ctrl + B 然后 p # 快捷键切换到上一个 Ctrl + B 然后 0-9 # 快捷键切换到指定窗口 # 重命名窗口 tmux rename-window new_name # 重命名窗口 Ctrl + B 然后 , # 快捷键重命名 # 关闭窗口 tmux kill-window # 关闭当前窗口 tmux kill-window -t 0 # 关闭窗口 0 Ctrl + B 然后 \u0026amp; # 快捷键关闭 # 交换窗口 tmux swap-window -s 0 -t 1 # 交换窗口 0 和 1 窗格管理 # 分割窗格 tmux split-window -h # 水平分割 tmux split-window -v # 垂直分割 tmux split-window -h -c ~/projects # 在指定目录分割 Ctrl + B 然后 % # 快捷键水平分割 Ctrl + B 然后 \u0026#34; # 快捷键垂直分割 # 切换窗格 tmux select-pane -U # 向上切换 tmux select-pane -D # 向下切换 tmux select-pane -L # 向左切换 tmux select-pane -R # 向右切换 tmux select-pane -t :.0 # 切换到指定窗格 Ctrl + B 然后 方向键 # 快捷键切换 Ctrl + B 然后 o # 顺时针切换 # 移动窗格 tmux swap-pane -U # 向上移动 tmux swap-pane -D # 向下移动 Ctrl + B 然后 { # 向左移动 Ctrl + B 然后 } # 向右移动 # 调整窗格大小 tmux resize-pane -U 5 # 向上调整 5 行 tmux resize-pane -D 5 # 向下调整 5 行 tmux resize-pane -L 5 # 向左调整 5 列 tmux resize-pane -R 5 # 向右调整 5 列 tmux resize-pane -Z # 最大化/恢复 Ctrl + B 然后 Ctrl + 方向键 # 快捷键调整大小 Ctrl + B 然后 z # 快捷键最大化 # 关闭窗格 tmux kill-pane # 关闭当前窗格 tmux kill-pane -t :.1 # 关闭指定窗格 # 窗格布局 tmux select-layout even-horizontal # 水平均分 tmux select-layout even-vertical # 垂直均分 tmux select-layout main-horizontal # 主水平布局 tmux select-layout main-vertical # 主垂直布局 tmux select-layout tiled # 平铺布局 tmux 快捷键 快捷键 功能 Ctrl + B 然后 % 水平分割窗口 Ctrl + B 然后 \u0026quot; 垂直分割窗口 Ctrl + B 然后 方向键 在窗格间切换 Ctrl + B 然后 z 最大化/恢复窗格 Ctrl + B 然后 c 创建新窗口 Ctrl + B 然后 n 切换到下一个窗口 Ctrl + B 然后 p 切换到上一个窗口 Ctrl + B 然后 d 分离会话（后台运行） Ctrl + B 然后 : 进入命令模式 Ctrl + B 然后 ? 显示快捷键帮助 Ctrl + B 然后 $ 重命名会话 Ctrl + B 然后 , 重命名窗口 Ctrl + B 然后 { 向左移动窗格 Ctrl + B 然后 } 向右移动窗格 Ctrl + B 然后 \u0026amp; 关闭窗口 Ctrl + B 然后 o 顺时针切换窗格 Ctrl + B 然后 0-9 切换到指定窗口 Ctrl + B 然后 ! 将窗格拆分为独立窗口 tmux 配置 创建 ~/.tmux.conf：\n# 基础配置 set -g default-terminal \u0026#34;xterm-256color\u0026#34; set -g history-limit 10000 set -g mouse on set -g default-shell \u0026#34;/bin/bash\u0026#34; # 前缀键 set -g prefix C-a unbind C-b bind C-a send-prefix # 状态栏 set -g status-bg \u0026#34;#222222\u0026#34; set -g status-fg \u0026#34;#888888\u0026#34; set -g status-left \u0026#34;#[fg=green]#H\u0026#34; set -g status-right \u0026#34;#[fg=blue]%H:%M:%S #[fg=red]%d-%b-%y\u0026#34; set -g status-interval 1 set -g status-justify centre # 窗口列表 set -g window-status-format \u0026#34;#[fg=cyan]#I #[fg=white]#W\u0026#34; set -g window-status-current-format \u0026#34;#[fg=green]#I #[fg=white]#W\u0026#34; # 窗格分割 bind | split-window -h bind - split-window -v bind k split-window -v -c \u0026#39;#{pane_current_path}\u0026#39; bind j split-window -h -c \u0026#39;#{pane_current_path}\u0026#39; # 窗格导航 bind h select-pane -L bind j select-pane -D bind k select-pane -U bind l select-pane -R # 窗格大小调整 bind -r H resize-pane -L 5 bind -r J resize-pane -D 5 bind -r K resize-pane -U 5 bind -r L resize-pane -R 5 # 窗口导航 bind -r 0 select-window -t :0 bind -r 1 select-window -t :1 bind -r 2 select-window -t :2 bind -r 3 select-window -t :3 bind -r 4 select-window -t :4 bind -r 5 select-window -t :5 bind -r 6 select-window -t :6 bind -r 7 select-window -t :7 bind -r 8 select-window -t :8 bind -r 9 select-window -t :9 # 重新加载配置 bind r source-file ~/.tmux.conf \\; display-message \u0026#34;Config reloaded!\u0026#34; # 复制模式 set -g mode-keys vi bind-key -T copy-mode-vi \u0026#39;v\u0026#39; send-keys -X begin-selection bind-key -T copy-mode-vi \u0026#39;y\u0026#39; send-keys -X copy-selection-and-cancel bind-key -T copy-mode-vi \u0026#39;Escape\u0026#39; send-keys -X cancel # 同步窗格 bind w setw synchronize-panes tmux 插件系统 安装 TPM（tmux plugin manager）：\ngit clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm 配置插件：\n在 ~/.tmux.conf 中添加：\n# 插件列表 set -g @plugin \u0026#39;tmux-plugins/tpm\u0026#39; set -g @plugin \u0026#39;tmux-plugins/tmux-sensible\u0026#39; set -g @plugin \u0026#39;tmux-plugins/tmux-resurrect\u0026#39; set -g @plugin \u0026#39;tmux-plugins/tmux-continuum\u0026#39; set -g @plugin \u0026#39;tmux-plugins/tmux-yank\u0026#39; set -g @plugin \u0026#39;catppuccin/tmux\u0026#39; # 初始化插件 run \u0026#39;~/.tmux/plugins/tpm/tpm\u0026#39; 常用插件：\n插件 功能 tmux-sensible 合理的默认配置 tmux-resurrect 保存和恢复会话 tmux-continuum 自动保存和恢复 tmux-yank 系统剪贴板支持 tmux-cpu CPU 使用率显示 tmux-memory 内存使用率显示 catppuccin/tmux 美观的主题 使用 TPM：\nCtrl + B 然后 I # 安装插件 Ctrl + B 然后 U # 更新插件 Ctrl + B 然后 Alt + U # 卸载插件 进阶用法 # 在会话间切换 tmux switch -t session_name # 重命名会话 Ctrl + B 然后 $ # 重命名窗口 Ctrl + B 然后 , # 移动窗格 Ctrl + B 然后 { 或 } # 调整窗格大小 Ctrl + B 然后 Ctrl + 方向键 # 同步所有窗格（同时执行相同命令） tmux setw synchronize-panes on # 开启同步 tmux setw synchronize-panes off # 关闭同步 # 保存和恢复会话（需要 tmux-resurrect） Ctrl + B 然后 Ctrl + s # 保存 Ctrl + B 然后 Ctrl + r # 恢复 # 录制和回放会话 tmux capture-pane -p \u0026gt; output.txt # 捕获当前窗格内容 tmux new-session -d -s demo \u0026#39;cat output.txt\u0026#39; # 回放 脚本化 tmux # 创建开发环境 tmux new-session -d -s dev tmux split-window -h -t dev tmux split-window -v -t dev:0 tmux select-window -t dev tmux attach -t dev # 启动项目 tmux new-session -d -s project tmux send-keys -t project \u0026#39;cd ~/projects/myapp\u0026#39; C-m tmux send-keys -t project \u0026#39;npm run dev\u0026#39; C-m tmux split-window -h -t project tmux send-keys -t project:1 \u0026#39;cd ~/projects/myapp\u0026#39; C-m tmux send-keys -t project:1 \u0026#39;vim .\u0026#39; C-m tmux attach -t project screen（传统终端复用器） screen # 启动 screen screen -S session_name # 创建命名会话 screen -r session_name # 恢复会话 screen -ls # 列出会话 screen -x session_name # 多用户共享会话 Ctrl + A 然后 d # 分离会话 Ctrl + A 然后 c # 创建新窗口 Ctrl + A 然后 n # 切换到下一个窗口 Ctrl + A 然后 p # 切换到上一个窗口 Ctrl + A 然后 0-9 # 切换到指定窗口 Ctrl + A 然后 ? # 显示帮助 screen -wipe # 清理死会话 SSH 配置 基础连接 ssh user@hostname # 基本连接 ssh -p 2222 user@hostname # 指定端口 ssh -i ~/.ssh/my_key user@hostname # 指定密钥文件 ssh -l user hostname # 指定用户名 ssh user@hostname command # 在远程执行命令并返回 SSH 配置文件 创建 ~/.ssh/config：\nHost server1 HostName 192.168.1.100 User edward Port 22 IdentityFile ~/.ssh/id_ed25519 ForwardAgent yes ServerAliveInterval 60 Host github HostName github.com User git IdentityFile ~/.ssh/github_key Host * Compression yes TCPKeepAlive yes 使用配置：\nssh server1 # 直接连接 配置文件权限：\nchmod 600 ~/.ssh/config # 必须设置为 600 chmod 700 ~/.ssh # 必须设置为 700 SSH 密钥管理 # 生成密钥对 ssh-keygen -t ed25519 -C \u0026#34;your_email@example.com\u0026#34; ssh-keygen -t rsa -b 4096 -C \u0026#34;your_email@example.com\u0026#34; # RSA 格式 # 指定密钥文件路径 ssh-keygen -t ed25519 -f ~/.ssh/github_key -C \u0026#34;github\u0026#34; # 复制公钥到服务器 ssh-copy-id user@hostname ssh-copy-id -i ~/.ssh/my_key user@hostname # 手动复制公钥 cat ~/.ssh/id_ed25519.pub | ssh user@hostname \u0026#34;mkdir -p ~/.ssh \u0026amp;\u0026amp; cat \u0026gt;\u0026gt; ~/.ssh/authorized_keys\u0026#34; # 使用密钥代理 eval \u0026#34;$(ssh-agent -s)\u0026#34; ssh-add ~/.ssh/id_ed25519 ssh-add -l # 查看已添加的密钥 ssh-add -d ~/.ssh/id_ed25519 # 删除密钥 ssh-add -D # 删除所有密钥 密钥文件权限：\nchmod 600 ~/.ssh/id_ed25519 # 私钥必须设置为 600 chmod 644 ~/.ssh/id_ed25519.pub # 公钥设置为 644 SSH Agent Forwarding 什么是 Agent Forwarding？\nAgent Forwarding 允许你在远程服务器上使用本地的 SSH 密钥，而不需要将私钥复制到远程服务器上。\n启用 Agent Forwarding：\n# 命令行启用 ssh -A user@hostname # 配置文件启用 Host server1 ForwardAgent yes 工作原理：\n本地机器 (Agent 运行，持有私钥) | | SSH 连接 (Agent Forwarding 启用) | 远程服务器 (通过 SSH 连接访问本地 Agent) | | 使用本地 Agent 认证 | GitHub/GitLab/其他服务器 安全注意事项：\n只在信任的服务器上启用 Agent Forwarding 可以通过 ssh-agent -k 结束 Agent 使用 ssh-add -x 锁定 Agent，需要密码才能使用 SSH ControlMaster（连接复用） 什么是 ControlMaster？\nControlMaster 允许复用同一个 SSH 连接，避免每次连接都重新认证，大幅提升连接速度。\n配置 ControlMaster：\n# ~/.ssh/config Host * ControlMaster auto ControlPath ~/.ssh/sockets/%r@%h-%p ControlPersist 600 # 保持连接 10 分钟 手动控制：\n# 创建主连接 ssh -M -S ~/.ssh/sockets/master user@hostname # 复用连接 ssh -S ~/.ssh/sockets/master user@hostname # 关闭连接 ssh -O exit -S ~/.ssh/sockets/master user@hostname SSH 端口转发 # 本地端口转发 ssh -L 8080:localhost:3000 user@server ssh -L 8080:remote-host:3000 user@server # 远程端口转发 ssh -R 8080:localhost:3000 user@server # 动态端口转发（SOCKS 代理） ssh -D 1080 user@server # 绑定到所有接口 ssh -L 0.0.0.0:8080:localhost:3000 user@server 端口转发示例：\n# 通过 SSH 访问远程数据库 ssh -L 5432:localhost:5432 user@db-server # 通过 SSH 访问内网服务 ssh -L 8080:internal-service:80 user@gateway # 将本地服务暴露到远程 ssh -R 8080:localhost:3000 user@server SSH 隧道示例 # 通过跳板机访问内网服务器 ssh -J jump@jumphost user@internal-server # 配置到 ~/.ssh/config Host internal HostName internal-server.local User edward ProxyJump jump@jumphost # 使用 ProxyCommand（旧版方式） Host internal HostName internal-server.local User edward ProxyCommand ssh -W %h:%p jump@jumphost SSH 安全配置 服务器端安全配置（/etc/ssh/sshd_config）：\n# 禁用密码登录 PasswordAuthentication no # 禁用 root 登录 PermitRootLogin no # 限制登录用户 AllowUsers edward admin AllowGroups ssh-users # 更改默认端口 Port 2222 # 限制连接数 MaxStartups 10:30:60 # 启用公钥认证 PubkeyAuthentication yes # 禁用无用的认证方式 ChallengeResponseAuthentication no GSSAPIAuthentication no # 设置空闲超时 ClientAliveInterval 600 ClientAliveCountMax 3 客户端安全配置：\n# ~/.ssh/config Host * PasswordAuthentication no # 优先使用密钥认证 PubkeyAuthentication yes StrictHostKeyChecking accept-new # 自动接受新主机密钥 HashKnownHosts yes # 对主机名进行哈希 SSH 多跳连接 # 直接多跳 ssh -J user1@host1,user2@host2 user3@host3 # 通过多个跳板机 ssh -o ProxyJump=\u0026#34;user1@host1,user2@host2\u0026#34; user3@host3 # 配置文件 Host host3 HostName host3.internal User user3 ProxyJump user1@host1,user2@host2 SSH 高级技巧 # 保持连接活跃 ssh -o ServerAliveInterval=60 user@hostname # 压缩传输 ssh -C user@hostname # 禁用 X11 转发 ssh -x user@hostname # 设置连接超时 ssh -o ConnectTimeout=10 user@hostname # 调试连接 ssh -v user@hostname # 详细输出 ssh -vvv user@hostname # 更详细输出 # 批量执行命令 ssh user@hostname \u0026#39;command1; command2; command3\u0026#39; # 传送文件 scp file.txt user@hostname:/path/ scp -r folder/ user@hostname:/path/ # 使用 rsync 同步 rsync -avz local/ user@hostname:/remote/ # 创建 SSH 隧道并后台运行 ssh -f -N -L 8080:localhost:3000 user@server # 使用 SSH 作为 SOCKS 代理 ssh -D 1080 -f -N user@server SSH 配置文件高级选项 选项 说明 IdentityFile 指定私钥文件路径 ForwardAgent 是否启用 Agent Forwarding ForwardX11 是否启用 X11 转发 ServerAliveInterval 心跳间隔（秒） ControlMaster 是否启用连接复用 ControlPath 连接套接字路径 ControlPersist 保持连接时间 ProxyJump 跳板机配置 ProxyCommand 代理命令 Compression 是否启用压缩 TCPKeepAlive 是否启用 TCP 保活 StrictHostKeyChecking 主机密钥检查策略 HashKnownHosts 是否哈希主机名 文件查找与定位 which/whereis/locate which python # 查找命令的可执行文件位置 whereis python # 查找命令的二进制、源码和手册 locate \u0026#34;*.txt\u0026#34; # 快速查找文件（基于数据库） updatedb # 更新 locate 数据库 find find . -name \u0026#34;*.py\u0026#34; # 查找 Python 文件 find . -type f -size +10M # 查找大于 10MB 的文件 find . -mtime -7 # 查找最近 7 天修改的文件 find . -user edward # 查找指定用户的文件 find . -name \u0026#34;__pycache__\u0026#34; -exec rm -rf {} + # 删除目录 fzf（模糊查找工具） 安装：\ngit clone --depth 1 https://github.com/junegunn/fzf.git ~/.fzf ~/.fzf/install 使用：\nfzf # 交互式模糊查找 Ctrl + T # 在命令行中插入文件路径 Ctrl + R # 模糊搜索命令历史 命令行工具链 管道与重定向 command1 | command2 # 管道：将 command1 的输出作为 command2 的输入 command \u0026gt; output.txt # 覆盖输出到文件 command \u0026gt;\u0026gt; output.txt # 追加输出到文件 command 2\u0026gt; error.txt # 错误输出重定向 command 2\u0026gt;\u0026amp;1 # 错误重定向到标准输出 command \u0026lt; input.txt # 输入重定向 command \u0026lt;\u0026lt; EOF # Here-doc 输入 进程替换 diff \u0026lt;(sort file1.txt) \u0026lt;(sort file2.txt) # 比较两个排序后的文件 grep \u0026#34;pattern\u0026#34; \u0026lt;(cat file1.txt file2.txt) # 在合并内容中搜索 命令分组 (command1 \u0026amp;\u0026amp; command2) \u0026gt; output.txt # 分组执行，重定向输出 { command1; command2; } \u0026gt; output.txt # 分组执行（注意分号和空格） 子 Shell (cd /tmp \u0026amp;\u0026amp; ls) # 在子 Shell 中执行，不影响当前目录 终端配置 终端类型 echo $TERM # 查看终端类型 export TERM=\u0026#34;xterm-256color\u0026#34; # 设置 256 色终端 颜色支持 # 测试 256 色 for i in {0..255}; do printf \u0026#34;\\x1b[38;5;${i}mcolour${i}\\x1b[0m\\n\u0026#34; done 字体配置 推荐字体：\nJetBrains Mono：JetBrains 出品的编程字体 Fira Code：支持连字的编程字体 Source Code Pro：Adobe 出品的编程字体 终端多路复用 # 在 tmux 中使用不同目录 tmux new-window -c ~/projects # 在指定目录创建窗口 远程开发环境 本地开发 vs 远程开发 方式 优点 缺点 本地开发 响应快、调试方便 环境配置繁琐、资源受限 远程开发 环境统一、资源充足 网络延迟、需要 SSH 远程开发工具 VS Code Remote Development # 安装 Remote-SSH 扩展 # 在 VS Code 中按 F1，输入 \u0026#34;Remote-SSH: Connect to Host\u0026#34; # 选择配置文件或输入 ssh 命令 SSHFS（远程文件系统挂载） sudo apt install sshfs # 安装 sshfs user@hostname:/remote/path /local/mountpoint # 挂载 umount /local/mountpoint # 卸载 rsync（文件同步） rsync -avz local/ user@hostname:/remote/ # 同步到远程 rsync -avz user@hostname:/remote/ local/ # 同步到本地 rsync -avz --delete local/ user@hostname:/remote/ # 同步并删除多余文件 实用技巧 快速导航 cd - # 切换到上一个目录 cd ~/projects # 使用波浪号 cd / # 切换到根目录 cd .. # 切换到上级目录 cd ../.. # 切换到上两级目录 pushd /path/to/dir # 入栈并切换 pushd +1 # 切换到栈中第 1 个目录 popd # 出栈并切换 dirs # 查看目录栈 dirs -v # 查看目录栈（带编号） 命令补全 ls docu\u0026lt;Tab\u0026gt; # 自动补全 git che\u0026lt;Tab\u0026gt; # 自动补全命令 cd /usr/loc\u0026lt;Tab\u0026gt; # 自动补全路径 \u0026lt;Tab\u0026gt;\u0026lt;Tab\u0026gt; # 显示所有可能的补全 # 命令行参数补全 ssh \u0026lt;Tab\u0026gt; # 补全 SSH 配置 kill \u0026lt;Tab\u0026gt; # 补全进程名 # 自定义补全（Bash） complete -W \u0026#34;start stop restart status\u0026#34; myservice 命令编辑 Ctrl + A # 移动到命令行开头 Ctrl + E # 移动到命令行结尾 Ctrl + U # 删除光标前的所有字符 Ctrl + K # 删除光标后的所有字符 Ctrl + W # 删除光标前的一个单词 Ctrl + Y # 粘贴上次删除的内容 Ctrl + T # 交换光标前后两个字符 Alt + F # 向前移动一个单词 Alt + B # 向后移动一个单词 Alt + D # 删除光标后的一个单词 Alt + Backspace # 删除光标前的一个单词 Alt + C # 大写光标所在单词 Alt + U # 大写光标后所有字符 Alt + L # 小写光标后所有字符 Esc + T # 交换光标前后两个单词 Ctrl + X + Ctrl + E # 在编辑器中打开当前命令 命令行计算器 echo \u0026#34;2 + 3\u0026#34; | bc # 基本计算 echo \u0026#34;scale=2; 10 / 3\u0026#34; | bc # 设置精度 echo \u0026#34;sqrt(16)\u0026#34; | bc # 平方根 echo \u0026#34;10^2\u0026#34; | bc # 幂运算 echo \u0026#34;s(0.5)\u0026#34; | bc -l # 正弦函数 python3 -c \u0026#34;print(2 ** 10)\u0026#34; # Python 计算 python3 -c \u0026#34;import math; print(math.pi)\u0026#34; node -e \u0026#34;console.log(2 + 3)\u0026#34; # Node.js 计算 临时环境 # 在子 Shell 中测试配置 bash # 修改配置... exit # 退出子 Shell，回到原始环境 # 使用子 Shell 执行命令 (cd /tmp \u0026amp;\u0026amp; ls) # 不影响当前目录 # 使用 fakeroot 模拟 root 权限 fakeroot dpkg-deb --build package/ 命令计时 time command # 显示命令执行时间 time ls -la # 示例 # 详细计时 time -p command # POSIX 格式 /usr/bin/time command # 更详细的计时 # 多次执行取平均值 for i in {1..10}; do time command; done # 统计执行时间 echo \u0026#34;scale=3; $(date +%s.%N)\u0026#34; # 高精度时间戳 文件编码转换 iconv -f GBK -t UTF-8 input.txt \u0026gt; output.txt # GBK 转 UTF-8 iconv -l # 列出所有支持的编码 dos2unix file.txt # DOS 转 Unix unix2dos file.txt # Unix 转 DOS sed -i \u0026#39;s/\\r$//\u0026#39; file.txt # 手动去除换行符 tr -d \u0026#39;\\r\u0026#39; \u0026lt; input.txt \u0026gt; output.txt # 删除回车符 终端配色主题 安装主题：\n# 安装 Base16 主题 git clone https://github.com/chriskempson/base16-shell.git ~/.config/base16-shell # 安装 Solarized 主题 git clone https://github.com/altercation/solarized.git ~/.solarized 配置主题：\n# ~/.bashrc BASE16_SHELL=\u0026#34;$HOME/.config/base16-shell/\u0026#34; [ -n \u0026#34;$PS1\u0026#34; ] \u0026amp;\u0026amp; \\ source \u0026#34;$BASE16_SHELL/profile_helper.sh\u0026#34; # 选择主题 base16_ocean 终端配色测试：\n# 测试 256 色 for i in {0..255}; do printf \u0026#34;\\x1b[38;5;${i}m%3d \u0026#34; \u0026#34;${i}\u0026#34; ((i + 1) % 16 == 0) \u0026amp;\u0026amp; printf \u0026#34;\\n\u0026#34; done # 测试真彩色 awk \u0026#39;BEGIN{ s=\u0026#34;/\\\\/\\\\/\\\\/\\\\/\\\\\u0026#34;; s=s s s s s s s s; for (colnum = 0; colnum\u0026lt;77; colnum++) { r = 255-(colnum*255/76); g = (colnum*510/76); b = (colnum*255/76); if (g\u0026gt;255) g = 510-g; printf \u0026#34;\\033[48;2;%d;%d;%dm\u0026#34;, r,g,b; printf \u0026#34;\\033[38;2;%d;%d;%dm\u0026#34;, 255-r,255-g,255-b; printf \u0026#34;%s\\033[0m\u0026#34;, substr(s,colnum+1,1); } printf \u0026#34;\\n\u0026#34;; }\u0026#39; 终端性能优化 # 禁用不必要的 Bash 特性 shopt -u autocd # 禁用自动 cd shopt -u cdspell # 禁用 cd 拼写检查 # 优化 ls 命令 alias ls=\u0026#39;ls --color=auto -F\u0026#39; # 减少输出信息 # 使用 faster alternatives command -v exa \u0026amp;\u0026amp; alias ls=\u0026#39;exa\u0026#39; command -v bat \u0026amp;\u0026amp; alias cat=\u0026#39;bat\u0026#39; command -v fd \u0026amp;\u0026amp; alias find=\u0026#39;fd\u0026#39; command -v rg \u0026amp;\u0026amp; alias grep=\u0026#39;rg\u0026#39; # 禁用 Bash 历史同步（如果不需要） # unset PROMPT_COMMAND 命令行安全 # 隐藏敏感命令（以空格开头） rm -rf / # 这条命令不会被保存到历史 # 使用密钥而非密码 ssh-keygen -t ed25519 ssh-copy-id user@hostname # 加密敏感文件 gpg -c secret.txt # 加密 gpg -d secret.txt.gpg # 解密 # 使用一次性密码 ssh user@hostname -o PreferredAuthentications=keyboard-interactive 自动化技巧 # 使用 expect 自动化交互 expect \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; spawn ssh user@hostname expect \u0026#34;password:\u0026#34; send \u0026#34;mypassword\\r\u0026#34; interact EOF # 使用 yes 命令自动回答 yes | apt install package # 使用 timeout 限制命令执行时间 timeout 10 long-running-command # 使用 watch 定期执行命令 watch -n 1 \u0026#34;date \u0026amp;\u0026amp; uptime\u0026#34; Shell 脚本基础 脚本结构 #!/bin/bash # 脚本描述 # Usage: script.sh [options] set -e # 遇到错误立即退出 set -u # 禁止使用未定义变量 set -o pipefail # 管道中任何命令失败都视为失败 # 脚本内容 echo \u0026#34;Hello, World!\u0026#34; 变量 # 定义变量 name=\u0026#34;Edward\u0026#34; age=25 # 使用变量 echo \u0026#34;My name is $name\u0026#34; echo \u0026#34;I am $age years old\u0026#34; # 命令替换 today=$(date +%Y-%m-%d) echo \u0026#34;Today is $today\u0026#34; # 数组 fruits=(\u0026#34;apple\u0026#34; \u0026#34;banana\u0026#34; \u0026#34;cherry\u0026#34;) echo ${fruits[0]} # 访问数组元素 echo ${#fruits[@]} # 数组长度 echo ${fruits[@]} # 所有元素 条件判断 # if 语句 if [ -f \u0026#34;file.txt\u0026#34; ]; then echo \u0026#34;文件存在\u0026#34; elif [ -d \u0026#34;dir\u0026#34; ]; then echo \u0026#34;目录存在\u0026#34; else echo \u0026#34;都不存在\u0026#34; fi # 字符串比较 if [ \u0026#34;$name\u0026#34; = \u0026#34;Edward\u0026#34; ]; then echo \u0026#34;Hello Edward\u0026#34; fi # 数字比较 if [ $age -gt 18 ]; then echo \u0026#34;成年人\u0026#34; fi # 逻辑运算 if [ -f \u0026#34;file.txt\u0026#34; ] \u0026amp;\u0026amp; [ -r \u0026#34;file.txt\u0026#34; ]; then echo \u0026#34;文件存在且可读\u0026#34; fi # 模式匹配 if [[ \u0026#34;$name\u0026#34; == E* ]]; then echo \u0026#34;名字以 E 开头\u0026#34; fi 循环 # for 循环 for i in {1..10}; do echo $i done # 遍历文件 for file in *.txt; do echo $file done # while 循环 count=1 while [ $count -le 10 ]; do echo $count count=$((count + 1)) done # until 循环 count=1 until [ $count -gt 10 ]; do echo $count count=$((count + 1)) done 函数 # 定义函数 greet() { echo \u0026#34;Hello, $1!\u0026#34; } # 调用函数 greet \u0026#34;Edward\u0026#34; # 返回值 add() { echo $(( $1 + $2 )) } result=$(add 3 5) echo \u0026#34;Result: $result\u0026#34; 参数处理 # 访问参数 echo \u0026#34;脚本名: $0\u0026#34; echo \u0026#34;参数1: $1\u0026#34; echo \u0026#34;参数2: $2\u0026#34; echo \u0026#34;所有参数: $@\u0026#34; echo \u0026#34;参数个数: $#\u0026#34; # 处理选项 while [[ \u0026#34;$#\u0026#34; -gt 0 ]]; do case $1 in -h|--help) echo \u0026#34;帮助信息\u0026#34;; exit ;; -v|--verbose) verbose=true ;; -o|--output) output=\u0026#34;$2\u0026#34;; shift ;; *) echo \u0026#34;未知选项: $1\u0026#34;; exit 1 ;; esac shift done 脚本调试 # 启用调试模式 set -x # 打印每条执行的命令 # 使用调试参数运行 bash -x script.sh # 条件调试 if [ \u0026#34;$DEBUG\u0026#34; = \u0026#34;true\u0026#34; ]; then set -x fi 实用脚本示例 #!/bin/bash # 文件备份脚本 SOURCE_DIR=\u0026#34;$1\u0026#34; DEST_DIR=\u0026#34;$2\u0026#34; if [ -z \u0026#34;$SOURCE_DIR\u0026#34; ] || [ -z \u0026#34;$DEST_DIR\u0026#34; ]; then echo \u0026#34;Usage: $0 \u0026lt;source_dir\u0026gt; \u0026lt;dest_dir\u0026gt;\u0026#34; exit 1 fi mkdir -p \u0026#34;$DEST_DIR\u0026#34; TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE=\u0026#34;$DEST_DIR/backup_$TIMESTAMP.tar.gz\u0026#34; echo \u0026#34;Starting backup...\u0026#34; tar -czf \u0026#34;$BACKUP_FILE\u0026#34; \u0026#34;$SOURCE_DIR\u0026#34; if [ $? -eq 0 ]; then echo \u0026#34;Backup completed: $BACKUP_FILE\u0026#34; else echo \u0026#34;Backup failed!\u0026#34; exit 1 fi 终端多任务管理 使用 tmux 管理开发环境 # 创建开发会话 tmux new-session -d -s dev -n editor tmux send-keys -t dev \u0026#39;cd ~/projects/myapp \u0026amp;\u0026amp; vim .\u0026#39; C-m tmux new-window -t dev -n server tmux send-keys -t dev:1 \u0026#39;cd ~/projects/myapp \u0026amp;\u0026amp; npm run dev\u0026#39; C-m tmux new-window -t dev -n logs tmux send-keys -t dev:2 \u0026#39;cd ~/projects/myapp \u0026amp;\u0026amp; tail -f logs/app.log\u0026#39; C-m tmux attach -t dev 使用 screen 管理后台任务 # 创建后台会话 screen -dmS backup screen -S backup -X stuff \u0026#39;./backup.sh\\n\u0026#39; # 查看会话 screen -ls # 恢复会话 screen -r backup 使用 systemd 管理服务 # 创建服务文件 sudo vim /etc/systemd/system/myapp.service [Unit] Description=My Application After=network.target [Service] Type=simple User=edward WorkingDirectory=/home/edward/app ExecStart=/home/edward/app/start.sh Restart=always RestartSec=5 [Install] WantedBy=multi-user.target # 启用服务 sudo systemctl daemon-reload sudo systemctl enable myapp sudo systemctl start myapp # 查看状态 sudo systemctl status myapp sudo journalctl -u myapp -f 性能优化 Shell 启动优化 # 检查启动时间 time bash -c \u0026#39;exit\u0026#39; # 分析启动过程 bash -x -l -c \u0026#39;exit\u0026#39; 2\u0026gt;\u0026amp;1 | head -50 # 延迟加载配置 # ~/.bashrc load_lazy() { case \u0026#34;$1\u0026#34; in git) source ~/.bash_git ;; python) source ~/.bash_python ;; esac } # 使用 zsh 替代 bash（更快的启动速度） 命令执行优化 # 使用 faster alternatives command -v exa \u0026amp;\u0026amp; alias ls=\u0026#39;exa\u0026#39; command -v bat \u0026amp;\u0026amp; alias cat=\u0026#39;bat\u0026#39; command -v fd \u0026amp;\u0026amp; alias find=\u0026#39;fd\u0026#39; command -v rg \u0026amp;\u0026amp; alias grep=\u0026#39;rg\u0026#39; # 禁用不必要的特性 shopt -u autocd shopt -u cdspell # 使用管道替代临时文件 grep \u0026#34;pattern\u0026#34; file.txt | sort | uniq # 使用进程替换 diff \u0026lt;(sort file1.txt) \u0026lt;(sort file2.txt) 内存优化 # 查看内存使用 free -h top -o %MEM # 清理缓存 sudo sync \u0026amp;\u0026amp; sudo echo 3 \u0026gt; /proc/sys/vm/drop_caches # 限制进程内存 ulimit -v 1024000 # 限制为 1GB # 使用临时文件系统 mkdir -p /tmp/cache mount -t tmpfs -o size=1G tmpfs /tmp/cache 练习 基础练习 修改 ~/.bashrc 添加自定义别名和函数 设置自定义命令提示符，显示用户名、主机名和当前目录 使用 tmux 创建多个窗口和窗格 配置 SSH config 文件连接到远程服务器 进阶练习 创建一个函数，自动创建项目目录结构 使用 fzf 实现快速文件查找和打开 设置 Git 分支信息在命令提示符中显示 配置 SSH 端口转发访问远程服务 编写一个备份脚本，自动备份指定目录到远程服务器 综合练习 使用 tmux 管理多个开发会话，包含编辑器、服务器、日志窗口 配置远程开发环境，实现代码同步和自动部署 编写完整的 Shell 脚本，实现自动化部署流程 配置终端配色主题和快捷键，打造个性化开发环境 总结 配置文件 文件 用途 ~/.bashrc Bash 交互式配置 ~/.bash_profile Bash 登录配置 ~/.zshrc Zsh 配置 ~/.ssh/config SSH 配置 ~/.tmux.conf tmux 配置 环境变量 变量 用途 PATH 命令搜索路径 EDITOR 默认编辑器 PS1 命令提示符 HISTCONTROL 历史记录控制 快捷键 快捷键 功能 Ctrl + R 反向搜索历史 Ctrl + Z 暂停任务 Ctrl + D 退出 Shell Ctrl + L 清屏 终端复用器 工具 特点 tmux 现代、功能强大、活跃维护 screen 传统、兼容性好 SSH 高级特性 特性 说明 Agent Forwarding 在远程服务器上使用本地密钥 ControlMaster 连接复用，提升连接速度 Port Forwarding 端口转发，访问内网服务 ProxyJump 跳板机配置 通过这一课的学习，你已经掌握了命令行环境的高级配置技巧。合理配置命令行环境可以极大提升工作效率，让你成为真正的命令行高手！\n","permalink":"https://youdward.com/posts/%E8%AE%A1%E7%AE%97%E6%9C%BA%E6%95%99%E8%82%B2%E7%BC%BA%E5%A4%B1%E7%9A%84%E4%B8%80%E8%AF%BE2.%E5%91%BD%E4%BB%A4%E8%A1%8C%E7%8E%AF%E5%A2%83/","summary":"\u003ch2 id=\"命令行环境概述\"\u003e命令行环境概述\u003c/h2\u003e\n\u003cp\u003e命令行环境不仅仅是输入命令的地方，它是一个强大的工作空间。通过合理配置命令行环境，可以极大提升工作效率。\u003c/p\u003e\n\u003ch3 id=\"什么是命令行环境\"\u003e什么是命令行环境\u003c/h3\u003e\n\u003cp\u003e命令行环境包括：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eShell 配置文件\u003c/strong\u003e：定义环境的行为和外观\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e环境变量\u003c/strong\u003e：存储系统级别的配置信息\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e别名和函数\u003c/strong\u003e：自定义快捷命令\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e命令历史\u003c/strong\u003e：记录之前执行的命令\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e作业控制\u003c/strong\u003e：管理前台和后台进程\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e终端复用器\u003c/strong\u003e：在单个终端中管理多个会话\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch2 id=\"shell-配置文件\"\u003eShell 配置文件\u003c/h2\u003e\n\u003ch3 id=\"bash-配置文件加载顺序详解\"\u003eBash 配置文件加载顺序详解\u003c/h3\u003e\n\u003cp\u003e当 Bash 启动时，会按照以下顺序查找并加载配置文件：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/etc/profile                    \u003cspan style=\"color:#75715e\"\u003e# 系统级配置（所有用户共享）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ↓\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e~/.bash_profile                 \u003cspan style=\"color:#75715e\"\u003e# 用户登录配置（优先）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ↓ 如果不存在\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e~/.bash_login                   \u003cspan style=\"color:#75715e\"\u003e# 备用登录配置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ↓ 如果不存在\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e~/.profile                      \u003cspan style=\"color:#75715e\"\u003e# 备用登录配置（适用于多种 Shell）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ↓\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e~/.bashrc                       \u003cspan style=\"color:#75715e\"\u003e# 交互式非登录 Shell 配置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e    ↓\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e/etc/bash.bashrc                \u003cspan style=\"color:#75715e\"\u003e# 系统级交互式配置\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e加载规则：\u003c/strong\u003e\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e登录 Shell\u003c/strong\u003e（如 \u003ccode\u003essh user@host\u003c/code\u003e、\u003ccode\u003elogin\u003c/code\u003e 命令）：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e首先执行 \u003ccode\u003e/etc/profile\u003c/code\u003e（系统级）\u003c/li\u003e\n\u003cli\u003e然后依次查找 \u003ccode\u003e~/.bash_profile\u003c/code\u003e → \u003ccode\u003e~/.bash_login\u003c/code\u003e → \u003ccode\u003e~/.profile\u003c/code\u003e，只执行第一个找到的\u003c/li\u003e\n\u003cli\u003e通常会在登录配置中通过 \u003ccode\u003esource ~/.bashrc\u003c/code\u003e 加载交互式配置\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e非登录 Shell\u003c/strong\u003e（如终端窗口、直接输入 \u003ccode\u003ebash\u003c/code\u003e）：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e只执行 \u003ccode\u003e~/.bashrc\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e\u003cstrong\u003e脚本执行\u003c/strong\u003e（如 \u003ccode\u003ebash script.sh\u003c/code\u003e）：\u003c/p\u003e","title":"计算机教育缺失的一课：2.命令行环境"},{"content":"为什么选择 Hugo Hugo 是目前世界上最快的静态网站生成器，相比其他工具具有显著优势：\nHugo 的核心优势 特性 说明 速度 构建速度极快，毫秒级完成 易用 无需安装复杂的依赖环境 Markdown 使用 Markdown 编写内容 主题丰富 上百款精美主题可选 完全免费 开源项目，无任何费用 与其他框架对比 构建速度对比（1000篇文章）： - Hugo: ~1秒 ⚡⚡⚡⚡⚡ - Hexo: ~30秒 ⚡⚡ - Jekyll: ~60秒 ⚡ - Gatsby: ~120秒 为什么选择 PaperMod 主题 PaperMod 是一款备受好评的 Hugo 主题，特别适合技术博客：\n主要特点 简洁美观：界面清爽，阅读体验极佳 响应式设计：完美适配手机、平板、电脑 暗色模式：支持自动切换明暗主题 SEO 友好：对搜索引擎优化友好 性能优异：加载速度快，轻量级 功能丰富：支持代码高亮、目录、标签、分类等 主题演示 # 主题配置文件示例 params: defaultTheme: auto # 自动切换明暗主题 author: 你的名字 description: 博客描述 本地快速搭建博客 本节详细介绍在 Windows 系统上从零搭建 Hugo 博客的完整步骤。\n准备工作 1. 安装 Git Git 是版本管理工具，Hugo 主题管理依赖它。\n下载地址： https://git-scm.com/download/win\n安装后验证：\ngit --version 应该看到类似输出：\ngit version 2.54.0.windows.1 2. 安装 Hugo（扩展版） Hugo 扩展版支持 Sass/SCSS 编译，PaperMod 主题需要此功能。\n方法一：使用 Winget（推荐）\n# 打开 PowerShell 或命令提示符，运行： winget install Hugo.Hugo.Extended 方法二：使用 Chocolatey\nchoco install hugo-extended 方法三：手动安装\n访问 https://github.com/gohugoio/hugo/releases 下载 hugo_extended_xxx_windows-amd64.zip 解压到 D:\\Hugo\\bin\\ 将 D:\\Hugo\\bin\\ 添加到系统环境变量 右键「此电脑」→ 属性 → 高级系统设置 → 环境变量 编辑 Path → 新建 → 输入 D:\\Hugo\\bin\\ 安装后验证：\nhugo version 应该看到类似输出：\nhugo v0.163.3-4d22555aebf458d5d150500c9ac4bee5b24cf0d3+extended windows/amd64 BuildDate=2026-06-18T16:18:24Z VendorInfo=gohugoio 第一步：创建博客项目 1.1 打开命令行工具\n打开 PowerShell 或 Git Bash，进入你想存放博客的目录：\n# 例如进入 D 盘的 Project 文件夹 cd D:\\Project 1.2 创建新网站\nhugo new site myblog myblog 是项目名称，可以改成你喜欢的名字\n1.3 进入项目目录\ncd myblog 1.4 查看项目结构\nmyblog/ ├── content/ # 博客文章目录 ├── layouts/ # 模板布局目录 ├── public/ # 构建输出目录（最终发布的网站） ├── static/ # 静态文件目录（图片、CSS、JS 等） ├── themes/ # 主题目录 ├── hugo.yaml # 配置文件（主配置） └── config.toml # 配置文件（可选，Toml 格式） 第二步：添加主题 Hugo 主题需要通过 Git 子模块方式安装，这样便于后续更新。\n2.1 初始化 Git 仓库\ngit init 2.2 添加 PaperMod 主题\ngit submodule add https://github.com/adityatelange/hugo-PaperMod.git themes/PaperMod 这一步会从 GitHub 下载主题到 themes/PaperMod 目录\n2.3 启用主题\n编辑 hugo.yaml，添加一行：\nbaseURL: \u0026#34;https://example.com/\u0026#34; languageCode: \u0026#34;zh-cn\u0026#34; title: \u0026#34;我的博客\u0026#34; theme: \u0026#34;PaperMod\u0026#34; 第三步：配置博客 PaperMod 主题推荐使用 hugo.yaml 作为配置文件。以下是推荐的基础配置：\nbaseURL: \u0026#34;https://你的用户名.github.io/\u0026#34; languageCode: \u0026#34;zh-cn\u0026#34; title: \u0026#34;我的技术博客\u0026#34; theme: \u0026#34;PaperMod\u0026#34; defaultContentLanguage: zh hasCJKLanguage: true params: author: \u0026#34;你的名字\u0026#34; description: \u0026#34;分享技术，记录生活\u0026#34; defaultTheme: auto # 自动切换明暗主题 ShowShareButtons: true # 显示分享按钮 ShowReadingTime: true # 显示阅读时间 ShowWordCount: true # 显示字数统计 displayName: \u0026#34;博客\u0026#34; founder: \u0026#34;你的名字\u0026#34; websiteTitle: \u0026#34; - 我的技术博客\u0026#34; menu: main: - name: 文章 url: /posts/ weight: 1 - name: 标签 url: /tags/ weight: 2 - name: 分类 url: /categories/ weight: 3 - name: 关于 url: /about/ weight: 4 第四步：创建文章 4.1 创建第一篇文章\nhugo new posts/hello-world.md 这会在 content/posts/ 目录下创建 hello-world.md 文件\n4.2 编辑文章\n用任意文本编辑器打开 content/posts/hello-world.md，你会看到：\n--- title: \u0026#34;Hello World\u0026#34; date: 2026-06-20 draft: false --- 在这里写下你的文章内容... Front Matter 配置说明：\n字段 说明 示例 title 文章标题 \u0026ldquo;Hello World\u0026rdquo; date 发布日期 2026-06-20 draft 是否草稿 false=发布，true=草稿 description 文章描述 用于 SEO 显示 tags 标签 [\u0026ldquo;技术\u0026rdquo;, \u0026ldquo;博客\u0026rdquo;] categories 分类 [\u0026ldquo;随笔\u0026rdquo;] 4.3 编写文章内容\n--- title: \u0026#34;Hello World\u0026#34; date: 2026-06-20 draft: false description: \u0026#34;我的第一篇博客文章\u0026#34; tags: [\u0026#34;博客\u0026#34;, \u0026#34;Hugo\u0026#34;] categories: [\u0026#34;随笔\u0026#34;] --- ## 文章标题 这是正文内容，支持 **Markdown** 语法。 ### 代码示例 ```python print(\u0026#34;Hello, Hugo!\u0026#34;) ``` 第五步：本地预览 Hugo 提供实时预览功能，修改内容后浏览器自动刷新。\n5.1 启动预览服务器\nhugo server -D -D 参数表示显示草稿文章（draft: true 的文章也会显示）\n5.2 访问预览\n打开浏览器，访问：http://localhost:1313\n5.3 实时预览说明\n命令 说明 hugo server 普通预览 hugo server -D 显示草稿预览 hugo server -p 1313 指定端口预览 修改文章保存后，浏览器会自动刷新显示最新内容。\n第六步：本地构建 当文章写好后，需要构建生成静态文件才能发布。\n6.1 执行构建\nhugo --minify --minify 参数会压缩 HTML、CSS、JS，减小文件体积\n6.2 查看构建结果\n构建完成后，public/ 目录就是最终要发布的内容：\npublic/ ├── index.html # 首页 ├── posts/ # 文章页面 │ ├── hello-world/ │ │ └── index.html ├── css/ # 样式文件 ├── js/ # 脚本文件 └── ... 第七步：常见问题排查 问题 解决方法 hugo: command not found Hugo 未安装或未添加到 PATH，重启终端试试 页面空白/无样式 确认 hugo.yaml 中 theme: \u0026quot;PaperMod\u0026quot; 已设置 主题样式不对 检查 themes/PaperMod 目录是否存在 文章不显示 确认 draft: false，或使用 hugo server -D 预览 下一步：部署上线 本地测试满意后，下一步是将网站部署到互联网。\n推荐方案：\nGitHub Pages：免费，配合 GitHub Actions 自动部署 Vercel：免费，国内访问速度快 Netlify：免费，稳定可靠 详情请参考本文档的「部署到 GitHub Pages」章节。\n常用命令 命令 说明 hugo new site blog 创建新网站 hugo new posts/xxx.md 创建新文章 hugo server 本地预览 hugo --minify 构建压缩版 hugo -D 显示草稿 部署到 GitHub Pages GitHub Pages 是 GitHub 提供的免费静态网站托管服务，配合 GitHub Actions 可以实现代码推送后自动部署。\n第一步：创建 GitHub 仓库 1.1 登录 GitHub\n访问 https://github.com 并登录你的账号。\n1.2 创建新仓库\n点击右上角「+」→「New repository」 填写仓库信息： Repository name：你的用户名.github.io 例如你的 GitHub 用户名是 john，就填写 john.github.io\nDescription：我的技术博客（可选） Visibility：选择 Public（公开） 勾选「Add a README file」 点击「Create repository」 1.3 仓库地址\n创建成功后，页面会显示仓库地址，例如：\nhttps://github.com/youdward/youdward.github.io 第二步：关联本地仓库 2.1 添加远程仓库地址\n在项目根目录执行：\n# 替换为你的实际 GitHub 用户名和仓库名 git remote add origin https://github.com/youdward/youdward.github.io.git 2.2 验证远程仓库\ngit remote -v 应该看到：\norigin https://github.com/youdward/youdward.github.io.git (fetch) origin https://github.com/youdward/youdward.github.io.git (push) 第三步：创建 GitHub Actions 配置 GitHub Actions 是 GitHub 的自动化工具，可以在你推送代码时自动构建和部署网站。\n3.1 创建工作流目录\nmkdir -p .github/workflows 3.2 创建部署配置文件\n在 .github/workflows/gh-pages.yml 创建文件，内容如下：\nname: Deploy Hugo site to Pages on: push: branches: - main workflow_dispatch: permissions: contents: read pages: write id-token: write concurrency: group: pages cancel-in-progress: false defaults: run: shell: bash jobs: build: runs-on: ubuntu-latest env: DART_SASS_VERSION: 1.100.0 GO_VERSION: 1.26.3 HUGO_VERSION: 0.163.3 NODE_VERSION: 24.16.0 steps: - name: Checkout uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - name: Setup Go uses: actions/setup-go@v6 with: go-version: ${{ env.GO_VERSION }} cache: false - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} - name: Setup Pages id: pages uses: actions/configure-pages@v6 - name: Install Dart Sass run: | curl -sLJO \u0026#34;https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz\u0026#34; tar -C \u0026#34;${HOME}/.local\u0026#34; -xf \u0026#34;dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz\u0026#34; rm \u0026#34;dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz\u0026#34; echo \u0026#34;${HOME}/.local/dart-sass\u0026#34; \u0026gt;\u0026gt; \u0026#34;${GITHUB_PATH}\u0026#34; - name: Install Hugo run: | curl -sLJO \u0026#34;https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz\u0026#34; mkdir \u0026#34;${HOME}/.local/hugo\u0026#34; tar -C \u0026#34;${HOME}/.local/hugo\u0026#34; -xf \u0026#34;hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz\u0026#34; rm \u0026#34;hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz\u0026#34; echo \u0026#34;${HOME}/.local/hugo\u0026#34; \u0026gt;\u0026gt; \u0026#34;${GITHUB_PATH}\u0026#34; - name: Verify installations run: | echo \u0026#34;Dart Sass: $(sass --version)\u0026#34; echo \u0026#34;Go: $(go version)\u0026#34; echo \u0026#34;Hugo: $(hugo version)\u0026#34; echo \u0026#34;Node.js: $(node --version)\u0026#34; - name: Build the site run: | hugo \\ --gc \\ --minify \\ --baseURL \u0026#34;${{ steps.pages.outputs.base_url }}/\u0026#34; - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: path: ./public deploy: environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest needs: build steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 第四步：提交并推送代码 4.1 创建 .gitignore 文件\n创建 .gitignore 文件，避免提交不需要的文件：\n# Hugo 构建输出 /public/ /.hugo_build.lock /resources/ # 操作系统文件 .DS_Store Thumbs.db # IDE 配置 .idea/ .vscode/ *.swp 4.2 提交代码\ngit add . git commit -m \u0026#34;Initial commit: Hugo blog with PaperMod theme\u0026#34; 4.3 推送到 GitHub\ngit branch -M main git push -u origin main -u origin main 会设置上游分支，以后只需 git push 即可。\n第五步：启用 GitHub Pages 5.1 等待 Actions 执行完成\n推送后，打开 GitHub 仓库页面，点击「Actions」标签查看构建状态。\n5.2 启用 Pages\n进入仓库 → Settings → Pages 在「Build and deployment」部分： Source 选择：GitHub Actions 不需要选择分支（因为我们用的是 Actions 方式） 5.3 等待部署\n部署通常需要 1-3 分钟。完成后会显示博客访问地址。\n第六步：访问你的博客 部署成功后，访问：https://你的用户名.github.io/\n例如：https://youdward.github.io/\n常见问题排查 问题 解决方法 Actions 显示红色 ❌ 点击查看日志，找出错误原因 Pages 一直显示「Pending」 等待几分钟，或检查 Actions 是否成功 页面显示 404 确认 baseURL 配置正确 样式丢失 确认 baseURL 以 / 结尾 构建成功但部署失败 检查 Pages 设置中的 Source 是否选择 GitHub Actions 后续更新博客 以后写完文章，只需：\n# 1. 提交代码 git add . git commit -m \u0026#34;更新文章\u0026#34; git push # 2. GitHub Actions 会自动构建和部署 # 3. 几分钟后博客就更新了 不需要手动构建或上传，自动化完成！\n使用云服务器和自定义域名部署 如果你已经购买了云服务器和域名，可以按照以下步骤在服务器上配置和搭建博客。\n前提条件：\n已购买云服务器（推荐 Ubuntu 22.04 系统） 已购买域名并完成 ICP 备案（国内服务器需要） 已将域名 A 记录解析到服务器 IP 第一步：连接服务器 1.1 获取服务器信息\n记录以下信息：\n公网 IP：如 123.45.67.89 登录用户名：通常是 root 登录密码：购买时设置的密码 1.2 SSH 连接服务器\n# Windows 使用 PowerShell 或 SSH 客户端 ssh root@123.45.67.89 # 输入密码登录 第二步：服务器环境配置 2.1 更新系统\n# Ubuntu/Debian apt update \u0026amp;\u0026amp; apt upgrade -y # CentOS yum update -y 2.2 安装 Nginx\n# Ubuntu/Debian apt install nginx -y # CentOS yum install nginx -y # 启动 Nginx systemctl start nginx systemctl enable nginx 2.3 验证 Nginx\n浏览器访问 http://你的服务器IP，看到 Nginx 欢迎页说明安装成功。\n2.4 安装 Git 和 Hugo\n# 安装 Git apt install git -y # 安装 Hugo（Ubuntu） apt install hugo -y # 或手动安装最新版 wget https://github.com/gohugoio/hugo/releases/download/v0.163.3/hugo_extended_0.163.3_linux-amd64.tar.gz tar -xzf hugo_extended_0.163.3_linux-amd64.tar.gz mv hugo /usr/local/bin/ # 验证安装 hugo version 第三步：上传博客文件 方法一：本地构建后上传（推荐）\n# 1. 本地构建 hugo --minify # 2. 使用 SCP 上传 public 目录到服务器 scp -r public/* root@123.45.67.89:/var/www/html/ 方法二：服务器上 Git 拉取并构建\n# 在服务器上执行 cd /var/www git clone https://github.com/yourname/yourname.github.io.git blog cd blog hugo --minify cp -r public/* /var/www/html/ 第四步：配置 Nginx 4.1 创建网站配置\nnano /etc/nginx/sites-available/yourdomain.com 写入以下内容：\nserver { listen 80; server_name yourdomain.com www.yourdomain.com; root /var/www/html; index index.html; location / { try_files $uri $uri/ =404; } # 启用 Gzip 压缩 gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml; # 静态资源缓存 location ~* \\.(jpg|jpeg|png|gif|ico|css|js)$ { expires 30d; } } 4.2 启用配置\n# 创建软链接 ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/ # 删除默认配置（可选） rm /etc/nginx/sites-enabled/default # 测试配置 nginx -t # 重载 Nginx systemctl reload nginx 第五步：配置 HTTPS（SSL 证书） 5.1 安装 Certbot\n# Ubuntu apt install certbot python3-certbot-nginx -y # CentOS yum install certbot python3-certbot-nginx -y 5.2 申请免费 SSL 证书\ncertbot --nginx -d yourdomain.com -d www.yourdomain.com 按提示输入邮箱，同意条款即可。Certbot 会自动修改 Nginx 配置。\n5.3 设置自动续期\n# 测试续期 certbot renew --dry-run # 添加定时任务 crontab -e # 添加以下行（每天凌晨 3 点检查续期） 0 3 * * * certbot renew --quiet 5.4 验证 HTTPS\n访问 https://yourdomain.com，浏览器地址栏显示锁图标说明配置成功。\n第六步：配置自动部署（可选） 6.1 创建部署脚本\nnano /root/deploy-blog.sh 写入以下内容：\n#!/bin/bash cd /var/www/blog git pull origin main hugo --minify cp -r public/* /var/www/html/ echo \u0026#34;Blog deployed at $(date)\u0026#34; 6.2 设置执行权限\nchmod +x /root/deploy-blog.sh 6.3 后续更新博客\n每次更新文章后，在服务器执行：\n/root/deploy-blog.sh 或者配置 GitHub Webhooks 实现推送后自动触发部署。\nGitHub Pages vs 云服务器对比 对比项 GitHub Pages 云服务器 费用 免费 150-360 元/年 配置难度 简单 中等 访问速度 国内较慢 国内快 自定义域名 支持 支持 HTTPS 自动配置 需手动配置 自由度 有限制 完全自由 适合人群 初学者、个人博客 进阶用户、企业网站 最终效果展示 部署完成后，你的博客就可以在互联网上访问了！\n🎉 博客已成功部署\n访问我的博客 → https://youdward.github.io/\n访问自定义域名 → https://youdward.com\n开始写作 现在你可以开始写博客了！推荐的内容方向：\n技术笔记：编程语言、框架、工具使用心得 项目经验：实战项目总结和问题复盘 知识体系：整理学习路线和知识框架 生活感悟：记录成长和思考 Markdown 写作示例 ## 二级标题 - 列表项 1 - 列表项 2 **加粗文字** 和 *斜体文字* [链接文本](https://example.com) ![图片描述](图片路径) 引用：输出的每一行代码，都是在成为更好的自己的路上。\n祝你博客之旅愉快！ 🎉\n","permalink":"https://youdward.com/posts/hugo-%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E5%8D%9A%E5%AE%A2/","summary":"\u003ch2 id=\"为什么选择-hugo\"\u003e为什么选择 Hugo\u003c/h2\u003e\n\u003cp\u003eHugo 是目前世界上最快的静态网站生成器，相比其他工具具有显著优势：\u003c/p\u003e\n\u003ch3 id=\"hugo-的核心优势\"\u003eHugo 的核心优势\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e特性\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e速度\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e构建速度极快，毫秒级完成\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e易用\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e无需安装复杂的依赖环境\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003eMarkdown\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e使用 Markdown 编写内容\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e主题丰富\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e上百款精美主题可选\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e完全免费\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e开源项目，无任何费用\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"与其他框架对比\"\u003e与其他框架对比\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e构建速度对比（1000篇文章）：\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- Hugo:    ~1秒 ⚡⚡⚡⚡⚡\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- Hexo:    ~30秒 ⚡⚡\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- Jekyll:  ~60秒 ⚡\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e- Gatsby:  ~120秒\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"为什么选择-papermod-主题\"\u003e为什么选择 PaperMod 主题\u003c/h2\u003e\n\u003cp\u003ePaperMod 是一款备受好评的 Hugo 主题，特别适合技术博客：\u003c/p\u003e\n\u003ch3 id=\"主要特点\"\u003e主要特点\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e简洁美观\u003c/strong\u003e：界面清爽，阅读体验极佳\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e响应式设计\u003c/strong\u003e：完美适配手机、平板、电脑\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e暗色模式\u003c/strong\u003e：支持自动切换明暗主题\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eSEO 友好\u003c/strong\u003e：对搜索引擎优化友好\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e性能优异\u003c/strong\u003e：加载速度快，轻量级\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e功能丰富\u003c/strong\u003e：支持代码高亮、目录、标签、分类等\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"主题演示\"\u003e主题演示\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-yaml\" data-lang=\"yaml\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# 主题配置文件示例\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003eparams\u003c/span\u003e:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003edefaultTheme\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003eauto \u003c/span\u003e \u003cspan style=\"color:#75715e\"\u003e# 自动切换明暗主题\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003eauthor\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e你的名字\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e  \u003cspan style=\"color:#f92672\"\u003edescription\u003c/span\u003e: \u003cspan style=\"color:#ae81ff\"\u003e博客描述\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch2 id=\"本地快速搭建博客\"\u003e本地快速搭建博客\u003c/h2\u003e\n\u003cp\u003e本节详细介绍在 Windows 系统上从零搭建 Hugo 博客的完整步骤。\u003c/p\u003e\n\u003chr\u003e\n\u003ch3 id=\"准备工作\"\u003e准备工作\u003c/h3\u003e\n\u003ch4 id=\"1-安装-git\"\u003e1. 安装 Git\u003c/h4\u003e\n\u003cp\u003eGit 是版本管理工具，Hugo 主题管理依赖它。\u003c/p\u003e","title":"Hugo 搭建个人博客"}]