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

xonsh 是 python 驱动的 shell, 所以在操作效率及交互和某些功能的先进性上比 bash 等优秀, 并且十分容易上手. 但相应地, 它是一个新兴的 shell, 并且不是所谓 "POSIX 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() 操作, 可以通过下列代码验证

tmp @ text = "Hello\nWorld"
tmp @ repr(text)
"'Hello\\nWorld'"
tmp @ str(text)
'Hello\nWorld'
tmp @ touch @(text)
tmp @ ls
'Hello'$'\n''World' # 文件名中真的有换行
tmp @ # 如果是 repr(), 就会创建 'Hello\nWorld' (无转义)

因此你可以这样创建一组数字序号文件

tmp @ for i in range(30):
           touch @(i)
    
tmp @ ls
0  2  4  6  8  10  12  14  16  18  20  22  24  26  28
1  3  5  7  9  11  13  15  17  19  21  23  25  27  29
tmp @

或者方便地补0

tmp @ for i in range(30):
           touch @(str(i).zfill(2))

tmp @ ls
00  02  04  06  08  10  12  14  16  18  20  22  24  26  28
01  03  05  07  09  11  13  15  17  19  21  23  25  27  29

同样的简单操作若用 bash 进行, 需要:

for i in {0..29}; do touch $(printf "%02d" $i); done

# 避免命令替换的子 shell 开销
for i in {0..29}; do
    printf -v name "%02d" "$i"
    touch "$name"
done

python 还有 bash 无法提供的一系列支持库与工具, 可处理多种格式文件而无需引用外部程序; 并且处理含有奇葩字符的文件名, xonsh 可以使用字符串的 repr 集成到命令中很难炸掉

不过, 同样的操作若用纯 python 脚本/REPL进行, 需要:

from pathlib import Path

for i in range(0, 30):
    name = str(i).zfill(2)
    path = Path(name)
    path.touch()

这两种对比足以显示 xonsh 的优越性

别名与 aliases

在 xonsh 中, 别名被存储在全局的 aliases -- 一个兼容字典 API 的 xonsh.aliases.Aliases 对象中.
像这样设置一个别名:

~ @ aliases['la'] = 'ls -a'

字符串形式建立的别名将自动转换为列表, 也即对象的智能性:

~ @ aliases['la'] = 'ls -a'
~ @ aliases['la']
['ls', '-a']

若要取消一个别名, 只需:

~ @ del aliases['la']
~ @ la
xonsh: subprocess mode: command not found: 'la'

此外, 扩展 xontrib-abbrevs (需要安装 xontrib-abbrevs 包) 还提供了一套独立的, 输入后会自动展开的类 alias 系统, 存放于全局的 abbrevs 字典对象中

~ @ abbrevs['ll'] = 'ls -l'
~ @ ls -l # 输入 ll 回车或空格后自动补全
总计 36
drwxr-x---  4 pluv wheel 4096  4月11日 12:25 Desktop
drwxr-x--- 10 pluv wheel 4096  4月 8日 00:18 Documents
drwxr-x---  5 pluv wheel 4096  4月12日 09:03 Downloads
drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Music
drwxr-x---  5 pluv wheel 4096  3月26日 09:04 Pictures
drwxr-x--- 10 pluv wheel 4096  4月 9日 19:39 Programs
drwxr-x---  2 pluv wheel 4096  3月10日 21:58 Public
drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Templates
drwxr-x---  3 pluv wheel 4096  2月18日 21:18 Videos
~ @

会话对象

xonsh 使用 @ 指代当前会话, 其为一个 XonshSessionInterface 对象, 具有以下公有属性:

@.env 是一个 xonsh.environ.Env 对象, 兼容字典 API, 用于存储或设置当前会话的环境变量, 当然 xonsh 提供了 $VAR = value 语法糖设置环境变量, bashism 插件也提供了 exportunset 命令
@.history 是一个 xonsh.history.json.JsonHistory 对象(当使用 JSON 后端时), 用于存储历史记录, 兼容列表 API
@.imp 是一个 xonsh.built_ins.InlineImporter 包导入器对象, 用于导入包
例如:

@ json = @.imp.json # 实际上等同于 import json

@.lastcmd 是一个 xonsh.procs.pipelines.HiddenCommandPipelinexonsh.procs.pipelines.CommandPipeline 对象, 存储上一个命令的捕捉结果(无论显式或隐式捕捉, 具体见下文)

子进程相关

shell 本质上是人类友好且带命令解释功能的子进程与内建命令启动工具, xonsh 对 subprocess 的处理和其他 shell 不同

发起子进程而并不进行捕获

为了避免歧义, xonsh 实际上对任何子进程都是捕获的(包括不外包操作符地运行命令, 使用 $[], ![], $(), !()), 此节只是介绍了一种 xonsh 不提供任何捕获后信息的子进程发起方法.

例如, 即使你不外套操作符运行一个命令, 命令本身也会被隐式捕获, xonsh 会读取捕获后的 HiddenCommandPipeline(隐藏式命令管线) 对象读取返回值, 并根据你的配置显示或不显示在你的下一个提示符, 并把那个对象放入 @.lastcmd 中, 详情请看下下节.

xonsh 提供了 $[] 操作符以显式发起一个子进程, 它不会捕获或隐藏式地捕获关于子进程的任何信息给任何程序乃至 xonsh 本身(返回一个 None 对象)

~ @ $[ls]
Desktop  Documents  Downloads  Music  Pictures  Programs  Public  Templates  Videos
~ @ $[echo yeah]
yeah

这通常被用于脚本中, 例如只需要调用 time 命令让用户读它的输出, 又怕它和 python 的 time 包重名的情境

![] 会自动对包含内容进行变量引用解析后套上引号, 本身也可以包含字符串, 但字符串内的内容将不会被解析
一个例子:

~ @ text = 1
~ @ $['ls @(text)']
xonsh: subprocess mode: command not found: 'ls @(text)'
~ @
~ @ $[ls @(text)]
ls: 无法访问 '1': 没有那个文件或目录
~ @

另一个例子, 说明即使不套字符串, $[] 内的内容也不会作为 python 表达式被解析

~ @ $[time]
time: missing program to run
Try 'time --help' for more information.
~ @ $[time.time()]
 
  File "<stdin>", line 1
    $[time.time()]
              ^^^^
SyntaxError: ('code: (',)

捕获子进程信息

xonsh 使用 $()!() 操作符捕获子进程, 但它们不是等价的
$() 操作符用于仅捕获子进程的输出
例如:

tmp @ $(ls -l)
'总计 0\n-rw-r--r-- 1 pluv wheel 0  4月12日 11:57 iamafile\n'

值得注意的是 xonsh 通过变量 $XONSH_SUBPROC_OUTPUT_FORMAT 来决定捕获得到的数据结构类型
默认是 'stream_lines', 即将所有输出行捕获为一个单一字符串
可将其设置为 'list_lines', 将所有输出行分行捕获为一个列表
例如

~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'list_lines'
~ @ $(ls -l)
['总计 36',
 'drwxr-x---  4 pluv wheel 4096  4月11日 12:25 Desktop',
 'drwxr-x--- 10 pluv wheel 4096  4月 8日 00:18 Documents',
 'drwxr-x---  5 pluv wheel 4096  4月12日 09:03 Downloads',
 'drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Music',
 'drwxr-x---  5 pluv wheel 4096  3月26日 09:04 Pictures',
 'drwxr-x--- 10 pluv wheel 4096  4月 9日 19:39 Programs',
 'drwxr-x---  2 pluv wheel 4096  3月10日 21:58 Public',
 'drwxr-x---  2 pluv wheel 4096  2月 6日 15:18 Templates',
 'drwxr-x---  3 pluv wheel 4096  2月18日 21:18 Videos']
~ @

不要将 $XONSH_SUBPROC_OUTPUT_FORMAT 设置为 'list_lines''stream_lines' 之外的其他值, 否则捕获是不会工作的

~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'list'
~ @ $(ls -l)
~ @ # 啥也没有

另外, $() 只会捕获 stdout 流的内容, 也就是说, 如果内容是以 stderr 流输出的, xonsh 不会捕获它, 这些内容将被输出(如下例)
以及若命令不会在 stdout 输出结果(如下例), 那么 xonsh 将视 $XONSH_SUBPROC_OUTPUT_FORMAT 来将空字符串 '' 或 空列表 [] 而不是 None 对象或者 [''] 这种东西反馈给变量作为回退结果 (应该是考虑到处理类型的一致性吧)

~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'stream_lines'
~ @ $(ls 不存在的目录)
ls: 无法访问 '不存在的目录': 没有那个文件或目录 # -> 这是输出的错误信息, xonsh 没有捕获它
'' # -> 这是捕获到的内容(一个空字符串)
~ @ s = $(ls 不存在的目录)
ls: 无法访问 '不存在的目录': 没有那个文件或目录
~ @ s
'' # -> 这是一个空字符串
~ @ $XONSH_SUBPROC_OUTPUT_FORMAT = 'list_lines'
~ @ s = $(ls 不存在的目录)
ls: 无法访问 '不存在的目录': 没有那个文件或目录
~ @ s
[]

对于有颜色的内容, xonsh 会捕获其控制码

可以用这个小程序测试

#include <iostream>

int main(){
    std::cout << "我是 stdout 的内容" << std::endl;
    std::cerr << "我是 stderr 的内容" << std::endl;
    std::cout << "我可以被捕获" << std::endl;
    std::cerr << "我不会被捕获, 而是被直接输出" << std::endl;
    std::cout << "\e[31m我是红色的, 我可以被捕获\e[0m" << std::endl;
    std::cerr << "\e[32m我是绿色的, 我不会被捕获\e[0m" << std::endl;
    return 0;
}
~ @ s = $(./a.out)
我是 stderr 的内容
我不会被捕获, 而是被直接输出
我是绿色的, 我不会被捕获
~ @ s
'我是 stdout 的内容\n我可以被捕获\n\x1b[31m我是红色的, 我可以被捕获\x1b[0m\n'
~ @

!() 则更为高级, 它捕获关于命令的更多信息, 并将其放入一个 CommandPipeline (命令管线) 对象中, 使其能够捕获所有流的内容及子进程的元数据
这个对象定义在 xonsh.procs.pipelines

~ @ !(ls 一个不存在的目录)
CommandPipeline(
  returncode=2,
  pid=40466,
  args=['ls', '一个不存在的目录'],
  alias=['ls', '--color=auto', '-v', '一个不存在的目录'],
  executed_cmd=['ls', '--color=auto', '-v', '一个不存在的目录'],
  timestamps=[1775966865.5426445, 1775966865.5505455],
  input='',
  output=[],
  errors="ls: 无法访问 '一个不存在的目录': 没有那个文件或目录\n"
)
~ @ !(ls $HOME)
CommandPipeline(
  returncode=0,
  pid=40483,
  args=['ls', '/home/pluv'],
  alias=['ls', '--color=auto', '-v', '/home/pluv'],
  executed_cmd=['ls', '--color=auto', '-v', '/home/pluv'],
  timestamps=[1775966873.558355, 1775966873.5660608],
  input='',
  output=['Desktop', 'Documents', 'Downloads', 'Music', 'Pictures', 'Programs', 'Public', 'Templates', 'Videos']
)

以下是一份常用的映射表, 由于 xonsh 不会显示所有对象属性(例如不会显示值为 None 的属性), 所以此表常见但不完整
完整的 CommandPipeline 对象手册见 xonsh.procs.pipelines - xonsh 0.22.8 documentation

CommandPipeline(
  returncode(int) -> 进程返回值, 0 为正常值
  pid(int) -> 进程 PID
  args -> 命令及其参数
  alias(list/FuncAlias) -> 进程使用的别名信息, 可能是解析后命令序列(对于子进程), 也可能是 FuncAlias 对象(对于内建命令别名), 如果没有使用别名, 此对象为 None
  executed_cmd(list) -> 解析后的进程参数
  timestamps(list[int, int]) -> 进程起始及终止 UNIX 时间戳
  input(str) -> 进程接受的输入 (stdin)
  output(str/list) -> 进程的标准输出 (stdout)
  errors(str) -> 进程的错误流输出 (stderr)
)

CommandPipeline.output 的格式依然受 $XONSH_SUBPROC_OUTPUT_FORMAT 控制, error 不受控制, 为 stream_line 格式

隐藏的捕获方式

术语隐藏式捕获进程的意思是: 不捕获命令的输出, 就像命令未被捕获一般, 但通过一个 HiddenCommandPipeline(隐藏式命令管线) 对象获得其输出之外的数据

事实上, xonsh 提供了 ![] 用于隐藏式捕获进程, 它的原理被同样应用到不外包操作符的命令执行中, 只是后者对写独立脚本的人几乎无用 -- 你不能将不外包操作符的命令的 HiddenCommandPipeline 对象装进自己的变量里, 因为它的捕获仅仅用于 xonsh 的返回值和历史记录处理, 以及 @.lastcmd

HiddenCommandPipeline 部分复用了 CommandPipeline 的方法与对象, 以下是一个例子:

~ @ cap = ![ls]
Desktop  Documents  Downloads  Music  Pictures  Programs  Public  Templates  Videos
~ @ cap
~ @ cap.__repr__()
''
~ @ cap.__repr__
<bound method HiddenCommandPipeline.__repr__ of >
~ @ cap.__str__()
''
~ @ cap.__str__
<bound method CommandPipeline.__str__ of >
~ @ cap.output # 空字符串
''
~ @ cap.errors # None

对这些操作的归纳呈现于一个表格:

类型 输出 返回值 备注
cmd 不捕获 隐藏命令管道 与 ![cmd] 相同
![cmd] 不捕获 隐藏命令管道
$[cmd] 不捕获
!(cmd) 捕获 命令管道
$(cmd) 捕获 字符串 返回标准输出

内建命令相关

你可能注意到上文对 FuncAlias 对象的提及, 它出现在"捕获内建命令"的 CommandPipeline 对象中
xonsh 的内建命令实际上是一个 FuncAlias 对象封装的函数对象

对于内建命令, 也可以通过 $()!() 捕获, 但行为有不同, 以下是一些例子

~ @ !(echo hi,xonsh)
CommandPipeline(
  returncode=0,
  pid=72351,
  args=['echo', 'hi,xonsh'],
  executed_cmd=['echo', 'hi,xonsh'],
  timestamps=[1775982843.3403664, 1775982843.3477223],
  input='',
  output='hi,xonsh'
)
~ @ !(cd ~)
CommandPipeline(
  returncode=0,
  args=['cd', '/home/pluv'],
  alias=FuncAlias({'name': 'cd', 'func': 'cd', 'return_what': 'result'}),
  executed_cmd=['/home/pluv'],
  timestamps=[1775982585.2600465, 1775982585.2614303],
  input='',
  output=''
)

可见 CommandPipelinealias 对于内置命令为命令对应的 FuncAlias 对象, 因为 xonsh 认为内置命令调用是 FuncAlias 的别名

调优

.xonshrc, xontribs 和 xpip

主目录的 .xonshrc 文件是 xonsh 启动时自动执行的命令列表, 其使用 xonsh 的命令解析器

当然你也可以执行 xonsh --no-rc 来避免加载 .xonshrc 排错

xonsh 还跨平台提供 source-bash, source-zshsource-cmd 来分别解析 bash, zsh 和 windows cmd.exe 命令解析器的运行命令文件, 例如使用 source-bash .bashrc 来迁移 bash 的配置文件
这些命令也有其他的 flag, 请参阅 xonsh 的文档


xontribs 是 xonsh 的插件系统
xontrib 命令可用于管理 xontrib
例如直接运行或运行 xontrib list 查看 xontrib 状态

~ @ xontrib
abbrevs            loaded      manual
argcomplete        loaded      manual
broot              loaded      manual
cd                 loaded      auto
coreutils          not-loaded
direnv             not-loaded
dotdot             loaded      auto
free_cwd           loaded      manual
jedi               loaded      manual
sh                 loaded      manual
uvox               loaded      auto
zoxide             loaded      manual
zoxide_init_cache  not-loaded

执行 xontrib load 来加载指定的 xontrib
执行 xontrib reload 来重载指定的 xontrib
执行 xontrib unload 来取消加载指定的 xontrib

例如 xontrib load coreutils 会加载 xonsh 跨平台实现的一套 coreutils (也就是 ls 等命令组件), 它并不能提供"原生对象集成", 性能和功能, 以及底层管理都不如 GNU coreutils 甚至 busybox 优秀, 唯二好处是调用开销较低和跨平台(对于 windows 来说比较方便)


xpip 是 xonsh 提供的 pip 别名, 它安装的包将仅安装至运行 xonsh 的 python 解释器包路径内

xpip 不是 pipx

可安装的 xontribs 可在 awesome-xontribs 仓库内找到(但这仓库有点老了, 虽然很多插件项目还活着)

以下是用 xpip 安装的比较有用的插件列表:

~ @ xpip install -U xontrib-abbrevs \
                 xontrib-argcomplete \
                 xontrib-free-cwd \
                 xontrib-zoxide \
                 xontrib-dotdot \
                 xonsh-direnv \
                 xontrib-sh \
                 xontrib-bashisms \
                 xontrib-uvox \
                 xontrib-broot \
                 xontrib-jedi \
                 xontrib-cd \

以下是说明:

  • xontrib-cd: 让 cd 命令后路径不需要转义
  • xontrib-abbrevs: 一套独立的 abbrevs 类 alias 系统, 好处是输入后会自动展开
  • xontrib-jedi: 通过 jedi 语言服务器插件来实现 xonsh 的 python 补全, 需要安装 jedi LSP
  • xontrib-broot: broot TUI 文件管理器集成(broot 不自带对 xonsh 的集成), 需要安装 broot, 默认配置 ctrl + N 快捷键, 可以自动 cd 到选定目录, 对目录跳转比较有用
  • xontrib-zoxide: 快速跳转目录工具, 个人认为没啥用
  • xontrib-dotdot: 可以通过 ...... 切换到上上上级目录
  • xonsh-direnv: 自动加载目录下 .env 文件, 这个插件会破坏 @.lastcmd 的行为, 因为 "上一条命令" 将永远是 direnv, 谨慎考虑安装
  • xontrib-vox: vox 是 xonsh 的一套 python 虚拟环境系统, 这个插件将 xonsh 和 vox 集成 (没用过)
  • xontrib-uvox: 将 xonsh 和 python uv 包管理器像 vox 一样集成 (很有用)
  • xontrib-free-cwd: 不让 xonsh 占用工作目录以避免 windows 删除目录提示占用中和 umount 提示目标忙的问题
  • xontrib-sh: 在 xonsh 内运行 bash, zsh, fish, tcsh, pwsh 命令, 原理依然是子进程, 但方便一些, 可以通过类似 IPython 的 ! <命令> 或者 !bash <命令> 快速使用, 效果等同于 bash -c '<命令>'
  • xontrib-bashisms: 在 xonsh 内直接支持部分 bash 风格的命令, 例如 unset, export
  • xontrib-argcomplete: 用于支持 argparse 参数解析器, 来进行部分 python 和 .xsh 脚本的参数补全, 它自己有一套参数生态, 自己写 .xsh 脚本运行时和对于一些 python 脚本很有用, 但对其他脚本来说无用

不要安装 xonsh-docker-tabcomplete, 这个插件实现老旧, 无法在高版本 docker 使用
xonfig web 的 xontrib 配置对于第三方 xontrib 有 bug, 最好不要用它, 因此要手动编辑 .xonshrc 来确保启用


以下是示例 .xonshrc 配置和一些有用的环境变量设置

# XONSH WEBCONFIG START
$PROMPT = '{BOLD_BLUE}{cwd_base}{RESET} @ ' # 提示符主题
$XONSH_COLOR_STYLE = 'default' # 颜色主题
$XONSH_SHOW_TRACEBACK = False # 不让 xonsh 显示 "是否启用 python traceback" 的提示
$SUGGEST_COMMANDS = False # 不让 xonsh 建议打错的命令
$XONSH_SUBPROC_OUTPUT_FORMAT = 'list_lines'
# xontrib load coreutils # 不加载 xonsh 的 coreutils
xontrib load jedi
xontrib load broot
xontrib load abbrevs
xontrib load argcomplete
xontrib load free_cwd
xontrib load zoxide
xontrib load direnv
xontrib load sh
# XONSH WEBCONFIG END

# 别名配置
aliases['l'] = 'ls'
aliases['la'] = 'ls -a'
aliases['ll'] = 'ls -l'
aliases['rrm'] = 'rm -rf'
aliases['cpr'] = 'rsync -ah --info=progress2'
aliases['cls'] = 'printf "\033c"'
aliases['lss'] = lambda: __import__('os').system('stat -c "%a %n" *')

source-bash $HOME/.profile

# 加载一些常用的 python 库以便调用
import math
import time as _time # 避免和系统 time 冲突
import os
import xonsh as _xonsh # 避免和 xonsh 冲突
import copy
import random

从 bash 转换与迁移

这一节直接翻译自 Bash to Xonsh Translation Guide - xonsh 0.22.8 documentation, 提供 bash 中常见语法对应的 xonsh 等效写法

Bash Xonsh 说明
没有表示会话的特殊对象 @ @ 对象包含 @.env - 环境变量, @.imp - 导入器, @.lastcmd - 上一条命令等
script.sh script.xsh 推荐的文件扩展名是 .xsh
#!/bin/bash #!/usr/bin/env xonsh 在 shebang 中使用 xonsh
echo --arg="val" echo \; echo --arg "val" echo {} echo "{}" echo ";" 请阅读子进程字符串教程以理解字符串如何成为 xonsh 中的参数. xonsh 中没有像 bash 中反斜杠 (\) 那样的转义字符概念. 可以使用单引号或双引号来移除某些字符、单词或括号的特殊含义.
$NAME${NAME} $NAME 按名称查找环境变量
export NAME=Peter $NAME = 'Peter' | 设置环境变量. 另请参阅 $UPDATE_OS_ENVIRON
unset NAME del $NAME 取消设置/删除环境变量
echo "$HOME/hello" | echo "$HOME/hello" 使用环境变量构造参数
echo $HOME/$(uname) echo @($HOME + '/' + $(uname)) 连接变量或文本与运行命令的结果
echo 'my home is $HOME' | echo @("my home is $HOME") 阻止环境变量展开
${!VAR} | ${var or expr} 通过另一个变量名查找环境变量. 在 xonsh 中, 这可以是任何有效表达式.
ENV1=VAL1 command $ENV1=VAL1 commandwith @.env.swap(ENV1=VAL1): command 设置临时环境变量并执行命令. 使用第二种带缩进块的表示法可在同一上下文中执行多条命令.
alias ll='ls -la' aliases['ll'] = 'ls -la' xonsh 中的别名可以是作为字符串或参数列表的子进程命令, 也可以是任何 Python 函数.
$(cmd args)`cmd args` @$(cmd args) 命令替换(允许命令的输出替换该命令本身). 将子进程命令的输出标记化并作为另一个子进程执行.
v=`echo 1` v=$(echo 1) v=$(echo 1) 在 bash 中, 反引号表示运行捕获的子进程——在 xonsh 中对应 $(). xonsh 中的反引号表示正则表达式 globbing(即 ls `/etc/pass.*`).
echo -e "\033[0;31mRed text\033[0m" printx("{RED}Red text{RESET}") 尽可能简单地打印彩色文本
shopt -s dotglob $DOTGLOB = True 使用 *** 进行 globbing 时也会匹配点文件, 或那些名称以字面量 . 开头的“隐藏”文件. 与 bash 类似, 默认情况下此类文件会被过滤掉.
if [ -f "$FILE" ]; p'/path/to/file'.exists()pf'{file}'.exists()if !(test -f $FILE): 可以实例化 Path 对象并使用 p-string 语法直接检查
set -e $RAISE_SUBPROC_ERROR = True 在非零返回码后引发失败. Xonsh 将引发 subprocess.CalledProcessError.
set -x trace on$XONSH_TRACE_SUBPROC = True 在执行期间打开源代码行的跟踪
&& &&and 子进程的逻辑与运算符
|| || 以及 or 子进程的逻辑或运算符
$$ os.getpid() 获取当前 shell 的 PID
$? @.lastcmd.rtn 返回上一条命令的退出码或状态. 要在 xonsh 脚本中获取命令的退出码, 对于非交互式进程请使用 !().rtn.
$<n> $ARG<n> 索引 n 处的命令行参数, 因此 $ARG1 相当于 $1
$@ $ARGS 所有命令行参数和字符串的列表
while getopts 使用 argparseclick 另请参阅 awesome-cli-app 和 xontrib-argcomplete
complete completer list 与许多其他 shell 一样, xonsh 在按下 Tab 键时能够补全部分指定的参数.
IFS $XONSH_SUBPROC_OUTPUT_FORMAT 更改输出表示和拆分. 另请查看 DecoratorAlias 以获取返回对象的能力, 例如 j = $(@json echo '{}').
将补全显示为列表 $COMPLETIONS_DISPLAY = 'readline' 显示补全将模拟 readline 的行为
docker run -it bash docker run -it xonsh/xonsh:slim 真的有人用容器只为跑一个 shell 吗?
exit 1 exit 1exit(1) 退出当前脚本

本文完 :3

posted @ 2026-04-12 22:57  pluvium27  阅读(4)  评论(0)    收藏  举报