fish-shell `status` 内置命令完全指南:查询运行时信息、控制作业与终端能力

原创2026-09-30 21:21:09656 阅读
文章标签:CLI开发工具

fish-shell status 内置命令完全指南:查询运行时信息、控制作业与终端能力

status 是 fish-shell 中一个用途广泛的内置命令(builtin),用于查询当前 shell 的运行时状态:是否登录 shell、是否交互式、是否处于命令替换或代码块中、当前运行的脚本路径与行号、调用栈、作业控制模式、编译期嵌入资源,以及终端与本地化(language)信息。本文基于 status.rst 文档,并结合 src/builtins/status.rs 的实现与 tests/checks/status.fish 测试用例,全面讲解每一个子命令的语义、返回值、使用场景与底层原理,让你能在脚本编写、调试与配置中熟练运用 status。

一、命令总览与基本用法

status 在无参数调用时,会打印当前 shell 的登录状态和作业控制(job control)状态摘要,即:

This is a login shell
Job control: Only on interactive jobs

从实现上看(status.rs),无子命令时输出由三部分组成:登录状态(通过 get_login() 判断)、作业控制模式(通过 get_job_control_mode() 获取,取值映射为 "Only on interactive jobs"(interactive)、"Never"(none)、"Always"(full)三种文本),以及当前解析器的调用栈(parser.stack_trace())。

除此之外,status 的绝大多数用法都需要指定一个子命令(subcommand)。完整的子命令清单如下(对应文档 Synopsis 与源码中 str_enum! 宏声明的 StatusCmd 枚举):

status
status is-login
status is-interactive
status is-interactive-read
status is-block
status is-breakpoint
status is-command-substitution
status is-no-job-control
status is-full-job-control
status is-interactive-job-control
status current-command
status current-commandline
status filename
status basename
status dirname
status fish-path
status function
status line-number
status stack-trace
status job-control CONTROL_TYPE
status features
status test-feature FEATURE
status build-info
status get-file FILE
status list-files [PATH ...]
status terminal
status terminal-os
status test-terminal-feature FEATURE
status language [list-available|set [LANGUAGE ...]|unset]

有两点使用约束需要特别注意(文档 Notes 部分):

  1. 同一词法层面只允许一个子命令:源码中通过 Options::try_set_status_cmd 实现互斥检查,若重复指定会报错 "options cannot be used together"。测试用例 status.fish 验证了 status --is-interactive --is-login 与 status -b is-interactive 都会触发该错误。
  2. 子命令可以写成短/长选项形式(旧式兼容):例如 status --is-login 等价于 status is-login,status -l 同样等价。这些标志形式已被标记为废弃(deprecated),未来版本可能移除。源码中 LONG_OPTIONS 表 定义了这些兼容别名,短选项字符串为 L:cbilfnhj:t,其中 -c(is-command-substitution)、-b(is-block)、-i(is-interactive)、-l(is-login)、-f(current-filename)、-n(current-line-number)、-t(print-stack-trace)、-j(job-control)、-L(level)。

二、is-* 系列:运行时状态断言(返回码语义)

is-* 系列子命令的核心价值在于以退出码形式断言 shell 的当前执行环境,因此天然适合写在条件判断中。返回值约定为:条件成立返回 0,不成立返回 1;参数错误时返回相应的非零错误码。

is-login / --is-login / -l

返回 0 表示 fish 是登录 shell(login shell),即它应当执行登录相关任务(如设置 PATH)。源码判断直接调用 get_login()(status.rs)。

is-interactive / --is-interactive / -i

返回 0 表示 fish 处于交互模式,即连接着键盘。源码判断调用 is_interactive_session()(status.rs)。这是脚本判断"当前是否有人在终端里操作"的最常用手段。

is-interactive-read / --is-interactive-read

返回 0 表示 fish 正在执行一个连接到键盘的交互式 read 内置命令,源码通过 reader_in_interactive_read() 判断(status.rs)。相关文档见 read.rst。

is-block / --is-block / -b

返回 0 表示 fish 当前正在执行一个代码块(如 begin/end、for 循环等),源码判断调用 parser.is_block()。测试用例 status.fish 验证了在顶层 status -b 为假、在 begin ... end 块内为真。

is-breakpoint

返回 0 表示 fish 正处于 breakpoint 命令触发的中断提示符上下文中。相关文档见 breakpoint.rst 与 fish_breakpoint_prompt.rst,源码判断调用 parser.is_breakpoint()。

is-command-substitution / --is-command-substitution / -c

返回 0 表示 fish 当前正在执行一个命令替换(command substitution),例如 (command) 中的子进程。源码通过 parser.scope().is_subshell 判断(status.rs)。

is-no-job-control / is-full-job-control / is-interactive-job-control

这三个子命令分别断言当前作业控制模式为 none、full、interactive,源码中对应与 JobControl::None、JobControl::All、JobControl::Interactive 的比较(status.rs)。它们常配合 status job-control 一起用于检测和设置作业控制策略。

三、信息查询类:脚本路径、行号、调用栈与当前命令

这类子命令主要用于自省:脚本如何被调用、当前执行到哪一行、调用链是怎样的。

current-command

打印当前正在运行的函数或命令的名称,替代已被废弃的 _ 变量。源码读取 parser.libdata().status_vars.command,若为空则回退到程序名(get_program_name(),通常为 "fish")(status.rs)。测试 status.fish 验证其输出为 fish。

current-commandline

打印当前正在执行的完整命令行,包含所有作业与操作符(例如 echo a; echo b 的全部内容)。源码读取 status_vars.commandline(status.rs)。

filename / current-filename / -f / --current-filename

打印当前运行脚本的文件名。注意以下细节:

  • 如果脚本是通过**符号链接(symlink)**调用的,返回的是链接本身的路径;
  • 如果脚本是通过管道喂给 source(如 cat file | source)执行的,返回 -;
  • 对应实现见 source.rst。

源码从 parser.current_filename() 获取(status.rs)。

basename 与 dirname

  • basename:只打印脚本文件名,去掉所有路径前缀;
  • dirname:只打印脚本所在路径,不含文件名本身。结果可能相对于 PWD(甚至就是 .),取决于脚本被调用的方式。这与把文件名传给 dirname(3) 等价,非常适合"读取当前脚本目录下的其他文件"这类需求。

源码中这两个子命令在 filename 结果之上调用 wdirname / wbasename(status.rs)。

fish-path

打印当前正在执行的 fish 实例的绝对路径。这是"尽力而为"的结果,具体输出取决于平台能提供给 fish 的信息;某些情况下可能只能得到 "fish"。源码优先使用 get_fish_path() 得到绝对路径后经 wrealpath 解析,失败则回退(status.rs)。

function / current-function

打印当前被调用函数的名称;如果不在任何函数中,则打印 "Not a function"(或对应语言的翻译字符串)。源码通过 parser.get_function_name(opts.level) 获取(status.rs)。测试 status.fish 验证了顶层调用输出 "Not a function"、函数内调用输出函数名、eval 调用函数也能正确识别。

line-number / current-line-number / -n / --current-line-number

打印当前运行脚本的行号。源码调用 parser.lineno_for_display()(status.rs)。注意:源码中保留了一个 TODO,讨论 -L level 参数如何解释(见 issue #4161 注释),当前行号打印未使用 level。

stack-trace / print-stack-trace / -t / --print-stack-trace

打印调用栈上所有函数调用的堆栈追踪。源码直接输出 parser.stack_trace()(status.rs)。测试 status.fish 验证了多层函数调用(包括 functions -c 复制出的函数)都能正确输出 in function '...' 与 called on line N of file .../status.fish 格式的追踪信息。

四、status job-control:切换作业控制模式

status job-control CONTROL_TYPE 用于设置作业控制类型,CONTROL_TYPE 取值三种:

取值 含义(源码 JobControl 枚举)
none 不进行作业控制(JobControl::None)
full 始终进行作业控制(JobControl::All)
interactive 仅对交互式作业进行控制(JobControl::Interactive)

源码通过 set_job_control_mode(job_control_mode) 应用设置,非法值会报 Invalid job control mode '...' 错误(status.rs)。测试用例 status.fish 验证了 status job-control full1、status --job-control=1none 均报错,而 status job-control none 可正常执行。

该子命令与 status 无参数输出中的 "Job control" 行相对应,是调试后台作业、管道进程组行为(pipeline pgroup)问题时的有力工具。

五、feature flags:特性开关的查询与检测

fish 使用**功能特性开关(feature flags)**来分阶段引入可能破坏脚本的破坏性变更:先以 opt-in 引入,数个版本后改为 opt-out(默认开启),最终移除旧行为。

status features:列出全部特性开关

status features 打印所有可用的特性及其当前状态。输出格式为 名称 状态 引入版本 说明,例如(来自 language.rst):

stderr-nocaret          on  3.0 ^ no longer redirects stderr
qmark-noglob            on  3.0 ? no longer globs
regex-easyesc           on  3.1 string replace -r needs fewer \'s
ampersand-nobg-in-token on  3.4 & only backgrounds if followed by a separating character
remove-percent-self     off 4.0 %self is no longer expanded (use $fish_pid)
test-require-arg        off 4.0 builtin test requires an argument
mark-prompt             on  4.0 write OSC 133 prompt markers to the terminal
ignore-terminfo         on  4.1 do not look up $TERM in terminfo database
query-term              on  4.1 query the TTY to enable extra functionality
omit-term-workarounds   off 4.3 skip workarounds for incompatible terminals

实现上,print_features 遍历 features::METADATA,通过 feature_test(md.flag) 判断每个开关是 on 还是 off,并按名称对齐打印(status.rs)。

status test-feature FEATURE:检测单个特性

status test-feature FEATURE 的返回值语义非常明确:

  • 0:该特性已启用(on);
  • 1:该特性已禁用(off);
  • 2:该特性名无法识别。

源码中通过 features::METADATA.iter().find(...) 查找匹配项,未找到直接 Err(2),找到则按 feature_test 结果返回 0 或 1(status.rs)。测试 status.fish 验证了 status test-feature stderr-nocaret 返回 0、status test-feature not-a-feature 返回 2。

特性开关的启用方式

除查询外,特性开关本身可在启动时通过 fish --features 按会话启用(见 fish.rst),或通过通用变量 fish_features 全局启用(language.rst):

# 按会话启用
fish --features qmark-noglob,regex-easyesc

# 全局启用(必须是 universal 或 exported 变量才会生效)
set -U fish_features regex-easyesc qmark-noglob

# 用 no- 前缀关闭某个特性
set -Ua fish_features no-qmark-noglob

版本号可作为分组使用,如 3.0 等价于 "stderr-nocaret" 和 "qmark-noglob";特殊组 all 启用全部特性。

六、build-info、get-file 与 list-files:编译期信息与内嵌资源

status build-info

打印 fish 的构建信息:构建系统(CMake 或 Cargo)、版本号、目标平台三元组(target/host)、构建 profile(如 debug/release)以及编译特性(如 embed-manpages、localize-messages、crt-static)。这主要服务于调试。源码分别读取 BUILD_VERSION、BUILD_TARGET_TRIPLE、BUILD_HOST_TRIPLE、BUILD_PROFILE 等编译期常量(status.rs)。测试 status.fish 验证了该子命令不接受额外参数。

status get-file FILE

打印编译时嵌入 fish 二进制的文件内容,包括默认的函数与补全集合、man 手册页和主题。哪些文件被嵌入取决于构建设置:

  • 若构建启用了 embed-manpages,man 页会通过 Docs 结构体(rust_embed,来自 $FISH_RESOLVED_BUILD_DIR/fish-docs/man/man1)嵌入;
  • CMake 构建下,$FISH_CMAKE_BINARY_DIR/share 中的 __fish_build_paths.fish 通过 CMakeBinaryDir 嵌入;
  • 默认函数与补全来自 crate::autoload::Asset。

实现依次尝试上述三个来源(status.rs)。返回值:文件被包含时返回 0,否则返回 1。

status list-files [PATH ...]

列出编译时嵌入 fish 二进制的所有文件路径,仅显示路径以可选参数 PATH 开头的条目;不带参数时列出全部。返回 0 表示有输出,1 表示没有匹配。源码对三个嵌入来源做 iter() 合并、按 PATH 前缀过滤后排序输出(status.rs)。

这两个子命令在排查"某个补全/函数/手册页为什么没有生效"时非常有用——可以直接确认资源是否真的被打进了二进制。

七、terminal 系列:终端能力探测

status terminal

打印 fish 运行所在的终端名称与版本,例如通过 XTVERSION(\e<a href="https://link.gitcode.com/i/670b6b69e26f74a814f74900bc549a7b" target="_blank"> Ps q 转义序列)向终端查询所得(见 [terminal-compatibility.rst)。需要注意的是:该信息在早期启动阶段不可用,只有从第一个交互提示符显示时起才可用(可能是通过内置 read 触发的),即早于第一个 fish_prompt 或 fish_read 事件。源码直接读取 xtversion()(status.rs)。

status terminal-os

打印终端所在操作系统名称,通过 XTGETTCAP query-os-name 查询(见 terminal-compatibility.rst)。与 status terminal 一样,只有第一个交互提示符显示后才可用;OS 名称不可用时返回 1。源码读取全局状态 TERMINAL_OS_NAME(status.rs)。

status test-terminal-feature FEATURE

返回 0 表示终端经探测支持给定特性;与 status terminal 一样仅在第一交互提示符之后可用;传入无法识别的特性名会打印错误。目前唯一可用的 FEATURE 是 scroll-content-up,对应终端支持 ECMA-48 的 SCROLL UP / SU 能力(terminfo 中的 indn,转义序列 \e<a href="https://link.gitcode.com/i/26b3e9d843f556e70061683e33250f71" target="_blank"> Ps S)。当 fish 检测到该支持时,Ctrl-L 绑定就可以使用 scrollback-push 特殊输入函数(详见 [terminal-compatibility.rst)。

源码实现很直接:特性名必须恰好等于 "scroll-content-up",随后用 get_scroll_content_up_capability() == Some(true) 判断(status.rs)。测试 status.fish 验证了缺参、多参、未知特性名都会报错,而在没有 TTY 的环境下 scroll-content-up 必然失败。

需要注意的是,终端查询能力本身受 query-term 特性开关控制:该开关开启时 fish 才向终端写入转义序列并读取响应;若终端不兼容,可临时执行 set -Ua fish_features no-query-term 关闭(language.rst)。

八、status language:消息本地化语言管理

status language 用于显示或修改 fish 消息的本地化(翻译)语言设置。该功能只在构建时启用了 localize-messages 特性时可用,否则会输出 "fish was built with the localize-messages feature disabled. The status language command is unavailable."(status.rs,测试见 status.fish)。

无参数调用:显示当前语言设置

无参数时打印当前生效的语言及其来源,格式类似:

Active languages (source: default):
  Fluent: de
  gettext: de

"source" 可能是 default、某个环境变量(如 LANGUAGE、LC_ALL),或 `status language set` 命令。实现来自 LocalizationState,同时维护 Fluent 与 gettext 两套翻译目录的语言优先级(settings.rs)。

status language list-available

打印 fish 拥有翻译的全部语言名称,这些名称可用于 set 子命令。实现将 Fluent 与 gettext 的可用语言合并、去重、排序后输出(settings.rs)。本仓库中实际的翻译文件位于 localization/fluent(如 zh_CN.ftl、de.ftl)与 localization/po(如 zh_CN.po、de.po)。

status language set [LANGUAGE ...]

设置 fish 自身消息的语言优先级,覆盖通过环境变量配置的语言,但只作用于 fish 本身,不传递给任何子进程。参数取 list-available 所列语言名,可传多个:翻译按列表顺序取第一个有对应翻译的语言。

例如 status language set pt_BR fr 之后:

  • 所有有巴西葡萄牙语翻译的消息以该语言显示;
  • 剩余消息中,有法语翻译的以法语显示;
  • 若所列语言都没有翻译,则回退到英文。

源码在 update_from_status_language_builtin 中实现:先过滤出真实存在的语言,按输入顺序去重后分别设置 Fluent 与 gettext 的优先级,并把 precedence_origin 标记为 StatusLanguage,同时会对重复输入(duplicates)与不存在的语言(non_existing)给出 lint 提示(settings.rs)。

status language unset

撤销 set 的效果,语言设置重新从环境变量获取。源码将 precedence_origin 重置为 Default,并重新按环境变量计算(settings.rs)。

与环境变量的关系

status language 覆盖的是 language.rst 中 "Locale Variables" 一节描述的本地化变量机制。不调用 status language 时,fish 按如下顺序决定语言(settings.rs):

  1. 若优先级由 status language 设置过,忽略环境变量;
  2. 依次取 LC_ALL、LC_MESSAGES、LANG 的第一个非空值,若以 C 或 POSIX 开头则视为 C locale 并禁用本地化;
  3. 否则使用 LANGUAGE 环境变量(可含多个语言,冒号分隔、靠前的优先,如 LANGUAGE=zh_TW:zh_CN:pt_BR);
  4. 否则使用 LC_ALL/LC_MESSAGES/LANG 的单语言值,并剥离编码后缀只保留 ll_CC 部分;
  5. 否则不做本地化。

九、综合实战:把 status 用起来

场景一:脚本判断执行环境

if status is-interactive
    # 交互式终端下才执行的初始化
    alias ll 'ls -l'
end

if status is-login
    # 登录 shell 才执行的 PATH 配置
    fish_add_path /opt/tools/bin
end

if status is-command-substitution
    # 被放在 ( ) 中执行时避免递归
    echo "in cmdsub"
end

场景二:定位脚本自身资源

# 读取与当前脚本同目录的配置文件
set -l dir (status dirname)
source "$dir/config.fish"

# 打印当前执行行号,便于日志定位
echo "Reached line "(status line-number)

场景三:在函数中获取调用栈

function my-dump-stack
    status stack-trace
end

与 status current-function 配合,可以在函数库中实现精确的错误上报。

场景四:按终端能力定制行为

# 仅当终端支持 scroll-content-up 时才绑定特殊功能
if status test-terminal-feature scroll-content-up
    bind ctrl-l scrollback-push
end

十、小结:status 是 fish 脚本的"运行时体检仪"

status 内置命令覆盖了 shell 运行时信息的方方面面:环境断言(is-* 系列)、脚本自省(filename/basename/dirname/line-number/stack-trace)、作业控制(job-control 与 is-*-job-control)、特性开关(features/test-feature)、编译信息与内嵌资源(build-info/get-file/list-files)、终端探测(terminal/terminal-os/test-terminal-feature)以及消息本地化(language)。由于每个查询子命令都遵循清晰的返回码语义,它们可以无缝嵌入 if、and、or 等条件结构,是编写健壮、可移植 fish 脚本与排查环境问题的基础设施。其完整文档与实现、测试分别位于 status.rst、status.rs 与 status.fish,需要更深入了解时可直接查阅。

登录后查看全文
fish-shell