Bash 历史保留下来的可搜索命令列表

bash history 是可以搜索的命令日志

命令历史能减少重复输入,却不是所有已执行操作的完整记录。Bash 通常在交互 Shell 中启用历史功能,配置还可以过滤条目或限制保留数量。运行中 Shell 的可搜索列表,与磁盘上的历史文件,是两个独立状态。理解这个区别,才能解释为什么某条命令在一个终端能找到,在另一个终端却没有,以及为什么意外结束会话可能丢掉尚未保存的记录。 会话内存与历史文件不同 HISTSIZE 限制内存中的历史条目数量,HISTFILESIZE 限制保存文件的行数。多行命令与时间戳记录会让行数不等于命令数,不能用简单的文件行数判断究竟保留了多少次操作。HISTFILE 决定保存位置,通常指向用户的 Bash 历史文件。变量未设置或为空时不会正常保存;目标不可写时,当前会话仍可能查到记录,却没有对应的持久化副本。 Bash 在参数展开之前、历史展开之后记录命令文本,并受到 HISTCONTROL 与 HISTIGNORE 的影响。因此,保存下来的变量引用并不一定显示原来执行时的变量值。当前目录、别名、函数、环境及外部文件,也都可能在下次复用命令之前发生变化。历史保存的是有用的输入,不是完整的可复现运行环境,更不是可以直接信任的操作审计证据。 追加保存不会自动刷新其他终端 Bash 正常退出时保存历史,histappend 决定追加到文件还是覆盖文件。如果此前没有主动保存,异常终止可能丢掉全部未写入的条目,不只是最后一条。history -a 追加当前会话新输入、且尚未追加过的历史行。提前保存可以缩小丢失窗口,但不等于承诺任何系统故障之后都能完整恢复,更不能把一次文件写入等同于业务日志的可靠持久化。 另一个仍在运行的 Shell 持有自己的列表,需要通过 history -n 等操作读取新追加的行。仅仅启用追加选项,不会让另一个终端的搜索结果实时更新。history -r 把整个文件内容追加到现有列表,重复运行可能制造重复条目;history -w 则用当前列表替换文件,可能丢掉其他会话留下的记录。这些命令的方向和覆盖语义不同,不能当作几种等价的同步写法。 如果把追加和导入接入 PROMPT_COMMAND,还要保留已有提示符钩子,以及它们对上一条命令退出状态的使用。直接覆盖已有钩子可能破坏其他 Shell 行为。多个会话共享一个文件,也不会因此形成可信的全局执行顺序。需要顺序、退出码或责任归属时,应使用独立的操作日志,而不是从交错的历史文本中反推完整事实。 搜索后先检查,再执行 在常见的 Emacs 风格 Readline 绑定中,Ctrl-R 向后增量搜索,再按一次查找更早的匹配。应使用编辑按键退出搜索,先检查所选命令,再决定是否执行;Ctrl-G 可以取消搜索。回车会接受命令,Ctrl-J 也是换行,不能把它说成不会执行的预览键。其他编辑模式或自定义终端的绑定可能不同,应以当前配置为准。 ...

二月 28, 2028 · 1 分钟 · XB Field Notes
shell 在目标程序运行前把文件名模式展开。

Bash 的通配符在命令执行前就展开

Bash 命令中的通配符通常会在目标程序启动前变成一组路径。它让批量处理很方便,也让同一行命令受到当前目录和 shell 选项的影响。程序拿到的是已经形成的参数,不会收到标记来告诉它哪些参数来自星号。因此,写命令时应先确定:希望传入实际匹配的文件,还是希望把模式本身交给另一个程序解释。两者需要不同的引号写法,不能依靠目标程序猜测意图。 先匹配,再保留参数边界 Bash 先进行分词,再进行文件名展开。没有引用的星号、问号和方括号模式可能因此生成多个参数。但匹配到的文件名即使包含空格,也仍然是一个参数,因为文件名展开之后不会再执行一次分词。常见问题发生在下一步:循环拿到了完整路径,却用没有引号的变量把它传给另一个命令。循环体中的 "$file" 仍然必要,不能因为前一步通配成功就省掉。 如果接收方需要模式,就应引用模式。例如 find . -name '*.txt' 将匹配规则交给 find;去掉引号后,Bash 会先在当前目录展开它,find 得到的可能是多个文件名,已经不是原来要表达的条件。变量里包含星号时也一样:引用变量会传递字面星号,不引用则可能触发文件名展开。引号只控制当前 shell 如何形成参数,不会禁止接收方按自己的语法解释收到的文本。 通配符匹配目录项,并不保证结果都是普通文件。名为 archive.txt 的目录也能匹配 *.txt,只想处理普通文件时还需要判断类型。普通星号不会越过斜杠,默认也不匹配名字开头的点;需要隐藏文件时,应明确使用相应模式或开启 dotglob。遍历整棵目录树是另一个需求,不能把当前目录的一次展开误认为递归搜索。 环境变量也能改变结果集合。Bash 中非空的 GLOBIGNORE 会过滤匹配结果,并且产生开启 dotglob 的副作用。因此,从交互会话继承状态的脚本可能意外纳入隐藏文件。下面的实验会清除这个变量,并明确指定相关选项,让断言对应一套可重复的环境,而不是依赖读者终端的偶然配置。 坏掉的样子 默认情况下,没有匹配结果的模式会保留为原文。因此,遍历 *.png 的循环可能仍然运行一次,变量内容就是一个并不存在的星号文件名。开启 nullglob 后,未匹配的模式会直接消失,但这也可能让接收方变成零文件参数调用。某些程序此时等待标准输入,另一些程序则使用默认目标。未定义变量检查和管道失败检查都不能证明通配符至少找到了一个文件。 对于允许为空的输入,可以在开启 nullglob 后先收集到 Bash 数组,检查元素数量,再决定是否调用消费者。对于必须存在的输入,failglob 会在未匹配时报告展开错误,并阻止该命令执行。如果只想临时改变行为,把选项修改放在子 shell 中,避免影响调用者的后续命令。交互终端的启动文件可能已经修改选项,所以终端上一次成功不能替代脚本自己的前置条件。 ...

九月 8, 2027 · 1 分钟 · XB Field Notes