资讯动态

fish-shell 的 umask 内置命令:文件创建模式掩码的查看、设置与符号模式解析

发布时间:2026/10/1 16:55:12 来源:尧图企业网站定制
CLI开发工具【免费下载链接】fish-shellThe user-friendly command line shell.项目地址https://gitcode.com/GitHub_Trending/fi/fish-shell点击查看免费下载导读本文围绕 fish-shell 提供的umask内置命令展开系统讲解文件创建模式掩码umask的八进制与符号两种表达方式、-S/--symbolic、-p/--as-command、-h/--help三个选项的用法并深入其实现umask在 fish 中是一个由进程实时计算的“电子变量”electric variable其设置最终会调用系统umask(2)系统调用符号模式则由 share/functions/umask.fish 中定义的 fish 函数负责解析与换算。读完本文你将掌握 umask 的工作原理、fish 特有的变量模型以及如何通过 tests/checks/umask.fish 中的测试用例验证各种边界行为。什么是 umaskumaskfile creation mode mask文件创建模式掩码用于限制新建文件与目录的默认访问权限。当程序包括 shell 的重定向、touch、mkdir等创建文件时系统会从请求的权限中减去掩码中标记的位从而保证新文件不会意外获得过宽的权限。fish 官方文档明确指出本文档描述的是 fish 内置的umask如果想查看非 fish 版本如 bash 自带的同名校内建命令的手册请使用command man umask见 doc_src/cmds/umask.rst。umask 有两种表达方式八进制数字表示“默认会被移除的权限位”。例如umask 027表示对组移除写权限2、对其他用户移除读写执行全部权限7。符号形式表示“默认会被授予的权限位”即要保留的权限。访问权限读r、写w、执行x以及u/g/o三类主体的完整说明参见chmod(1)手册页。语法与选项umask [OPTIONS] [MASK]不带任何参数时umask以八进制数字形式打印当前文件创建模式掩码。选项说明-S/--symbolic以符号形式如urwx,grx,o而非八进制形式打印掩码-p/--as-command以可重新作为输入使用的命令形式输出如umask 0027-h/--help显示该命令的使用帮助在 share/functions/umask.fish 的实现中这三个选项通过argparse -n umask h/help p/as-command S/symbolic -- $argv解析其中h/help对应-h/--helpp/as-command对应-p/--as-commandS/symbolic对应-S/--symbolic。八进制掩码的语义传入一个八进制数字作为参数时当前 shell 的 umask 会被设置为该值此后新建文件与目录默认会移除该掩码指定的权限。例如umask 177177的每一位含义如下1即二进制001表示移除执行位x7即111表示移除读、写、执行全部权限1属主u移除执行权限7属组g移除全部权限7其他用户o移除全部权限。文档给出的等价示例是umask 177或umask urw二者都将文件创建掩码设置为“属主可读可写其他任何用户没有任何权限”。数值合法性与底层校验八进制掩码不是任意数字都能生效。在 fish 的 Rust 实现中umask是一个可写但值由进程实时计算的电子变量见 src/env/impl/var.rs 中的writable_var(L!(umask), Computed(GET_UMASK))。当用户通过set -g umask ...或umask函数设置它时会触发 src/env/impl/environment.rs 中的set_umaskfn set_umask(list_val: [WString]) - EnvStackSetResult { if list_val.len() ! 1 || list_val[0].is_empty() { return EnvStackSetResult::Invalid; } let Ok(mask) fish_wcstol_radix(list_val[0], 8) else { return EnvStackSetResult::Invalid; }; if mask 0o777 || mask 0 { return EnvStackSetResult::Invalid; } // Do not actually create a umask variable. On env_stack_t::get() it will be calculated. umask(Mode::from_bits(mask as libc::mode_t).unwrap()); EnvStackSetResult::Ok }这里有两处关键校验值必须以 8 为基数解析fish_wcstol_radix(list_val[0], 8)因此umask 228、umask 0282、umask 1234这类包含非法八进制数字的值都会被拒绝解析后的数值必须在0..0o777即 0 到 511范围内超出范围同样返回Invalid。校验通过后fish 会直接调用 nix 封装下的系统调用umask(2)umask(Mode::from_bits(mask as libc::mode_t).unwrap())完成真实掩码的设置并且不会真正创建名为umask的变量——该变量的值始终在读取时动态计算。这一点被 tests/checks/set.fish 的检查项印证set umask abc会得到错误set: Tried to modify the special variable umask to an invalid value。读取掩码电子变量与 GET_UMASK当不带参数执行umask时fish 函数读取$umask变量。这个值并非存储的普通变量而是由GET_UMASK这个 Getter 实时生成的见 src/env/impl/var.rsconst GET_UMASK: Getter |_env| { // note umask() is an absurd API: you call it to set the value and it returns the old // value. Thus we have to call it twice, to reset the value. The env_lock protects // against races. Guess what the umask is; if we guess right we dont need to reset it. let guess Mode::S_IWGRP | Mode::S_IWOTH; let res umask(guess); if res ! guess { umask(res); } EnvVar::new_from_name(L!(umask), sprintf!(0%0.3o, res.bits())) };这段代码值得细读它揭示了 POSIXumask(2)的一个著名“怪癖”调用它设置新值的同时会返回旧值因此查询当前掩码必须先把猜测值设置进去、再恢复原值。fish 的策略是猜测当前掩码为S_IWGRP | S_IWOTH即八进制022组与其他用户均屏蔽写权限调用umask(guess)获得真实旧值res若res ! guess则立即调用umask(res)恢复原值避免竞态窗口内掩码被篡改将res格式化为带前导零的四位八进制字符串sprintf!(0%0.3o, ...)如0027作为变量值返回。因此echo $umask输出的总是形如0027的四位八进制字符串同时读操作本身不会改变掩码。umask因此被 fish 归为“电子变量”electric variable体系它与其他变量一样可读可写但值由进程状态计算而来且写入时会被set_umask拦截并转发给内核。符号掩码的语义如果传入符号掩码则直接指定期望的权限位而不是其反掩码。符号掩码是一个逗号分隔的权利列表每个权利由三部分组成适用对象whou文件属主、g文件属组、o其他用户或a三者全部。操作模式mode将权限设置为新值、在原有基础上追加指定权限、-从原有基础上移除指定权限。权限位permsr、w、x的任意组合分别表示读、写、执行。如果省略第一、二部分则分别默认视为a和。文档给出的示例是r,uw等价于ar,uw含义是所有用户获得读权限文件属主额外获得写权限。注意fish 官方文档明确提示“符号掩码目前并不能按预期工作”Note that symbolic masks currently do not work as intended。原因可以从实现中看出符号模式的解析与换算全部由 fish 函数 share/functions/umask.fish 完成而非内核它与 bash 语义存在已知偏差同时 tests/checks/umask.fish 的注释也承认其期望输出“全部基于 bash 执行相同命令时的行为”逐条手工核对而来。因此生产脚本中若对权限有强要求建议优先使用八进制形式或先用umask -S/ 测试用例验证符号模式在你所用版本上的实际表现。符号模式的解析流程share/functions/umask.fish 中的__fish_umask_parse是符号掩码的核心解析器流程如下八进制捷径先用string match -qr ^0?[0-7]{1,3}$判断输入是否为合法八进制掩码是则补零为四位后直接返回符号校验否则用正则([ugoa]*)([-]?)([rwx]*)校验整个逗号分隔列表并额外拒绝“有 who 无 mode”的非法组合如urw拆分处理把掩码拆成u、g、o三个数字位对每个权利项解析出 scope、mode、perms换算权限值r4、w2、x1与八进制权限位一致通过预置的换算表__fish_umask_add_table、__fish_umask_remove_table、__fish_umask_set_table完成“符号权限 → umask 数字”的映射输出最终以0开头拼接三位数字返回四位八进制字符串。而__fish_umask_print_symbolic则负责反向转换把$umask的每一位数字还原成u...,g...,o...的符号形式这正是umask -S的输出来源。顶层 umask 函数share/functions/umask.fish 定义的顶层umask函数逻辑如下零参数若$umask未设置则默认取113即八进制0113屏蔽属主的执行位以及组、其他用户的全部权限随后根据选项输出——-p/--as-command打印umask $umask-S/--symbolic调用__fish_umask_print_symbolic否则直接打印$umask一个参数调用__fish_umask_parse解析并校验成功后set -g umask $parsed设置全局变量umask进而触发set_umask写入内核失败则打印umask: Invalid mask ...并返回 1多个参数打印umask: Too many arguments并返回 1。默认值113的引入是为了在$umask从未被设置时也能给出一个合理的回退值。完整示例以下示例均基于 tests/checks/umask.fish 中的真实用例整理# 设定基线掩码 027然后查看八进制与符号两种输出 umask 027 umask # 输出 0027 echo umask var $umask # 输出 umask var 0027 umask -S # 输出 urwx,grx,o # 非法掩码会被拒绝并报错 umask 1234 # 报错umask: Invalid mask 1234 umask 228 # 报错umask: Invalid mask 228 umask 0282 # 报错umask: Invalid mask 0282 # 符号模式先清空掩码再追加权限 umask 0 umask u-w,o-x umask # 输出 0021 umask -S # 输出 urw,grwx,orw # 符号模式 直接设定权限 umask 777 umask ur,gw,orw umask -S # 输出 urwx,grwx,orw # 省略 who 与 mode默认 a 与 umask 0 umask rx # 等价于 umask arx umask -S # 输出 urx,grx,orx # 组合操作r,gw,ox,o-r 逐个调整 umask 777 umask r,gw,ox,o-r umask -S # 显式逐位设置 umask urwx,grwx,o umask # 输出 0003组、其他用户均屏蔽写与执行之外的权限 umask urwx,g,orwx umask # 输出 0070关于最后几组输出可对照umask语义推算urwx,grwx,o表示属主与属组完整权限、其他用户无任何权限对应的反掩码为0003仅属主与属组移除执行位1之外的权限——测试文件中的#CHECK注释给出了逐条期望值是理解符号换算的最佳参考。测试验证与已知限制tests/checks/umask.fish 是 fish 官方对 umask 的回归测试集覆盖了基线八进制掩码的输出0027与符号输出urwx,grx,o非法掩码的拒绝路径1234、228、0282大量符号模式的、-、组合及其与 bash 行为的对照Cygwin 下noacl挂载点的特殊处理此时权限由 umask 模拟测试会跳过依赖cut的符号输出分支。测试注释特别说明所有符号模式的期望值都是编写者以 bash 相同命令的行为为基准逐一验证的如果 bash 在这些场景下存在 bugfish 可能同样存在——这也是文档中“符号掩码目前并不能按预期工作”警告的直接来源。此外 tests/checks/set.fish 验证了通过set命令把umask设为非法值时会被拒绝印证了 src/env/impl/environment.rs 中set_umask的校验逻辑。与普通 shell 变量的差异小结方面普通变量umask电子变量存储存在变量栈中不真正存储读取时由GET_UMASK调用umask(2)实时计算写入直接改栈由set_umask拦截校验后调用umask(2)写入内核输出格式原样固定四位八进制如0027默认值无未设置时umask函数回退为113理解这层差异对排查权限问题很有帮助任何子进程包括其他 shell、编辑器、构建工具创建文件时都会继承该掩码而 fish 之所以用电子变量包装是为了在保持$umask可读可写接口的同时确保它与内核状态始终一致。赞分享CLI开发工具【免费下载链接】fish-shellThe user-friendly command line shell.项目地址https://gitcode.com/GitHub_Trending/fi/fish-shell点击查看免费下载相关推荐fish shell ulimit 内置命令完全指南查看与设置进程资源限制fish shell ulimit 内置命令完全指南查看与设置进程资源限制 ulimit 是 fish 内置命令builtin用于读取或修改当前 sheCLI开发工具fish-shell 的 cd 内置命令完全指南目录切换、CDPATH 与符号链接处理fish shell 的 cd 内置命令完全指南目录切换、CDPATH 与符号链接处理 cd 是 fish shell 中切换当前工作目录的内置命令负责更新CLI开发工具fish-shell 的 exit 内置命令退出码语义、source 上下文行为与底层实现解析fish shell 的 exit 内置命令退出码语义、source 上下文行为与底层实现解析 本篇指南以 fish shell 官方文档 exit.rstCLI开发工具上一篇6个步骤掌握Unity-Webview从集成到实战下一篇从零到一用GPT-2快速生成专属AI文本本地跑通仅需5分钟创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑