十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Linux Shell编程实战指南:从基础命令到脚本化与故障排查

Linux Shell编程实战指南:从基础命令到脚本化与故障排查 我最早对 shell 产生兴趣是很多年前在服务器上改配置文件一个文件一个文件用 vim 改到怀疑人生。后来发现一条sed命令能把批量的替换活儿干完才真正意识到shell 不只是那个黑底白字的窗口它本身是一套完整的、可以编程的表达方式。这篇文章不讲那种“翻开书从第一章背到最后一章”的教条而是把所有我觉得真正常用的操作、坑和思路串起来从手动敲命令讲到写脚本再到排查故障尽量一次说透。适合刚接触 Linux 的同学也适合已经写了一阵子脚本、但总觉得哪里不够顺手的人。目录先放这儿先说 shell 到底是什么、命令和环境的关系再从文件批量重命名这个高频需求切入基础操作接着进入脚本部分变量、循环、参数处理、函数然后把错误处理单独拉出来讲这是脚本能不能用的分水岭最后是老手也会翻车的编码、权限、空格、工具链以及常见问题速查。你完全可以把它当成一份查阅手册也可以从头读一遍。1. 先把 shell 的底子摸清楚它是谁能干什么1.1 不是你敲的每一行都是 shell命令、内建和外部程序很多人理解错了“shell 命令”这个概念。你在终端里敲一个ls这个ls不是 shell 实现的而是系统里一个独立的程序shell 只是找到了它、创建了一个子进程去执行它、然后等它返回结果。真正由 shell 自己干的事情比如cd、export、shift、umask这种才叫 shell 内建命令。区分这两件事很实用。比如which cd在多数系统上会告诉你找不到因为cd不是文件它是 shell 自己的功能而which ls能给你返回/bin/ls。再比如在脚本里你可能会写read这也是内建命令不依赖外部程序所以它可以给变量赋值而外部程序做不到这件事。理解这层关系对初期的心态调整很重要。你用 shell 不是在使用一个“软件”而是在使用一个“环境”去协调几十个外部程序。也就是说shell 脚本的很多性能损耗、行为差异、报错方式根本不是 shell 的意思而是里面调用的外部程序在起作用。排错的时候先分清“这是 shell 的问题”还是“这是某个命令的问题”能省下大量时间。还有一个基础但必须记住的点shell 不只有一个。最常见的是 bash还有 sh、zsh、fish、ksh。很多时候你用 bash 的语法写了个脚本把它放到一个只有 sh 的嵌入式系统上跑直接报语法错。所以写脚本第一行#!/bin/bash不是装饰它是在明确告诉操作系统“请用哪个解释器来读我”。1.2 从一个最常用的需求切入文件批量重命名先看一个非常常见的场景目录里有 100 个文件命名是IMG_001.jpg这种你想统一改成photo_001.jpg。手动一个个mv能改到你崩溃这时候 shell 的价值就出来了。最简单的方案是for f in IMG_*.jpg; do mv $f photo_${f#IMG_} done这里的${f#IMG_}是 bash 的参数展开意思是“去掉变量值开头的前缀IMG_”剩下的部分拼接在photo_后面。这种写法不依赖rename命令在纯 bash 环境里就能跑兼容性非常好。如果你用的发行版有rename命令注意有两个流派perl 版和 util-linux 版语法完全不同也可以一行完成rename s/^IMG_/photo_/ IMG_*.jpg # perl 版但我的习惯是优先用 bash 原生语法原因有两个一是在不同服务器上你无法确定有没有安装 perl 版 rename二是脚本里用循环还能顺手加日志、加判断后续扩展性更好。1.3 弄清楚你的 shell 环境bash、sh、zsh、PowerShell 不是一回事热词里同时出现了power shell、shell、linux shell说明大家在这几个词上很容易混。我简单给它们归个类。Linux/macOS 下的 shell常见就是 bash、zsh、sh。它们是同一家族的“Unix shell”语法大体相通但细节有差异。PowerShell这是微软专门为 Windows 做的脚本环境它的对象管道、命令命名规则和 Unix shell 完全不同但设计思路相当现代。cmd这是 Windows 老式命令行功能弱很多很多 Linux 用户到 Windows 上第一反应是装 PowerShell 或者干脆装一个 Git Bash。经常有人在 Linux 上写好了 shell 脚本到 Windows 上发现跑不了。这不是你写错了是环境根本不同。如果未来你需要在 Windows 上跑 Unix 脚本方案通常是装 WSL 或者 Git Bash而不是硬把 bash 脚本塞给 cmd 执行。还有热词里出现的lcaros shell extensions和nilesoft shell这俩是 Windows 文件夹右键菜单的第三方增强工具跟 Unix shell 不是一回事只不过都叫 shell 而已。看到这种词别慌知道它们是另外一个领域的东西就行。2. 从手动敲命令到脚本化变量、循环、参数一个都不能少2.1 变量与通配符别把路径写死写脚本的时候最忌讳的就是把所有路径直接写死在代码里。这不是说不能写而是当你把脚本交给另一个人、或者换一台服务器跑的时候全写死的脚本几乎必然要改。正确做法是把环境相关的信息提取成变量。比如BASE_DIR/data/logs LOG_ARCHIVE$BASE_DIR/archive today$(date %Y%m%d)注意变量赋值时等号两边不能有空格BASE_DIR /data/logs这种写法在 shell 里会解释成执行命令BASE_DIR并传入两个参数直接报错。这是新手最容易踩的第一个坑。通配符方面*代表任意字符?代表一个字符[...]代表字符集合。比如*.log、file?.txt、[0-9]*。需要特别注意的是通配符在没有匹配结果时默认会保留原样传给命令。比如目录下没有.log文件时ls *.log会提示找不到但如果你在脚本里做循环这个原样字符串反而会被当作文本处理导致逻辑出错。这时候可以开启nullglobshopt -s nullglob files(*.log)nullglob是 bash 的选项开启后没有匹配项时通配符会展开成空列表而不是保留原字符串。这个细节在批量处理任务里能救你一次。2.2 for 循环批量处理的核心武器shell 的for循环是使用频率最高的结构基本上“批量”两个字就离不开它。标准写法for i in 1 2 3 4 5; do echo $i done但实际工作中很少有人把数字列出来更多是用范围for i in {1..100}; do mkdir -p dir_$i done注意{1..100}是 bash 的扩展语法sh 不支持。如果想要更好的兼容性可以用seqfor i in $(seq 1 100); do echo $i done不过$(seq ...)这种命令替换方式会先执行外部程序性能略慢100 个数还好如果是 10 万个数建议换成{1..100000}或者直接用 C 风格循环for ((i1; i100000; i)); do echo $i doneC 风格循环在 bash 里完全支持而且对数字处理更灵活如果你要处理步长、倒序这类需求它比{...}扩展更顺手。2.3 shift 命令把脚本参数一个接一个弹出来shift是 shell 里一个很简单但很有用的内建命令。它做什么呢把位置参数向左移动一位。也就是说原本$1变成$2$2变成$3原来$1就没了。配合while循环可以很优雅地处理数量不定的参数while [ $# -gt 0 ]; do echo 参数: $1 shift done更常见的是用它配合 case 实现命令行选项解析while [ $# -gt 0 ]; do case $1 in -f|--file) FILE$2 shift 2 ;; -v|--verbose) VERBOSE1 shift ;; *) echo 未知选项: $1 exit 1 ;; esac done这里的shift 2表示一次性移掉两个参数因为-f后面一定会跟一个文件路径。写这种解析逻辑时我习惯在每个分支结尾都显式shift否则很容易出现死循环参数始终减不下去。2.4 函数与流程控制让脚本不是一串面条脚本写长了如果不用函数后面排错的时候会非常痛苦。函数定义很简单log() { echo [$(date %Y-%m-%d %H:%M:%S)] $* } log 开始备份函数里可以用return返回状态码0 代表成功非 0 代表失败。调用的时候不要加括号直接写函数名。这跟多数编程语言不一样容易混。流程控制方面if判断的常见写法是if [ -f $FILE ]; then echo 文件存在 elif [ -d $DIR ]; then echo 目录存在 else echo 都不存在 fi注意[其实是一个命令test 命令的别名所以[ -f $FILE ]里[后面必须有空格]前面也必须有空格$FILE最好加双引号防止文件名里有空格导致参数被拆开。这个细节我见过太多人翻车后面会专门再讲。函数与流程控制配合能让脚本变成“堆乐高”而不是“拧麻花”。我通常会把日志、备份、检查环境、失败退出这类通用逻辑全部写成函数放在脚本顶部后面主流程就简洁多了。3. 让脚本不轻易崩错误处理、退出码和日志3.1 退出码是什么$?值怎么用每个命令执行完都会返回一个整数给 shell叫退出码。0 表示成功非 0 表示失败。你在终端里经常看到$?它保存的是上一条命令的退出码。ls /nonexistent echo $?这条命令会输出一个非 0 值通常是 2因为目录不存在。脚本里判断命令是否成功习惯上是mkdir -p /data/logs if [ $? -eq 0 ]; then echo 创建成功 else echo 创建失败 fi不过这里有个更简洁的写法既然if可以直接判断命令是否成功就不需要先存$?再比较了if mkdir -p /data/logs; then echo 创建成功 else echo 创建失败 fi这种写法可读性更好。如果你只是想在命令失败时立刻退出可以写mkdir -p /data/logs || exit 1||的意思是“左边失败才执行右边”exit 1表示脚本以失败状态结束。3.2 set -e 不是万能的以及“忽略错误继续执行”的正确姿势很多教程会推荐在脚本开头写set -e表示“任何一步失败就退出”。这个设置在简单脚本里确实好用但它不是银弹反而会带来不少隐性坑。典型的问题有几个管道命令中只有最后一个命令的退出码会被set -e检查。比如cmd1 | cmd2哪怕 cmd1 失败只要 cmd2 成功脚本也不会退出。想改变这一点要配合set -o pipefail。命令替换里的失败不一定触发退出。比如dir$(ls /nonexistent)set -e不一定会拦住。在if条件里出现的失败命令不会触发退出因为这是 shell 语法的一部分。所以我的习惯是写基础脚本时可以用set -euo pipefail保底但涉及“某个命令失败是可以接受”的场景就显式把失败吞掉rm -f $tmp_file || true后面的|| true表示“无论 rm 是否成功整体都算成功”。这是实现“忽略错误继续执行”最常见的手段。如果你真的需要“一段脚本失败后继续另外一段必须卡死”可以局部控制set e # 这里面的失败不会导致退出 restore_something set -eset e临时关闭跑完再set -e恢复。这个方法比在每行后面都挂|| true更整洁但要注意别在函数里改完忘了恢复作用域会影响整个脚本。3.3 把关键操作包起来失败重试与超时控制真正生产环境的脚本跟教程里的“单次执行”区别很大。举个例子你去下载文件网络抖动一下命令失败如果脚本直接exit那整个任务白跑了如果什么错误都吞掉又可能拿到不完整的文件。这时候需要手动控制重试次数。max_retry3 count0 until wget -O $output $url; do count$((count 1)) if [ $count -ge $max_retry ]; then echo 重试 $max_retry 次后仍然失败 exit 1 fi sleep 2 done这段逻辑里until循环会一直跑到命令成功为止中间记录失败次数超过上限就退出。你还可以把sleep时间改成一个递增变量实现指数退避避免在服务端恢复前反复打爆接口。超时控制是另一个容易忽视的问题。ping、curl、ssh这类命令如果网络异常可能会长时间卡住。shell 里最直接的超时方案是利用外部命令timeouttimeout 30 ping -c 10 192.0.2.1如果命令在 30 秒内没执行完timeout会杀掉它并返回一个非 0 状态。这里要特别提醒timeout默认发送 TERM 信号有些程序会忽略如果你遇到“超时没杀掉”的情况可以加-k参数指定强制信号。3.4 日志输出与调试技巧bash -x脚本出问题的时候空口猜原因是最低效的做法。我在排障时第一件事就是用调试模式跑一遍bash -x ./myscript.sh加了-x之后bash 会把每条实际执行的命令展开打印出来变量也会替换成实际值。这个输出非常直观你能立刻看到究竟是哪一步的参数不对。比如脚本里有rm -rf $dir-x模式会显示rm -rf /data/tmp123你一眼就能看出路径有没有问题。如果脚本很长只想看某一段可以在关键函数附近手动加set -x # 需要追踪的代码 set x日志方面除了echo我建议规范一下级别log_info() { echo [INFO] $*; } log_error() { echo [ERROR] $* 2; }注意2的意思是把标准错误输出重定向到错误通道这样在重定向日志时错误信息不会被管线吞掉。很多运维的排查脚本把错误打到了 stdout结果管道下游一过滤什么线索都没了。4. 老手也会踩的坑空格、编码、权限与“奇怪字符”4.1 文件名带空格和换行for 循环直接翻车这是 shell 里最经典的一个坑。很多人写for f in $(ls *.txt); do echo $f done如果文件名是my file.txt循环会把这一行拆成两个文件my和file.txt。原因在于$(ls ...)的输出会被 shell 按空格、制表符、换行拆分成多个词。这不是for的问题是分词word splitting的表现。正确处理方式是直接用通配符展开而不是解析命令输出。因为通配符展开的结果不会做这种二次分词。for f in *.txt; do echo $f done如果你要处理的是一个列表文件每一行一个文件名正确做法是用while readwhile IFS read -r line; do echo $line done files.txt这里IFS的意思是关闭默认的空白字符分割read -r防止反斜杠被转义。这样即使文件名里有空格也能整行读入。更极端的情况是文件名里带换行符需要用find -print0配合read -d find . -name *.txt -print0 | while IFS read -r -d f; do echo $f done这个方案用了 null 作为分隔符因为路径里几乎不可能出现 null 字符所以最安全。不过别在这里加管道如果你想在循环里修改变量并让它们在循环外生效管道会启动子 shell变量修改会丢失。这个问题单独写一节。4.2 脚本文件的字符编码UTF-8 BOM 与 CRLF热词里有“shell 怎么查看脚本的字符编码”这个确实很容易被忽略。脚本文件本身是文本文本就有编码问题。先看文件编码file script.sh如果是ASCII text或UTF-8 Unicode text基本没问题如果显示带CRLF说明文件是 Windows 换行风格。CRLF 会导致什么问题呢最典型的是 shebang 行#!/bin/bash\r系统会把\r当成解释器名字的一部分然后报错/bin/bash\r: No such file or directory或者脚本里每行结尾都有一个不可见的^M命令和参数连在一起各种诡异报错。处理 CRLF 有两个常用手段dos2unix script.sh # 或者 sed -i s/\r$// script.shdos2unix是专用工具没安装的话直接用sed。UTF-8 BOM 更加阴险。BOM 是文件开头的三个字节EF BB BF很多 Windows 编辑器会默认加上。Unix 工具一般不认识它于是第一行 shebang 变成\xEF\xBB\xBF#!/bin/bash同样导致“解释器找不到”。排查方法head -c 3 script.sh | xxd能看到efbbbf就说明有 BOM。移除 BOMsed -i 1s/^\xEF\xBB\xBF// script.sh写脚本时我建议统一用 UTF-8 无 BOM换行符统一 LF。编辑器里设置一次后面能少踩很多隐形坑。4.3 权限问题Permission denied 不只是 chmod x执行脚本遇到Permission denied第一反应是chmod x script.sh这没错但权限问题不止这一个。脚本本身需要可读和执行权限。如果用户只有读权限没有执行权限会报 Permission denied如果只有执行权限没有读权限bash 解释器读不了脚本内容同样报错。一般推荐chmod 755 script.sh。再一个容易被忽略的点你执行脚本的方式。./script.sh需要脚本有执行权限但如果你用bash script.sh执行实际上只需要读权限因为你是把脚本文件作为参数传给 bash 解释器读的。这个区别在写脚本时可以用来规避某些情况下无法给执行权限的问题。还有一种“Permission denied”跟挂载选项有关。比如某个目录以noexec方式挂载哪怕权限位是 755也只能读不能执行。这常见于/tmp和一些容器挂载目录。排查时可以用mount | grep tmp看挂载参数。写定时任务脚本时如果脚本放在/tmp下就很容易遇到这个问题。4.4 source 与bash script.sh的差别source script.sh或者简写. script.sh与bash script.sh是两种完全不同的执行方式。bash script.sh会启动一个新的子进程脚本里定义的变量、函数在子进程结束后全部消失父 shell 里看不到。source会在当前 shell 进程里直接执行脚本内容脚本里定义的变量、函数会留在当前环境里。还有一个很实际的区别是工作目录。子进程执行脚本时脚本内部的cd不会影响当前 shell 的目录而source一个带cd的脚本会把当前 shell 的目录也改了。所以很多初始化环境、设置别名、加载密钥的操作都要求用source而普通的自动化任务更推荐用bash script.sh这样不会污染当前环境。“linux 执行 shell 文件命令 获取文件所在目录”这个热词也经常出现。在脚本内部想拿到自己所在的目录很多人写cd $(dirname $0)这在不同调用方式下会出问题。更稳妥的是SCRIPT_DIR$(cd $(dirname ${BASH_SOURCE[0]}) pwd)BASH_SOURCE[0]能拿到当前脚本的路径dirname取目录cd过去再pwd得到绝对路径。这个写法在任何目录下执行脚本都能找到脚本位置配合相对路径引用同目录下的其他文件非常可靠。4.5 脚本文件中的时空错位子 shell 与变量丢失我上面提到管道里的while循环它读取文件没问题但如果你在循环里对变量做累加循环结束后变量值可能没有变。原因就是管道两侧在子 shell 中执行变量修改不会传回来。count0 cat files.txt | while read -r line; do count$((count 1)) done echo $count # 仍然输出 0解决方法是用进程替换count0 while read -r line; do count$((count 1)) done (cat files.txt) echo $count(...)是 bash 的进程替换它把这个“命令的输出”包装成一个虚拟文件while 循环在当前 shell 里执行变量自然能保留。类似的坑还出现在find ... | while和for循环的$(...)中理解了子 shell 的存在排这类错就很快。5. 常见问题排查与实用工具链5.1 常见问题速查表现象可能原因处理方式执行脚本报/bin/bash^M: No such file or directory文件是 CRLF 换行sed -i s/\r$// script.sh或dos2unix脚本第一行有\ufeff或报解释器找不到UTF-8 BOMsed -i 1s/^\xEF\xBB\xBF// script.sh文件名带空格循环被拆开命令输出触发了分词改用通配符或while IFS read -r line循环里累加变量最后变回 0管道左侧进入子 shell用进程替换代替管道chmod x后仍不能执行挂载选项 noexec检查挂载参数换目录放脚本set -e下脚本提前退出有命令返回非 0按预期允许失败的命令加 命令很长被截断或变量为空引号没加变量引用统一加双引号$?不是预期的退出码上一条命令被别名或函数包裹单独执行一次原始命令验证网络命令卡住不返回没设超时用timeout包一层脚本在 zsh 下正常在 sh 下报语法错用了 bash 专属语法检查数组、${var,,}、[[ ]]等写法这个表是我自己在维护几十个脚本时沉淀下来的。每一条背后都是至少踩过一次的坑尤其是 CRLF 和 BOM隔一段时间总会遇见一次。5.2 用 shellcheck 做静态检查别靠肉眼找错写完脚本不要急着跑先让工具扫一遍。shellcheck 是我目前见过最靠谱的 shell 静态检查工具。很多发行版可以直接安装apt install shellcheck # 或 yum install shellcheck # 或 brew install shellcheck用法很简单shellcheck myscript.sh它会提示你哪一行可能有问题还可能给出具体建议。比如“双引号应该加到变量上”“在循环中使用了 ls 输出”“不建议使用 cat 只读单个文件”等等。我第一次用 shellcheck 时自己的脚本被提示了几十处问题其中大多数不是语法错误而是潜在的坑。比如cat file | grep keyword它会建议改成grep keyword file省掉一个多余的管道和子进程。这些建议未必每条都必须采纳但能帮你意识到自己的写法哪里不够好。顺带一提如果想要统一格式风格可以用shfmt自动格式化脚本。它会把缩进、空格、换行整理成比较规范的样子。虽然 shell 对空格不敏感但统一格式对多人维护很有帮助。5.3 脚本加密、编译与分发到底有没有必要热词里有“shell 文件加密网站”和“shell script compiler”。先说结论shell 脚本本质是文本所谓加密在技术层面很难做到绝对安全所谓编译往往也只是把脚本打包成一个二进制外壳源码仍然能提取出来。市面上常见的shc工具可以把脚本转成一个加密的二进制可执行文件。但它有几个致命问题需要目标机器有相同架构和动态库环境跨平台分发不方便。所谓的加密很容易被逆向懂行的人用工具几分钟就能还原脚本内容。shc 在某些新系统上编译出来的二进制运行时会因为 glibc 版本不一致而报错。所以我的建议是如果脚本里有敏感信息比如数据库密码正确的做法不是加密脚本而是把敏感信息放到权限受限的配置文件里脚本里只读取不写死。如果脚本需要分发给别人但又不想让对方看到内部逻辑先想想这个需求是否合理——很多时候分发二进制或服务化是更合适的方式。至于“shell 文件加密网站”我强烈不建议用。你把完整源码粘贴到别人网站上等于直接把脚本内容送给第三方这比自己脚本泄露的风险还高。密钥管理是另一门课不要指望靠“加密脚本”解决。5.4 跨到 WindowsPowerShell 与 cmd 的区别有热词专门问 “power shell 和 cmd 区别”。如果你的日常是 Windows 环境这个话题非常重要。cmd 的历史很长但它能做的事情相对有限脚本语言也简陋。PowerShell 是微软后来推出的壳和脚本环境它最大的特点是“面向对象”命令之间传递的不再是纯文本而是结构化的对象。比如Get-Process | Where-Object { $_.CPU -gt 100 }这个管道里流淌的是进程对象而不是文本行。跟 Linux shell 相比PowerShell 的语法习惯很不一样它用Get-Content而不是cat用Set-Location而cd只是别名。如果你用的是 PowerShell 7 跨平台版本在 Linux 上也能跑。但注意PowerShell 脚本和 bash 脚本不互通不能简单地把.ps1改名为.sh就拿去 Linux 执行。对于做运维或桌面自动化的 Windows 用户学 PowerShell 是值得的。但如果你是 Linux 服务器方向bash 仍然是优先级最高的技能。5.5 adb shell移动端调试里的 shell 场景热词里有一批adb shell这个我简单说两句。adb是 Android Debug Bridge 的缩写它的adb shell子命令可以让你在电脑上打开一个运行在安卓设备上的 shell。后面可以接普通命令也可以接设备相关的调试工具。比如看设备电量历史adb shell dumpsys batterystats --enable full-wake-history adb shell dumpsys batterystats再比如列出应用包名adb shell pm list packages如果你在adb shell后面看到一串类似/data/app/~~xxxx/package-name-xxx的路径那是系统给应用分配的安装目录不同版本 Android 路径格式会变不用太慌。adb shell本质上就是一个远程 shell很多 Linux 上的 shell 概念在这里照样适用比如管道、重定向、grep用法完全一样。6. Linux shell 编程岗位需求与学习建议6.1 这个岗位需要哪些技能热词里有“linux shell 编程的岗位需求以及知识技能需求”。从招聘信息来看shell 很少单独作为岗位名称但很多运维、测试开发、后端开发岗位会明确要求“熟练使用 Linux 和 shell”。这里的能力通常分为几层基础层熟悉常用命令能查看日志、查找文件、处理文本用单行命令完成临时任务。中间层能写结构化脚本有变量、函数、参数解析、错误处理能做定时任务。进阶层能写几十行以上的自动化工具考虑并发、日志轮转、失败重试能跨机器部署。如果你在简历里写“熟练掌握 shell”至少要保证能独立完成一个带参数解析、日志、错误处理的部署脚本。单会写for循环往屏幕上打印数字别写这条。6.2 一个可复训的参考路径我给零基础的朋友一个快速上手路径先练命令。每天在终端里处理小事比如find、grep、awk、sed各找几个例子跑通不要求背参数但要理解管道和重定向的工作原理。再写脚本。挑一个重复操作改成脚本比如批量重命名、批量备份到目录、整理下载文件夹。脚本不一定复杂但一定要包含变量和循环。加参数。给你的脚本加-h、-f、-v这类选项学shift和case让脚本变得可配置。做错误处理。想想“如果这个目录不存在怎么办”“如果网络断了怎么办”然后写重试、日志和失败退出。最后看规范。跑 shellcheck、读别人的开源脚本学习一些约定俗成的写法。我不建议买一本几百页的 shell 书从头啃边做边查效率更高。重点是把“查 man 手册”和“用搜索引擎”变成习惯随着踩坑变多原来记不住的参数自然而然就记住了。7. 我最后想啰嗦的三件小事第一给脚本里的每个变量加双引号。这是成本最低、收益最高的习惯。$file比$file安全得多。第二写脚本时永远假设你的输入可能完全不正常目录可能不存在、文件可能为空、网络可能超时。多写几个判断不会浪费太多时间但真遇到问题时能省下大把补救时间。第三遇到诡异报错先用bash -x跑一遍再查文件编码和换行符。很多你以为的“语法错误”其实只是文件里藏了一个看不见的字符很多你以为的“系统问题”其实只是某个路径没有按预期展开。shell 这个东西说简单确实简单ls、cd、cp一会儿就上手了说深也确实深一个IFS都能牵扯出好几层原理。但它的迷人之处正在于此你对细节理解得越深能驾驭的任务边界就越宽。希望这篇东西能帮你少走一段我当年走过的弯路。
返回列表