以观察、判断和行动三个面板呈现here-string 输入检查。

Here-string 提供文本并追加换行

Bash 的 here-string 会把一个词展开后的值送给命令的标准输入,并在末尾追加一个换行符。当变量里已经有少量文本,而目标程序从标准输入读取时,这种写法很方便。它省掉了额外的打印命令,但并不保证输入与变量逐字节相同,也不承诺内部一定使用内存、管道或文件。判断是否适用,应看接收方需要什么输入,而不是只比较命令行长度。 接收命令读取之前先完成展开 三个小于号之后的词可以包含变量展开、算术展开和命令替换,因此不一定是字面常量。单引号保留源码中的字面文本,双引号则允许通常的变量和命令展开。通过变量展开得到的字符串,即使包含美元符号和括号,也不会仅因出现在 here-string 中就被再次作为 shell 命令执行。源码中明确写出的替换语法与变量保存的数据必须区分。 当前 Bash 不会对 here-string 的词执行分词或文件名展开,而是把结果作为一个字符串交付。这里引用变量仍能清楚表达意图,但不能把这种重定向上下文和普通命令参数位置混为一谈。后者使用未引用变量时仍可能按空白拆分,或把星号变成文件名。因此,某个值通过 here-string 正常到达,并不能证明在别处省略引号也安全。 末尾追加换行是无条件发生的。原值没有换行时会多一个,原值已经有尾部换行时还会再多一个。空变量也因此产生包含一个换行字节的输入,而不是立即结束的零字节输入。对于按行处理的工具,这常常符合需求;对于严格长度、签名字节或特定结束字符的协议,则可能直接改变意义,不能把它当作原始字节透传方式。 坏掉的样子 Shell 形成输入后,read 还会按自己的规则解释。默认分词可能移除首尾 IFS 空白,反斜杠也可能作为转义符处理。读取需要保留空格和反斜杠的一行时,应使用 IFS= read -r name。它保护的是这一行的内容;单次 read 仍然在第一个换行处结束,并不会自动把任意多行字符串全部装进变量。输入构造规则与消费者解析规则是两个阶段。 带重定向的内建命令或循环可以在当前 shell 中运行,因此其中的赋值能够在后续代码中继续使用。相比把循环放进管道,这有助于避免 Bash 通常为管道阶段创建子 shell 导致的状态丢失。重定向本身不会包上一层子 shell,但如果表达式的其他地方明确使用子 shell 或命令替换,它们仍然具有通常的作用域与副作用,不能因为用了 here-string 就忽略。 ...

二月 15, 2027 · 1 分钟 · XB Field Notes
以观察、判断和行动三个面板呈现Shell 模式分发。

case 只执行与模式匹配的那一支

Case 语句把一个值依次与一组 shell 模式比较,适合命令分发、有限答案选择或文件名后缀分类。每个分支明确自己处理哪些输入,多个模式也可以共享同一个动作。分支顺序和默认行为都是接口的一部分:输入同时属于多个类别时谁先处理,完全不属于已知类别时是否报错,都需要明确规定。不能只看到成功输入能跑通,就认为整个分发器已经可靠。 模式匹配整个值 Case 使用的是 shell 模式,不是正则表达式。星号匹配任意长度字符串,问号匹配一个字符,方括号描述字符集合或范围。因此,[0-9]* 表示一个数字后面可以跟任意内容,并不表示整串都是数字。若要验证非空十进制文本,可以先拒绝空字符串,以及包含任意非数字字符的字符串,而不必把输入放进算术求值环境。 start|up|run) 这样的多个模式可以共用动作。*.log) 等后缀规则则只是在分类字符串,并不会打开文件或搜索目录。因此,匹配成功不能证明对应文件存在。与普通文件名展开不同,字符串匹配中的斜杠和开头的点也没有相同的特殊排除作用。分类、文件存在性和文件类型是不同判断,真正操作文件前仍应根据需求单独验证。 Bash 会对被测试的词执行文档规定的展开,但不会像普通命令参数那样对它分词或执行文件名展开。因此,这个位置的未引用空变量不会删掉语法上的词,进而把 case 变成语法错误。引用被测试值仍然是清楚的书写习惯。若开启 nounset,则要另外处理未提供的位置参数,或先验证参数数量;引号本身不能让未设置参数变得可用。 实际执行某个分支后,case 返回该分支最后执行命令的状态。因此,匹配成功与动作成功是两件事。下面的实验也让一个已经选中的分支返回六,并检查失败继续向外传播,避免把“找到了正确分支”误报成“分支里的操作已经成功”。 坏掉的样子 使用通常的双分号结束符时,首个匹配分支执行后,case 就结束。精确值应放在宽泛类别前,兜底星号通常放在最后,否则通用选项分支会吞掉后面的精确状态命令。语法检查只能证明结构可解析,不能证明顺序符合业务优先级。应该构造同时匹配两个规则的输入,并断言实际执行哪个动作,才能发现被遮蔽的分支。 如果没有任何模式匹配,case 不执行动作,却仍然返回零。因此,没有默认分支不等于会自动报错。命令分发器若要求拒绝未知命令,就必须在默认分支明确报告并返回失败;可选分类逻辑则可能有意允许不匹配。这个策略来自调用契约,不能假设 case 自带完整输入校验,更不能依赖自动退出选项补足业务含义。 模式上的引号也会改变含义。"*.log" 匹配字面字符,而不引用的写法匹配整个后缀类别。变量提供模式时同样需要区分:它究竟是受控的匹配表达式,还是应该精确比较的普通文本。不要让一个本来只是标识符的值意外获得通配能力,也不要为了“所有东西都加引号”而破坏本来需要的模式语义。 选项和版本还会改变行为。Nocasematch 控制是否忽略大小写;扩展模式要求在相关代码被解析之前开启 extglob。较新 Bash 还提供继续执行或继续匹配的其他分支结束符,它们不同于双分号,也不一定能在旧 shell 上运行。下面实验刻意只使用普通模式和双分号,保持 Bash 三点二兼容语法,本轮实际使用 Bash 五点二运行,避免把可选语法混入基本规则。 快速验证 下面完整代码包含精确命令、别名组、通配类别、空输入错误和未知输入错误。第二个函数验证十进制文本,不会把数字开头但后面含字母的值放过。断言同时检查正常输出与错误码,而不是仅观察整个脚本最后有没有非零退出。 ...

一月 30, 2027 · 2 分钟 · XB Field Notes