Skip to content

Bash Shell:校验标识符并输出编号清单 ​

本篇以Bash 标识符转换为例;前置是Bash 5.2、稳定普通输入文件与 ASCII 行协议。源码与输入与下文配置共用同一份文件,示例未执行。

教学版本族:Bash 5.2 语义,Zsh 5.9 对比;不假设 macOS 自带 Bash 满足 5.2,不把 /bin/sh 当作 Bash。核对日期2026-10-17,不称为2026最新版。示例未执行,配置与命令需按目标环境验收。

完整标识符转换器 ​

Bash Shell 清单示例未执行。需要已有 Bash5.2;无外部依赖。参数恰为一个可读普通文件,每行限1至32个 ASCII 字母、数字、下划线或连字符;总行数最多1000。成功输出带序号的 TSV,不创建、删除或改名任何文件;失败不输出成功记录。它是语言练习,不是系统文件扫描器。

完整源码和输入文件 ​

Bash Shell 清单转换器采用数组先缓存有效行,全部输入校验完成后才输出。IFS= read -r 避免反斜杠和前后空白被改写;末行没有换行仍处理。底层读取错误与EOF不能完全区分,文件变更与不可信同用户并发不是此教学脚本的安全保证,因此要求输入是自己拥有的稳定小文件,拒绝链接,不用于特权任务。

bash
#!/usr/bin/env bash
# Bash 5.2; no external commands; not executed by the documentation build.
set -u
export LC_ALL=C
if (( $# != 1 )); then
  printf '%s\n' 'usage: labels.bash INPUT' >&2
  exit 2
fi
if [[ ! -f "$1" || ! -r "$1" || -L "$1" ]]; then
  printf '%s\n' 'input must be a readable regular non-link file' >&2
  exit 1
fi
# Bash read otherwise skips NUL bytes; reject them before line parsing.
if IFS= read -r -d '' nul_probe < "$1"; then
  printf '%s\n' 'NUL bytes are not valid text input' >&2
  exit 2
fi
labels=()
line=''
index=0
while IFS= read -r line || [[ -n "$line" ]]; do
  (( index += 1 ))
  if (( index > 1000 )) || [[ ! "$line" =~ ^[A-Za-z0-9_-]{1,32}$ ]]; then
    printf 'invalid record at line %d\n' "$index" >&2
    exit 2
  fi
  labels+=("$line")
done < "$1"
if (( ${#labels[@]} == 0 )); then
  printf '%s\n' 'empty input' >&2
  exit 2
fi
for (( index=0; index<${#labels[@]}; index++ )); do
  if ! printf '%d\t%s\n' "$((index+1))" "${labels[index]}"; then
    exit 1
  fi
done
text
alpha
beta_2
text
alpha
../outside
text
# Existing GNU Bash 5.2. Builtins only, LC_ALL=C.
# Not /bin/sh, not Zsh; system tools are not required.

完整项目位于仓库 examples/m5-next-shell/;README给出全部未来自跑命令与精确清理步骤,正文包含全部源码和输入。

markdown
# Bash 标识符转换器(未执行)

完整文件:labels.bash、good.txt、bad.txt、requirements.txt、本说明。已有Bash5.2,无包安装和外部命令依赖;macOS自带旧Bash不视为合格,Zsh不能替代。Git Bash在本环境初始化失败,未做语法解析或执行。

输入是自己拥有的稳定普通非链接小文件,父目录不可被其他人改写;每行1–32个ASCII字母数字下划线或连字符,1–1000行。不是任意路径列表,CRLF和NUL字节会被拒绝。NUL预扫描先于行解析,会读完整文件,因此“受控小文件”是前置条件,行数上限并非任意大文件内存防护。示例不修改输入、不创建文件、无服务。

未来隔离自跑命令(本轮严禁执行):

```bash
bash ./labels.bash ./good.txt
bash ./labels.bash ./bad.txt
```

成功预期TSV两行,编号1对应alpha、2对应beta_2;退出0。bad.txt第2行非法,退出2、stdout为空。空文件、行数超限、多余参数退出2;不可读/非普通/链接输入退出1。输出设备失败只约定非零:若printf返回错误且脚本到达显式失败分支,则退出1;若进程因SIGPIPE终止,Bash按128+信号编号表示,常见平台可能为141。本例未捕获SIGPIPE,不能保证固定码1。输出可能已写出部分字节,调用者不能只看stdout。

本例要求稳定小文件;read的底层错误与正常EOF在此简化接口里并不能全部区分,不用于损坏介质、并发改写或特权输入;需要可靠I/O诊断时使用有异常读接口的语言。本例不承诺抵抗恶意同身份替换。

恢复只需修正或换回输入副本后重试;若自行重定向,先保留错误输出与退出码,不能把半成品当完成。清理仅删除自己复制的示例输入和保存的输出,不删除来源文件、不递归操作任意路径。

成功和拒绝预期 ​

Bash Shell 清单预期对 alpha、beta_2 输出编号1和2。bad.txt 第二行包含斜杠,预期退出2且 stdout 为空;多余参数也退出2。不可读输入退出1,printf检测到输出错误并执行显式失败分支时退出1;若因SIGPIPE终止,则可能按128+信号编号返回,例如常见平台为141。因此stdout管道提前关闭只约定非零,不承诺固定为1。行数上限控制内存,不能用大文件测试替代协议设计。README 提供未来自跑命令。

转换器练习与解答 ​

题目:若把数组改成读取一行输出一行,非法末行发生时调用者拿到什么?

参考解答:调用者可能已写入部分 TSV,单看输出无法确认完整成功;必须同时看退出码并使用临时文件提交。当前示例通过先验证后输出减少输入失败的半成品,但输出设备失败仍可能留下部分字节。

核对资料 ​

退出状态核对:GNU Bash Exit Status说明致命信号以128+信号编号表示;本例未捕获SIGPIPE,不承诺管道提前关闭固定退出1。

继续阅读