ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Missing Semester 中文版:Shell 与脚本编程实战指南

Missing Semester 中文版:Shell 与脚本编程实战指南 文档教程教育【免费下载链接】missing-semester-cn.github.iothe CS missing semester Chinese version项目地址https://gitcode.com/gh_mirrors/mi/missing-semester-cn.github.io点击查看免费下载导读本文基于 Missing Semester 中文站《Shell and Scripting》讲义_2019/shell.md展开系统讲解如何把 shell 从敲命令的窗口升级为可编程的工作台你会掌握循环与条件分支、命令替换与通配符、管道组合与进程替换以及作业控制、信号与进程管理最终能用几行脚本完成过去需要手动重复上百次的操作。读完本文你将具备编写健壮 shell 脚本、组合命令行工具并管理后台进程的完整能力这也为后续课程_2020/shell-tools.md中的 shell 工具、_2020/command-line.md中的任务控制打下坚实基础。Shell 是什么一个高效、可编程的文本接口shell 是你与计算机之间最高效的文本接口。打开终端后首先看到的是 shell提示符prompt它代表shell 正在等待你的指令。最基本的用法是运行程序和命令cd切换目录ls列出文件与目录mv、cp移动和复制文件但 shell 的能力远不止于此——你可以调用计算机上的任何程序而且几乎所有操作都有对应的命令行工具它们往往比图形界面更高效、更适合自动化。关键点在于shell 同时是一门交互式编程语言脚本语言。市面上有众多 shell你大概率用过sh或bashPOSIX 系的通用选择与某种语言绑定的 shell如cshC shell宣称更好用的 shell如fish、zsh、ksh本讲义聚焦于应用最广泛的sh和bash在_2026/course-shell.md中同样以 bash 为主线并提到 fish、zsh 等改进型 shell。在_2020/course-shell.md中作者用一个精辟的类比解释了为什么要学它GUI、语音、AR/VR 等交互方式覆盖了 80% 的场景但你不能点击一个不存在的按钮也不能用语音输入一个还没有被录入的指令——只有回到文本接口你才能充分利用计算机的全部能力。Shell 编程是工具箱里非常实用的技能你可以直接在提示符下编写程序也可以把程序写进文件用#!/bin/sh声明解释器再通过chmod x赋予可执行权限。使用 shell 编程循环、命令替换与变量重复执行命令for循环想多次运行同一条命令直接写一个循环for i in $(seq 1 5); do echo hello; done这条命令包含大量值得拆解的知识点for x in list; do BODY; doneshell 没有花括号用dodone界定循环体。;用于终止一条命令等价于换行。列表切分shell 会把list按规则切分依次赋给变量x并执行循环体。这里的切分是按空白字符切分whitespace splitting后文会专门讨论这一陷阱。$(seq 1 5)命令替换先运行程序seq参数为1和5再把整个$()替换为该程序的输出。它等价于for i in 1 2 3 4 5echo hello脚本中一切都是命令。这里运行echo命令打印它的参数。shell 会到$PATH冒号分隔的目录列表中逐一搜索命令对应的程序。变量与特殊变量用赋值注意等号两边不能有空格用$变量名取值foobar echo $foo配合ls可以遍历当前目录的所有文件for f in $(ls); do echo $f; donebash 还内置了一组特殊变量变量含义$1到$9传给脚本的第 1 至第 9 个参数$0脚本自身的名字$#参数个数$$当前 shell 的进程 ID在_2020/shell-tools.md中这份特殊变量清单被进一步扩充$所有参数、$?上一条命令的返回值0 表示成功、!!完整的上一条命令常用于sudo !!、$_上一条命令的最后一个参数等。这些变量是脚本与外部世界通信的约定接口值得逐一实践。条件判断iftest/[只打印目录名for f in $(ls); do if test -d $f; then echo dir $f; fi; done这里的新语法if CONDITION; then BODY; fiCONDITION本身是一条命令如果它以退出状态 0成功返回就执行BODY还可以接else或elif。同样因为 shell 没有花括号用thenfi界定。test是另一个程序提供各种检查和比较条件为真时以 0 退出。遇到不认识的命令man COMMAND是你的好朋友例如man test。[是test的别名写法[ -d $f ]与test -d $f等价。不妨亲自查看man test以及which [你会惊讶于[居然也是一个真实存在的程序。在_2020/shell-tools.md中作者进一步建议尽量使用[[ ]]而不是[ ]——[[是 bash 内建的比较器拥有特殊解析规则支持代替-a、||代替-o等更现代的写法能显著降低出错概率代价是无法兼容sh。参数分割陷阱与引号shell 脚本 Bug 的最大来源But wait! This is wrong! What if a file is called My Documents?上面的脚本有一个致命缺陷。假如目录里有个文件叫My Documentsfor f in $(ls)会展开为for f in My Documents于是test先作用于My再作用于Documents完全不是我们想要的结果bash 按空白字符切分参数这并不总是我们想要的。这是 shell 脚本中最大的 Bug 来源。解决办法使用引号处理空格for f in My Documents就能正确工作。同样的问题无处不在test -d $f中如果$f含空白test会直接报错echo碰巧没事是因为它切分后按空格重新拼接但如果文件名里包含换行符也会被转成空格。规则对所有你不想被切分的变量使用都要加引号。那for f in $(ls)又会怎样——整个输出变成单个字符串同样不是我们想要的。通配符Globbing正确的答案bash 知道如何用模式查找文件*匹配任意字符串?匹配任意单个字符{a,b,c}匹配这些字符中的任意一个花括号展开用 glob 重写for f in *; do ... done # 当前目录下所有文件glob 匹配到的每个文件都会成为独立的参数天然规避了空格切分问题不过使用时仍需加引号test -d $f。还可以构造更高级的模式for f in a*当前目录下所有以a开头的文件for f in foo/*.txtfoo目录下所有.txt文件for f in foo/*/p??.txtfoo的各子目录下所有以p开头、三个字符的.txt文件在_2020/shell-tools.md中花括号展开还有更多实战形态例如convert image.{png,jpg}会展开为convert image.png image.jpgtouch {foo,bar}/{a..h}能一次创建 16 个文件mv *{.py,.sh} folder批量移动文件。空白问题不止于此[的空参数陷阱if [ $foo bar ]; then ...如果$foo是空字符串展开后[的参数就只剩和bar语法直接失效。虽然可以用[ x$foo xbar ]这种加前缀的老技巧绕过去但更干净的做法是改用[[——它由 bash 直接解析不会对变量做这种二次切分。组合性Composability管道、重定向与进程替换shell 之所以强大很大程度上源于可组合性与其写一个包办一切的程序不如把多个程序串联起来。核心字符是|管道a | b同时运行a和b把a的全部输出作为b的输入最终打印b的输出。三个标准流每个进程都有三个流STDIN程序读取输入的地方STDOUT程序打印输出的地方STDERR程序可选使用的第二个输出通道默认情况下STDIN是你的键盘STDOUT和STDERR都是你的终端——但你可以改变这一切a | b把a的STDOUT接到b的STDINa fooa的STDOUT写入文件fooa 2 fooa的STDERR写入文件fooa fooa的STDIN从文件foo读取提示tail -f会在文件被写入时实时打印它管道实战操作另一个程序的输出ls | grep foo列出名字包含foo的所有文件ps | grep foo列出命令行包含foo的所有进程journalctl | grep -i intel | tail -n5系统日志中最后 5 条包含 intel不区分大小写的消息who | sendmail -t meexample.com把当前登录用户列表发给指定邮箱这正是大量数据处理data wrangling工作的基础_2020/course-shell.md中给出了更完整的演示ls -l / | tail -n1取根目录最后一行curl --head --silent google.com | grep --ignore-case content-length | cut --delimiter -f2提取响应头中的 content-length。而在_2026/course-shell.md里一条ssh myserver journalctl ... | sed -E ... | sort | uniq -c | sort -nk1,1 | tail -n10 | awk ... | paste -sd,的长管道能从远程 SSH 日志中直接统计出最常登录的用户名——这正是组合威力的最好证明。更多组合方式命令分组(a; b) | tac——先运行a再运行b把两者全部输出交给tac逆序打印输入。进程替换process substitution一个冷门但极有用的特性。b (a)会运行a为它的输出流生成一个临时文件名并把该文件名作为参数传给b。例如diff (journalctl -b -1 | head -n20) (journalctl -b -2 | head -n20)这条命令对比上一次启动与上上次启动的日志前 20 行差异。进程替换的价值在于当命令需要以文件方式接收数据而不是从 STDIN 读时它提供了完美的桥接_2020/shell-tools.md中diff (ls foo) (ls bar)也是同样的用法。作业与进程控制后台任务与信号把程序放到后台后缀让程序在后台运行立即把提示符还给你——适合同时运行两个程序比如服务器和客户端server client。注意后台程序仍然把终端当作STDOUT所以更稳妥的写法是server server.log client把输出重定向到日志文件。作业管理命令jobs查看当前会话中的所有作业会显示 Running 状态fg %JOB把作业调回前台不带参数则操作最近的一个^Z即CtrlZbg暂停当前程序并放到后台。^Z会停止当前进程并使其成为作业bg让最近一个作业在后台继续运行等价于当初用启动disown切断后台作业与会话的连接否则退出登录时作业会一起终止nohup同样用于防止挂断——它是忽略SIGHUP信号的封装$!最近一个后台进程的 PID关于这条链路_2020/command-line.md给出了完整的信号原理与一段实战会话sleep 1000后用^Z挂起、nohup sleep 2000 启动防挂断后台任务、jobs查看状态、bg %1恢复、kill -STOP %1再次挂起、kill -SIGHUP触发挂断、最后kill %2终止。它还强调SIGKILL不能被捕获会立刻结束进程但可能留下孤儿进程等副作用。查看与操作其他进程ps列出运行中的进程。ps -A也写作ps ax打印所有用户的进程ps参数非常多详见man pspgrep按名字搜索进程类似ps -A | grep。pgrep -af搜索并显示完整命令行参数kill按 PID 向进程发送信号pkill则按名字搜索后发送常配合-f信号进程间通信的软件中断信号告诉进程去做某事常见的有信号写法等价按键行为SIGKILL-9或-KILL^\Ctrl\立即退出不可捕获SIGTERM-15或-TERM^CCtrlC优雅退出SIGINT由^C触发^C中断可被程序捕获SIGSTOP/SIGTSTP由^Z触发^Z暂停进程在_2020/command-line.md中有一个精彩的例子一段 Python 程序用signal.signal(signal.SIGINT, handler)捕获并忽略SIGINT此时^C无法停止它必须发送SIGQUIT^\才行——这解释了为什么有时Ctrl-C无法结束进程。信号是理解整个任务控制机制的钥匙。Flags命令行参数的通用约定大多数命令行工具通过**标记flags**接收参数。标记通常有短形式-h和长形式--help运行CMD -h或man CMD通常能获得完整的标记列表。短标记可以合并rm -r -f等价于rm -rf或rm -fr。一些事实上的通用约定标记常见含义-a包含所有文件含以.开头的隐藏文件-f强制如rm -f-h显示帮助-v开启详细输出verbose-V打印版本号双横线--的意义在内建命令和许多其他命令中--表示选项到此为止后面都是位置参数。如果你恰好有一个名为-v的文件grep pattern -- -v能正常工作而grep pattern -v会被解析成选项。同理可以用touch -- -v创建这种特殊命名的文件。完整实战练习继承原讲义以下是讲义附带的全部练习强烈建议逐一动手PATH、which、type运行echo $PATH或echo $PATH | tr -s : \n美化输出观察列出了哪些目录用which定位echo、ls、mv等常见命令。注意which不理解 shell 别名改用type和command -v对比输出差异运行PATH清空后再次执行之前的命令思考为什么有些命令仍能工作、有些不能。特殊变量~、.、..分别展开成什么$?表示什么$_呢!!展开成什么!!*和!l呢提示这是历史展开的用法查阅文档并熟悉它们的用法。xargs有时管道目标命令不接收换行分隔的输入。例如file命令能报告文件属性试运行ls | file与ls | xargs file观察xargs做了什么。它把标准输入内容转换成命令行参数。Shebang解释器声明编写一个名为hello的脚本内容如下用chmod x hello赋予执行权限后运行./hello#! /usr/bin/python print(Hello World!)删掉第一行再执行观察 shell 如何使用或不再使用这一行你还会经常见到#! /usr/bin/env bash这种写法它更可移植——思考env与which的区别以及env依据哪个环境变量决定运行哪个程序。管道、进程替换与子 shell 的差异创建可执行脚本slow_seq.sh#! /usr/bin/env bash for i in $(seq 1 10); do echo $i; sleep 1; done分别运行以下三条命令观察执行时序与输出的区别./slow_seq.sh | grep -P [3-6]grep -P [3-6] (./slow_seq.sh)echo $(./slow_seq.sh) | grep -P [3-6]结论要点管道中两侧程序并发运行进程替换同样并发而$()命令替换会等待命令完全结束后才继续。杂项运行touch {a,b}{a,b}后ls看出现了哪些文件应为aa、ab、ba、bb运行echo HELLO | tee hello.txt体会tee同时写文件与标准输出的能力运行cat hello.txt hello.txt预测并观察实际发生什么先echo HELLO hello.txt再echo WORLD hello.txt对比覆盖与追加运行printf \e[38;5;81mfoo\e[0m\n观察彩色输出可进一步搜索 ANSI 颜色转义序列touch a.txt后运行^txt^log看看 bash 帮你做了什么历史替换再运行fc历史命令编辑器。键盘快捷键^A行首、^E行尾^R历史反向搜索^L清屏^C、^\、^D中断 / 退出 / EOF^U删除到行首、^Y粘贴被删除的文本记住^X表示按下CtrlX这些快捷键与前述^Z挂起、^S/^Q输出流控制一起构成命令行日常操作的全套手势。进阶指引本仓库内的延续学习路径_2020/course-shell.md第一讲的完整中文讲义覆盖提示符、$PATH、man、重定向与管道、sudo与tee写/sys的经典坑_2020/shell-tools.mdshell 脚本语言函数、退出码、/||短路、set与find/grep/rg/history/fzf等工具_2020/command-line.md信号与任务控制的完整原理、tmux、别名、dotfiles 与 SSH_2026/course-shell.md2026 版入门讲义补充了set -euo pipefail严格模式、sed/awk等现代工作流以及反复运行测试直到失败的 flaky test 复现脚本可用于复现 CI 偶发失败。shell 脚本的复杂度在增加但收益也随之增长从记住魔法咒语到组合程序解决问题正是这门课想要给你的核心转变。赞分享文档教程教育【免费下载链接】missing-semester-cn.github.iothe CS missing semester Chinese version项目地址https://gitcode.com/gh_mirrors/mi/missing-semester-cn.github.io点击查看免费下载相关推荐Missing Semester 项目Shell 工具与脚本编程深度解析Missing Semester 项目Shell 工具与脚本编程深度解析 前言 在计算机科学领域掌握命令行工具和脚本编程是每个开发者的必修课。本文将深入探讨文档教程Missing Semester 项目Shell 基础与高效使用指南Missing Semester 项目Shell 基础与高效使用指南 引言 在计算机科学领域我们常常专注于编写程序让计算机执行重复任务却忽略了自身在使用计文档教程命令行环境实战指南别名、Shell 框架、终端复用器与现代 CLI 工具The Missing Semester 中文版命令行环境实战指南别名、Shell 框架、终端复用器与现代 CLI 工具The Missing Semester 中文版 本篇技术指南来自《计算机教育中缺文档教程教育上一篇QQ空间历史说说导出Excel一次扫码备份十年动态的完整指南下一篇DDrawCompat完全指南让经典DirectX游戏在现代Windows上焕发新生创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表