记对 xonsh shell 的利用, 脚本编写, 迁移及调优

[复制链接]
发表于 2026-4-13 10:42:12 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
xonsh 是 python 驱动的 shell, 在操纵服从, 交互和外部功能的先辈性上比 bash 等精良, 而且非常轻易上手. 但相应地, 它是一个新兴的 shell, 而且不是所谓 "OSIX Shell"(只管某些活动比力相似), 以是仍需做一些相识.
xonsh 的提示符为 @, 由于它读作 "consh", 脚本文件扩展名为 .xsh, 官方界说的 markdown 代码块标签也是 xsh
克制本文末了更新, xonsh 的最高版本是 0.22.8
特别语法

Shell 与 Python 的集成是 xonsh 易用性的根源, xonsh 向下直接兼容多数 python 脚本语法, 而且有提供独特语法以便将 python 和 shell 集成
下令表明与机制相干

引用变量和对象

在 xonsh 中, 你可以直接利用 print(x) 输出一个变量和对象, 但若想将它作为 touch 下令的参数呢? xonsh 能带你告别 bash 貌寝的字符串拼接和 for 语法.
xonsh 利用 @() 操纵符在 shell 环境中引用一个变量, 它会自动对对象举行 str() 而不是 repr() 操纵, 可以通过下列代码验证
  1. tmp @ text = "Hello\nWorld"
  2. tmp @ repr(text)
  3. "'Hello\\nWorld'"
  4. tmp @ str(text)
  5. 'Hello\nWorld'
  6. tmp @ touch @(text)
  7. tmp @ ls
  8. 'Hello'$'\n''World' # 文件名中真的有换行
  9. tmp @ # 如果是 repr(), 就会创建 'Hello\nWorld' (无转义)
复制代码
因此你可以如许创建一组数字序号文件
  1. tmp @ for i in range(30):
  2.            touch @(i)
  3.    
  4. tmp @ ls
  5. 0  2  4  6  8  10  12  14  16  18  20  22  24  26  28
  6. 1  3  5  7  9  11  13  15  17  19  21  23  25  27  29
  7. tmp @
复制代码
大概方便地补0
  1. tmp @ for i in range(30):
  2.            touch @(str(i).zfill(2))
  3. tmp @ ls
  4. 00  02  04  06  08  10  12  14  16  18  20  22  24  26  28
  5. 01  03  05  07  09  11  13  15  17  19  21  23  25  27  29
复制代码
同样的简单操纵若用 bash 举行, 须要:
  1. for i in {0..29}; do touch $(printf "%02d" $i); done
复制代码
  1. # 避免命令替换的子 shell 开销
  2. for i in {0..29}; do
  3.     printf -v name "%02d" "$i"
  4.     touch "$name"
  5. done
复制代码
python 尚有 bash 无法提供的一系列支持库与工具, 可处理处罚多种格式文件而无需引用外部步伐; 而且处理处罚含有奇葩字符的文件名, xonsh 可以利用字符串的 repr 集成到下令中很难炸掉
不外, 同样的操纵若用纯 python 脚本/REPL举行, 须要:
  1. from pathlib import Path
  2. for i in range(0, 30):
  3.     name = str(i).zfill(2)
  4.     path = Path(name)
  5.     path.touch()
复制代码
这两种对比足以体现 xonsh 的精良性
别名与 aliases

在 xonsh 中, 别名被存储在全局的 aliases -- 一个兼容字典 API 的 xonsh.aliases.Aliases 对象中.
像如许设置一个别名:
  1. ~ @ aliases['la'] = 'ls -a'
复制代码
字符串情势创建的别名将自动转换为列表, 也即对象的智能性:
  1. ~ @ aliases['la'] = 'ls -a'
  2. ~ @ aliases['la']
  3. ['ls', '-a']
复制代码
若要取消一个别名, 只需:
  1. ~ @ del aliases['la']
  2. ~ @ la
  3. xonsh: subprocess mode: command not found: 'la'
复制代码
别的, 扩展 xontrib-abbrevs (须要安装 xontrib-abbrevs 包) 还提供了一套独立的, 输入后会自动睁开的类 alias 体系, 存放于全局的 abbrevs 字典对象中
  1. ~ @ abbrevs['ll'] = 'ls -l'
  2. ~ @ ls -l # 输入 ll 回车或空格后自动补全
  3. 总计 36
  4. drwxr-x---  4 pluv wheel 4096  4月11日 12:25 Desktop
  5. drwxr-x--- 10 pluv wheel 4096  4月 8日 00:18 Documents
  6. drwxr-x---  5 pluv wheel 4096  4月12日 09:03 Downloads
  7. drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Music
  8. drwxr-x---  5 pluv wheel 4096  3月26日 09:04 Pictures
  9. drwxr-x--- 10 pluv wheel 4096  4月 9日 19:39 Programs
  10. drwxr-x---  2 pluv wheel 4096  3月10日 21:58 Public
  11. drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Templates
  12. drwxr-x---  3 pluv wheel 4096  2月18日 21:18 Videos
  13. ~ @
复制代码
路径匹配, 正则表达式和反引号

xonsh 和大多数 shell 一样, 默认利用 glob 匹配路径, 比方:
  1. ~ @ ls
  2. Desktop  Documents  Downloads  Music  Pictures  Programs  Public  Templates  Videos
  3. ~ @ echo D*
  4. Desktop Documents Downloads
  5. ~ @
复制代码
在 xonsh 中, `` 不像在bash中一样代表历程更换. 思量到反引号自己在 python3 中已经失去了作用, 于是 xonsh 的开发者非常智慧地给反引号分配了正则路径匹配功能, 无需用户去导入并繁琐地利用 re 和 pathlib 模块
如许你就可以用反引号括住正则表达式以对路径举行正则匹配
比方:
  1. ~ @ touch report2023.txt
  2. ~ @ ls `report.*\.txt`
  3. report2023.txt
复制代码
同理:
  1. # 匹配 /etc 目录下所有 .conf 结尾的文件
  2. ls `/etc/.*\.conf`
  3. # 匹配 /tmp 目录下任意深度的 .log 文件
  4. ls `/tmp/.*\.log`
  5. # 匹配家目录下所有 .py 文件
  6. ls `~/.*\.py`
复制代码
会话对象

xonsh 利用 @ 指代当前会话, 其为一个 XonshSessionInterface 对象, 具有以下公有属性:
@.env 是一个 xonsh.environ.Env 对象, 兼容字典 API, 用于存储或设置当前会话的环境变量
@.history 是一个 xonsh.history.json.JsonHistory 对象(当利用 JSON 后端时), 用于存储汗青记录, 兼容列表 API
@.imp 是一个 xonsh.built_ins.InlineImporter 包导入器对象, 用于导入包
比方:
  1. @ json = @.imp.json # 实际上等同于 import json
复制代码
@.lastcmd 是一个 xonsh.procs.pipelines.HiddenCommandPipeline 或 xonsh.procs.pipelines.CommandPipeline 对象, 存储上一个下令的捕获结果(无论显式或隐式捕获, 具体见下文)
子历程相干

shell 本质上是人类友爱且带下令表明功能的子历程与内建下令启动工具, xonsh 对 subprocess 的处理处罚和其他 shell 差别
发起子历程而并不举行捕获

为了克制歧义, xonsh 现实上对任何子历程都是捕获的(包罗不外包外包操纵符地运行下令, 利用 $[], ![], $(), !()), 此节只是先容了一种 xonsh 不提供任何捕获后信息的子历程发起方法.
比方, 纵然你不外衣操纵符运行一个下令, 下令自己也会被隐式捕获, xonsh 会读取捕获后的 HiddenCommandPipeline(隐蔽式下令管线) 对象读取返回值, 并根据你的设置体现或不体如今你的下一个提示符, 并把谁人对象放入 @.lastcmd 中, 详情请看下下节.
xonsh 提供了 $[] 操纵符以显式发起一个子历程, 它不会捕获或隐蔽式地捕获关于子历程的任何信息给任何步伐以致 xonsh 自己(返回一个 None 对象)
  1. ~ @ $[ls]
  2. Desktop  Documents  Downloads  Music  Pictures  Programs  Public  Templates  Videos
  3. ~ @ $[echo yeah]
  4. yeah
复制代码
这通常被用于脚本中, 比方只须要调用 time 下令让用户读它的输出, 又怕它和 python 的 time 包重名的情境
![] 会自动对包罗内容举行变量引用剖析后套上引号, 自己也可以包罗字符串, 但字符串内的内容将不会被剖析
一个例子:
  1. ~ @ text = 1
  2. ~ @ $['ls @(text)']
  3. xonsh: subprocess mode: command not found: 'ls @(text)'
  4. ~ @
  5. ~ @ $[ls @(text)]
  6. ls: 无法访问 '1': 没有那个文件或目录
  7. ~ @
复制代码
另一个例子, 阐明纵然不套字符串, $[] 内的内容也不会作为 python 表达式被剖析
  1. ~ @ $[time]
  2. time: missing program to run
  3. Try 'time --help' for more information.
  4. ~ @ $[time.time()]
  5.   File "<stdin>", line 1
  6.     $[time.time()]
  7.               ^^^^
  8. SyntaxError: ('code: (',)
复制代码
捕获子历程信息

xonsh 利用 $() 和 !() 操纵符捕获子历程, 但它们不是等价的
$() 操纵符用于仅捕获子历程的输出
比方:
  1. tmp @ $(ls -l)
  2. '总计 0\n-rw-r--r-- 1 pluv wheel 0  4月12日 11:57 iamafile\n'
复制代码
值得注意的是 xonsh 通过变量 $XONSH_SUBPROC_OUTPUT_FORMAT 来决定捕获得到的数据结构范例
默认是 'stream_lines', 即将全部输出行捕获为一个单一字符串
可将其设置为 'list_lines', 将全部输出行分行捕获为一个列表
比方
  1. ~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'list_lines'
  2. ~ @ $(ls -l)
  3. ['总计 36',
  4. 'drwxr-x---  4 pluv wheel 4096  4月11日 12:25 Desktop',
  5. 'drwxr-x--- 10 pluv wheel 4096  4月 8日 00:18 Documents',
  6. 'drwxr-x---  5 pluv wheel 4096  4月12日 09:03 Downloads',
  7. 'drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Music',
  8. 'drwxr-x---  5 pluv wheel 4096  3月26日 09:04 Pictures',
  9. 'drwxr-x--- 10 pluv wheel 4096  4月 9日 19:39 Programs',
  10. 'drwxr-x---  2 pluv wheel 4096  3月10日 21:58 Public',
  11. 'drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Templates',
  12. 'drwxr-x---  3 pluv wheel 4096  2月18日 21:18 Videos']
  13. ~ @
复制代码
不要将 $XONSH_SUBPROC_OUTPUT_FORMAT 设置为 'list_lines' 和 'stream_lines' 之外的其他值, 否则捕获是不会工作的
  1. ~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'list'
  2. ~ @ $(ls -l)
  3. ~ @ # 啥也没有
复制代码
别的, $() 只会捕获 stdout 流的内容, 也就是说, 假如内容是以 stderr 流输出的, xonsh 不会捕获它, 这些内容将被输出(如下例)
以及若下令不会在 stdout 输出结果(如下例), 那么 xonsh 将视 $XONSH_SUBPROC_OUTPUT_FORMAT 来将空字符串 '' 或 空列表 [] 而不是 None 对象大概 [''] 这种东西反馈给变量作为回退结果 (应该是思量到处理处罚范例的划一性吧)
  1. ~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'stream_lines'
  2. ~ @ $(ls 不存在的目录)
  3. ls: 无法访问 '不存在的目录': 没有那个文件或目录 # -> 这是输出的错误信息, xonsh 没有捕获它
  4. '' # -> 这是捕获到的内容(一个空字符串)
  5. ~ @ s = $(ls 不存在的目录)
  6. ls: 无法访问 '不存在的目录': 没有那个文件或目录
  7. ~ @ s
  8. '' # -> 这是一个空字符串
  9. ~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'list_lines'
  10. ~ @ s = $(ls 不存在的目录)
  11. ls: 无法访问 '不存在的目录': 没有那个文件或目录
  12. ~ @ s
  13. []
复制代码
对于有颜色的内容, xonsh 会捕获其控制码
可以用这个小步伐测试
[code]#include int main(){    std::cout
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表