☰
Shell脚本入门必会:echo、read、printf、test四个命令详解
2026/10/10 5:37:06 网站建设 项目流程

很多人学 Shell 脚本入门时,最先接触的就是echo,能把ls、cd、grep用得滚瓜烂熟,但真到自己动手写脚本,反而卡在几个最基础的环节:怎么让脚本暂停等用户输入、怎么输出一张对得齐的表格、怎么写判断条件。问题往往不在复杂语法上,而在于对echo、read、printf、test这四条“语法级”命令的理解太浅。这四个命令看似简单,实则是构成一切 Shell 脚本交互、判断、输出能力的基石。这篇文章就把它们逐个拆开讲透——原理是什么、坑在哪里、实际项目里怎么组合着用,希望能帮刚入门的同学少走几个弯路,也能让写过一段时间脚本的人重新审视一下自己的用法是否稳。

1. 四个命令的分工逻辑:为什么Shell脚本入门必须先吃透它们

1.1 脚本的本质:输入、输出、判断

任何一个脚本,无论业务多复杂,拆到最底层都逃不开三类动作:接收外界数据(输入)、把结果呈现给用户(输出)、根据条件决定走哪条分支(判断)。Shell 编程里对应这三类动作的最基础命令,正好就是read、echo/printf、test。

拿一个真实场景来说:假设你要写一个部署脚本,脚本启动时需要问用户“目标目录不存在,是否创建?”,这叫输入;答完之后,脚本根据回答决定创建还是不创建,这叫判断;最后把每一步的执行结果打印成带缩进、带颜色的日志,这叫输出。整个流程没有碰任何高级命令,但已经是一个完整可用的工具脚本了。所以说,这四个命令不是“四个单独的零碎知识点”,而是一套互相协作的最小闭环。

1.2 它们和普通命令的区别:语法级 vs 工具级

很多 Shell 教程会把ls、cd、curl、grep和这四个命令混在一起讲,这是我觉得比较误导的地方。ls是“工具级”命令,作用是完成一个具体业务动作;而echo、read、printf、test是“语法级”命令,它们更接近编程语言里的输出语句、输入语句、逻辑判断表达式。

差异体现在哪里?举个例子,printf的行为几乎在所有符合 POSIX 标准的 Shell 里都一致,你可以在 bash、dash、sh、zsh 里跑同一个printf脚本而不用担心行为漂移;但ls的彩色输出、排序方式在不同系统上差异明显。这两个层级最大的区别是:工具级命令可以随时替换成别的工具,而语法级命令是你自己写的脚本逻辑的一部分,换了一个环境,脚本能不能跑,取决于你对它们的理解到不到位。

我自己带过不少新人,观察到一个规律:凡是在[ ]里不加引号、用>比较数字、在管道里用read后变量丢失这类问题上反复踩坑的,基本都是没想清楚这四件事的分工。所以这篇博文不按“命令大全”的路子罗列参数,而是按“输入-输出-判断-组装”的思路来逐个突破。

2. echo:看似简单,输出细节里全是门道

2.1 三种引号状态的差异:不加引号、单引号、双引号

echo最容易翻车的地方不是命令本身,而是它后面的字符串怎么写。初学者经常看见三种写法都输出同一个结果,就以为随便写都行,直到遇到变量和特殊符号才懵。

name="shell" echo hello world # 裸奔式 echo 'hello world' # 单引号 echo "hello world" # 双引号

这三种写法在输出“hello world”时没有区别,但一旦字符串里出现变量、转义符、通配符,行为就完全不同了:

  • 不加引号:Shell 会先做变量展开、命令替换、路径通配符展开,然后按空格把所有结果拆成多个词传给echo,最后用空格拼接输出。这个“拆词”过程是很多人没意识到的隐藏步骤。
  • 单引号:里面所有字符都按字面意思输出,$、反引号、*全部失效。
  • 双引号:变量和命令替换照常执行,但路径通配符不展开,分词也不发生。

举个最容易踩坑的例子:

name="hello world" echo $name # 输出 hello world(但实际拆成两个词,只是输出时用空格连起来) echo "$name" # 输出 hello world(作为一个整体)

echo $name和echo "$name"在屏幕上看起来一样,但前者会把name的值按空白拆开再重组,如果值里有连续空格,连续空格会被折叠成一个;后者则会原封不动保留。所以凡是涉及变量输出,我强烈建议养成加双引号的习惯。这个习惯在test判断里同样关键,后面会再讲。

2.2 转义与换行:echo 不是默认不换行,而是默认帮你换行

echo每条输出末尾默认带一个换行符,这是它与printf最明显的差异点之一。很多人以为echo "hello"输出的是hello,其实完整输出是hello\n。在绝大多数场景这就是你想要的效果,但当你需要连续输出不带换行的提示时,就得用-n参数:

echo -n "请输入用户名:" read username

这样提示和输入会出现在同一行,交互体验更自然。如果想要在字符串中间插入换行或制表符,bash 内建的echo默认不解释转义序列,需要加-e:

echo "第一行\n第二行" # 原样输出 \n 两个字面字符 echo -e "第一行\n第二行" # 真正换行 echo -e "列1\t列2\t列3" # 制表符分隔

需要特别留意的是,-e不是 POSIX 标准里echo的通用选项。在 Ubuntu 等系统默认的dash里,sh script.sh执行时echo -e "abc"会把-e当成普通文本原样输出,结果变成-e abc;Linux 发行版里/bin/sh指向 dash 是很常见的事情,这也是为什么很多教程会强调:如果你要精确控制转义和格式,请用printf,而不是赌echo -e是否可用。

2.3 echo 最容易被忽视的边界行为

第一类边界是输出以-开头的字符串。比如你想输出-n这个字面文本:

echo "-n" # 某些 Shell 里输出为空,因为 -n 被当成参数 echo -- "-n" # 部分实现支持 -- 结束选项解析 printf '%s\n' "-n" # 更稳妥

用printf处理这类场景几乎不会出问题,这也是它存在的意义之一。

第二类边界是变量值以特殊选项开头或者含有控制字符。比如echo "$var",当$var的值刚好是-e时,echo 会当成开启转义选项处理,行为就变了。加不加引号都避免不了这个风险,因为-e是在参数解析阶段被识别的。真正一劳永逸的做法就是:输出普通文本用 echo,输出任何不可控变量或需要精确格式的内容,一律用 printf。这是我在脚本里一贯的取舍标准。

第三类边界是echo与命令替换组合时的换行丢失。比如:

result=$(ls -1) echo "$result" # 换行保留 echo $result # 换行全部变成空格

不加引号的echo $result会把命令结果里的换行当成空白分隔符重新“拍平”,这往往是脚本日志格式搅成一团的原因。命令行里肉眼看着没问题,写到脚本里就开始出现怪现象,多半就是引号问题。

3. read:脚本获取输入的真正入口

3.1 交互式读取:-p -t -s 三个参数解决大部分场景

很多脚本不是给人填参数的,而是运行时需要按步骤确认。read就是把标准输入读进变量的命令,基础用法是read 变量名,回车结束。如果变量名不止一个,Shell 会按空白把输入切分成多段,剩余部分全给最后一个变量。

最常用的三个参数是配合交互场景的:

read -p "请输入目标IP: " ip # 直接在同一行打印提示 read -t 10 -p "10秒内确认是否继续(y/n): " confirm # 超时自动跳过 read -s -p "输入密码: " passwd # 静默模式,输入不回显

-p负责把提示文本和输入框放在同一行,脚本里就省掉一句echo -n;-t给了超时保护,避免脚本卡死在等人输入;-s常用于密码读取,回车后需要自己补一个换行打印,否则后续输出会直接接在密码后面。

这三个参数在实际脚本里几乎是标配。比如一个自动化巡检脚本,会先read -t 5给操作员一个中断窗口,超时就按默认值继续跑,既保留人工干预能力,又不会阻塞无人值守的任务。

3.2 按行读取文件:while read 的正确姿势

read不只是读键盘,更常见的高阶用法是逐行读取文件内容。经典写法是:

while IFS= read -r line; do echo "$line" done < config.txt

这里有三个细节值得解释,因为很多人直接抄但不知道为什么要这么写:

  • IFS=:把内部字段分隔符临时置空,防止read把行首行尾的空白字符吃掉。默认的IFS包含空格和制表符,会让读进来的行“变形”。
  • -r:禁止把行尾的反斜杠当作续行符处理。如果不加,文件里一行末尾是\时,read 会认为行没结束,把下一行拼接进来,导致解析错乱。
  • done < file:把文件通过输入重定向喂给整个循环,而不是在循环体里一个个打开文件。

为什么不直接用for line in $(cat file)?因为 $(cat file) 会先做命令替换,文件里的换行全部变成空格,整个文件变成一长串单词,随后还要再经历一次分词和通配符展开,几乎每一行都会变形。用一个包含空行、带星号、带空格的配置文件试一次就能直观体会到差距。

3.3 管道里的 read 陷阱:变量为什么会“凭空消失”

按行读文件时,有一种很自然的写法是:

cat config.txt | while read line; do count=$((count+1)) done echo "总行数: $count"

运行后你会发现$count是空的。原因是管道右侧的命令是放在子Shell里执行的,while read循环体里对变量的所有修改都发生在子Shell环境内,循环结束、子Shell退出,改动就丢了。外面的父Shell看到的还是初始值。

这不是read本身的问题,而是管道机制的特性。解决思路有两种:一是改用输入重定向的方式,也就是while ... done < config.txt,这样循环在当前Shell里执行;二是如果非要用管道,就把需要带出来的结果写到文件里再读,或者用组结构{ ...; } | command配合进程替换(< <(command))让整个结构在同一进程里跑。我个人的习惯是能不用cat | while就不用,改成重定向或进程替换,避免后续排查时多花一小时才想起子Shell这回事。

3.4 多变量读取:一次请求拆成多个输入

有时候交互需要用户一次输入多个值,比如“端口号 协议 超时时间”,可以写:

read -r port proto timeout

用户输入8080 https 30,三个值会按顺序分别进入三个变量,最后一个变量会接收剩余的所有内容。如果只想读两个字段,而用户多给了一个值,多出来的部分会整体粘在第二个变量上,不会报错。这个特性在解析形如名称 值的配置行时很有用:

while read -r key value; do echo "key=$key, value=$value" done < settings.txt

配合IFS=:还能自定义分隔符,比如解析/etc/passwd风格的数据:

IFS=: read -r user pass uid gid desc home shell < <(echo "root:x:0:0:root:/root:/bin/bash") echo "用户: $user, UID: $uid, 家目录: $home"

4. printf:格式化输出的正规军,echo 的跨平台替代者

4.1 为什么必须学 printf:echo 的跨shell差异和不可控格式

我前文反复提到echo -e在 sh 和 bash 下行为不一致,而printf最大的价值就是“稳定”。它源自 C 语言的printf,在 POSIX 规范里行为严格定义,不管用 bash、dash、zsh 还是 ksh 跑,同一个脚本的输出基本一致。

printf "hello\n" # 输出 hello 并换行 printf "%s\n" "hello" # 按字符串格式输出

printf的第一个参数是格式字符串,后面的参数根据格式符逐一对位输出。注意它不会像echo那样对参数里的变量做额外的二次整理,所以printf '%s\n' "$var"是处理不可控变量的最安全方式——不管变量里有多少个空格、以什么特殊字符串开头,都会被当作一个普通参数塞进%s里。

4.2 格式符实操:字符串、整数、浮点数的对齐与精度

printf的格式符最常用的就四个:%s处理字符串、%d处理整数、%f处理浮点数、%x处理十六进制。配合宽度和精度修饰符,可以做表格对齐、补零、小数截断等效果。

printf "%-20s %10s\n" "项目名" "状态" printf "%-20s %10s\n" "nginx" "OK" printf "%-20s %10s\n" "mysql" "FAIL"

%-20s表示左对齐占 20 个字符宽度,%20s表示右对齐占 20 个宽度。做日志输出时,这比手工补空格靠谱得多,因为你不用自己去数字符串长度。

数字格式化:

printf "本次失败次数: %d\n" 3 printf "占用率: %.1f%%\n" 67.89 # 保留一位小数,%% 输出百分号 printf "编号: %05d\n" 42 # 相当于是 00042 printf "十六进制: %#x\n" 255 # 输出 0xff

这里面有无数新人踩过%%的坑:格式字符串里%是特殊字符,要输出字面百分号必须写两个%%。否则printf "占用率: %\n"会直接报错或者输出怪异内容。

4.3 printf中文乱码的真相与处理

热搜里经常有人搜“printf中文乱码”,我自己也遇到过。先分清楚:printf本质是字节流输出,如果终端和系统 locale 都是 UTF-8,它输出中文和在文件里写入中文没有任何区别,不会有乱码问题。真正常见的两类“中文乱码”其实是:

第一类是终端编码问题。脚本里输出中文乱码,先查环境变量:

echo "$LANG"

如果输出的是POSIX或C,说明当前 locale 是英文模式,很多终端下中文会显示成问号。简单处理是:

export LANG=zh_CN.UTF-8

放在脚本开头,或者写入用户的环境配置文件里。但要注意,很多服务器没有安装中文本地化数据,强制设置zh_CN.UTF-8可能报错。兼容做法是export LANG=C.UTF-8,大部分现代 Linux 都支持,中文能正常显示,英文提示也不受影响。

第二类是对齐宽度错乱。真正和printf直接相关的“乱”不是乱码,而是对不齐。printf的宽度计算按“字符个数”来算,一个中文字符算一个宽度单位,但在终端里实际显示占两个英文字符的宽度。所以下面的输出右边会不齐:

printf "%-15s\n" "名称" printf "%-15s\n" "数据库服务"

解决思路有两个:一是中英文混合对齐时不要依赖printf的宽度符,改用column -t之类的工具对表格做重排;二是在知道固定列宽的前提下,把中文字符串按显示宽度换算成字节长度,手动补齐,但这样代码会很丑。项目里如果明确要求中文表格对齐,我的建议是直接用制表符或者column工具,不要跟printf的宽度较劲。

4.4 printf 与变量展开的隐藏交互

网上有个经典段子:printf "$var"写法在某些情况下会挂。原因是如果$var的值里恰好有%,这个%会被当作格式符引入,比如变量内容是100%损坏,printf "$var\n"会把%损坏当成未知格式符,轻则报错,重则输出错乱。正确做法永远是:

printf "%s\n" "$var"

让变量以参数形式进入,而不是拼进格式字符串。同理,格式字符串里要输出变量值但想控制宽度时,可以写成printf "%-20s" "内容",内容是参数,宽度在格式里写死。这种习惯能帮你避开一大批隐性问题。

5. test:决定脚本走向的隐形判断器

5.1 三种写法的历史沿革与选择

test这个命令有很多“长相”,最古老的直接写test 条件,随后演变成更常用的[ 条件 ],再后来 bash 等高级 Shell 里有了[[ 条件 ]]扩展写法。三种写法本质都在做同样的事:根据条件真假返回退出码 0 或 1。

对新人来说,最令人困惑的是[ ]里的空格。[实际上是一个命令,不是语法符号,所以它和里面的表达式之间必须有空格,]和前面表达式之间也必须有空格。写成[$var]或者[ $var]都会报错,因为命令名变了。

我的实践选择是:

  • [[ ]]是 bash/zsh 的扩展,支持=~正则匹配、&&、||直接写在里面,且不会做词拆分,逻辑也更直观,推荐在 bash 脚本里使用。
  • [ ]是 POSIX 标准写法,可移植性最强,但表达能力弱、容易踩引号坑。
  • 如果脚本要同时兼容多种 Shell(比如/bin/sh环境),就老老实实用[ ]加引号。

5.2 文件判断:检查存在性、类型、权限

写脚本时最常判断的无非是文件或目录的状态。下面这组参数要刻进脑子:

[ -e "$path" ] # 路径存在(文件、目录、软链接均算) [ -f "$path" ] # 是普通文件 [ -d "$path" ] # 是目录 [ -s "$path" ] # 文件存在且大小非0 [ -r "$path" ] # 当前用户可读 [ -w "$path" ] # 当前用户可写 [ -x "$path" ] # 当前用户可执行 [ "$f1" -nt "$f2" ] # f1比f2新

典型用法是部署脚本里的路径预检:

if [ -d "/opt/myapp" ]; then echo "目录已存在" else echo "目录缺失,准备创建" fi if [ ! -w "/opt/myapp" ]; then echo "目录不可写,请检查权限" exit 1 fi

这里-w判断的是“当前用户视角”的可写性,依赖运行时用户身份。如果脚本通过sudo执行,判断结果和普通用户执行可能完全不同,所以要留意脚本本身的运行身份。

5.3 数值比较与字符串比较:千万不能混用

这是test命令里事故率最高的区域。我见过太多人写这样的判断:

count=10 if [ $count > 5 ]; then echo "数量大于5" fi

这段代码本质上并不是在比较“10是否大于5”,而是把>当成了输出重定向操作符,实际执行的是:给[ $count这个命令创建了一个名为5的文件并写入内容,然后判断命令是否成功。初始执行可能碰巧退出码为 0,于是输出“数量大于5”,但副作用是当前目录多了一个叫5的文件。这是非常经典的坑。

数值比较必须用专用的单字母运算符:

[ "$count" -eq 5 ] # 等于 [ "$count" -ne 5 ] # 不等于 [ "$count" -gt 5 ] # 大于 [ "$count" -ge 5 ] # 大于等于 [ "$count" -lt 5 ] # 小于 [ "$count" -le 5 ] # 小于等于

字符串比较则是另一套:

[ "$name" = "shell" ] # 相等(POSIX标准) [[ "$name" == "shell" ]] # 相等(bash扩展) [ "$name" != "shell" ] # 不等 [ -z "$name" ] # 长度为0 [ -n "$name" ] # 长度非0

特别注意>和<在字符串比较里也可以用,但比较的是字典顺序而不是数值大小。要是把[[ "$count" > 5 ]]放在 bash 里,当count=8时输出“大于”,当count=10时反而输出“不大于”,因为字符串"10"在字典序上小于"5"。这种随机性极强的问题,排查起来非常隐蔽。

5.4 组合条件:-a/-o 与 &&/|| 的区别和引号问题

[ ]里组合多个条件用-a(and)和-o(or):

if [ "$user" = "root" -a "$home" = "/root" ]; then echo "root用户" fi

而[[ ]]和普通 shell 语句级可以直接用&&、||:

if [[ "$user" == "root" && "$home" == "/root" ]]; then echo "root用户" fi

这两种混用很常见,但要注意范围和优先级。推荐在[ ]里统一用-a/-o,在[[ ]]里统一用&&/||,不要混着写,否则到优先级问题时很难阅读。

引号问题在test里尤其致命。看这段:

name= if [ -n $name ]; then echo "非空" fi

当$name为空时,经过 Shell 展开后[ -n $name ]实际变成[ -n ]。此时-n被当成一个长度为 2 的字符串传入单参数测试,条件为真。这个反直觉的结果让很多人怀疑人生。正确写法是:

if [ -n "$name" ]; then

变量加双引号,空变量展开后依然是一个空参数,条件才符合预期。凡是test里用到变量,一律加双引号,这条规则能过滤掉 90% 的误判问题。

5.5 [[ ]] 的高级特性:正则匹配是杀手锏

bash 的[[ ]]里可以用=~做正则匹配,这个能力在判断输入格式、校验 IP、校验版本号时非常实用:

version="1.9.3" if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "版本号格式合法" fi answer="Y" if [[ "$answer" =~ ^[Yy](es)?$ ]]; then echo "用户确认" fi

这比[ "$answer" = "y" -o "$answer" = "Y" ]这种写法清爽太多。正则功能是 bash 3.0 之后引入的,日常几乎不用担心兼容性,但对类似/bin/sh的环境不可用,所以脚本第一行写了#!/usr/bin/env bash,才建议在主逻辑里依赖[[ ]]。

6. 四个命令协同:一个完整的上线前自检脚本

6.1 脚本需求与设计思路

光讲单个命令枯燥,组合起来才能体现价值。假设我们现在要写一个上线前自检脚本preflight.sh,需求如下:

  1. 接收目标目录作为参数,未传则使用默认路径;
  2. 检查目录是否存在,不存在则询问用户是否创建;
  3. 检查目录是否可写,不可写则报错退出;
  4. 展示一个格式化的检查项表格;
  5. 最终确认后输出部署开始提示。

这个脚本刚好覆盖echo、read、printf、test四个命令的全部核心用法。设计上没有用任何高级功能,但足够作为模板套用到真实项目里。

6.2 完整实现与逐段讲解

#!/usr/bin/env bash # preflight.sh - 上线前基础环境自检 target_dir="${1:-/opt/myapp}" check_passed=0 printf "%-25s %s\n" "检查项" "结果" printf "%-25s %s\n" "-------------------------" "------" # 检查1:目标目录是否存在 if [ -d "$target_dir" ]; then printf "%-25s %s\n" "目标目录存在" "通过" else printf "%-25s %s\n" "目标目录存在" "缺失" read -r -p "目录 $target_dir 不存在,是否创建?[y/N] " answer if [[ "$answer" =~ ^[Yy](es)?$ ]]; then mkdir -p "$target_dir" || { printf "创建目录失败,退出\n" >&2 exit 1 } printf "%-25s %s\n" "目录创建" "通过" else printf "用户取消部署,退出\n" >&2 exit 1 fi fi # 检查2:目标目录是否可写 if [ -w "$target_dir" ]; then printf "%-25s %s\n" "目标目录可写" "通过" else printf "%-25s %s\n" "目标目录可写" "不可写" printf "请检查目录权限,或使用具备写权限的身份执行\n" >&2 exit 1 fi # 检查3:磁盘可用空间(简单模拟判断,大于100M视为通过) avail=$(df -Pm "$target_dir" | awk 'NR==2 {print $4}') if [ "$avail" -gt 100 ]; then printf "%-25s %s\n" "磁盘可用空间" "通过" else printf "%-25s %s\n" "磁盘可用空间" "不足" exit 1 fi # 最终确认 printf "==== 预检完成,结果列表见上方 ====\n" read -r -p "确认继续部署?[y/N] " confirm if [[ ! "$confirm" =~ ^[Yy](es)?$ ]]; then echo "已取消部署" exit 0 fi echo "开始部署..." # 这里放真实业务动作 sleep 1 printf "部署完成\n"

逐步拆解一下这段脚本里每个决策的来由:

  • ${1:-/opt/myapp}是参数展开,$1未传时用默认值,脚本就不会因为少一个参数而报错。
  • 表格部分全部用printf "%-25s %s\n",左对齐宽度固定,肉眼就能看出哪行没对齐。
  • 用户确认部分用read -r -p,-r防止用户输入反斜杠时出现意外续行。
  • 判断用户是否一路确定时,把[[ =~ ^[Yy](es)?$ ]]这种写法和大小写都兼容。
  • 磁盘空间判断里把awk提取的数字存成字符串,然后用-gt比较数字值。有人会问“字符串怎么直接比较数字?”——在 test 的-gt语义里,两边的字符串会被当作整数解析并比较,所以"150"和100的比较结果是正常的数字比较,而不是字典序比较。
  • 第二个read的确认判断用了[[ ! "$confirm" =~ ... ]],非匹配时退出,配合exit 0让取消不算错误,符合业务直觉。

6.3 复盘高频坑位:这条排查链路值得记下来

我把实际维护脚本过程中最高频的坑位按“现象-原因-解法”的格式列一下,每条都是真实踩过的:

现象根因正确做法
[ $count > 5 ]生成了名为5的文件>被当成重定向数字比较用-gt,字符串比较用>时放在[[ ]]里
[ -n $name ]对空变量判真变量展开后-n变成单参数统一"$name"加双引号
管道里 read 后变量丢失管道子Shell隔离变量用done < file或进程替换
echo -e "a\nb"在 sh 下输出字面\ndash 等 shell 不识别-e精确格式用printf
printf "$var"报未定义格式符变量里的%进入格式串用printf "%s" "$var"
df -Pm输出里的数字带空格字段提取不准awk 'NR==2 {print $4}'后按空格分列
[[ "10" > "5" ]]返回假字典序比较而非数值比较数值比较必须用-gt

如果脚本行为诡异,我的排查顺序一般是:先看变量是否为空、再看引号是否齐全、然后确认管道子Shell是否有数据丢失、最后用bash -x script.sh打开执行追踪,一步步看每条命令的展开结果。配合set -u(变量未定义即报错)和set -o pipefail(管道中任一命令失败即算失败),能把一大部分静默问题直接变成显式报错。

6.4 再扩展一步:应用到批量配置读取场景

这个自检脚本稍微改一下,就能变成处理配置文件的通用模板。比如目标目录列表存在targets.txt里,每行一个路径,逐个执行预检:

while IFS= read -r line; do case "$line" in ""|\#*) continue ;; *) do_check "$line" ;; esac done < targets.txt

continue跳过空行和以#注释的行,这个模式在实际运维脚本里随处可见。用read -r保证转义安全,用 IFS 置空保证字段不被贪吃,再用case做首字符判断——三个特性合在一起,就能稳妥地解析大多数简单配置文件。

从我个人经验来说,这四个命令里最容易被低估的是read,它的“按行读文件 + 子Shell坑 + 多变量拆解”三个能力,几乎能覆盖所有轻量配置解析需求;而printf是最值得从入门阶段就养成的习惯,一旦适应了它的格式思维,再看echo的多平台差异会觉得处处是暗礁。写脚本没有太多高深莫测的技巧,把输入、输出、判断这三件事做扎实,就已经超过一大半的“半桶水”代码了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询