用 Rust uutils 替换 Windows PowerShell 内置 cmdlet

更新:适用于 Linux 的 Windows 子系统(WSL2)已更新 2.0 版本,一定程度上解决了之前占用端口等弊病。再加上之前更新的许多实用功能,个人已经使用 WSL2 的 zsh 基本代替了 Windows 下的 PowerShell,故本文的意义不再像以前那么大。

uutils 是一个用 Rust 重写的 GNU Coreutils,也就是 Linux 上的 cpmvtouch 等程序的一个实现。虽然在 Windows 的 PowerShell 中执行这些命令也能得到类似于 Linux 的结果,但实际上是一个 PowerShell 内置 cmdlet 的 alias,在某些情况下的兼容性并不是那么好。本文探索一种用 uutils 的命令替代 PowerShell 内置 cmdlet 的方法,以让 Windows 的命令行体验更加 Unix 化(虽然还是没那么 Unix 就是了)。

uutils/coreutils

安装 uutils

可以使用预编译的二进制安装,也可以使用源代码编译安装。由于后面添加自动补全还需要源代码,所以我选择后者。

如果不需要 shell 自动补全,那么可以直接从上面 GitHub 仓库的 releases 下载对应的压缩包(coreutils-version-x86_64-pc-windows-{msvc|gnu}.zip),将解压后的 coreutils.exe 放在任何一个 PATH 下的目录下即可。至于如何添加 PATH,此处不再赘述,很容易搜到。

默认编译或下载到的是 multi-call binary,即调用方式为 coreutils.exe <command>,本文也按照这个情况书写。也可以编译为分离的二进制文件,具体请参考上面的 GitHub Readme。

编译源代码需要先 安装 Rust 工具链,未安装的请移步教程。

在你认为合适的位置,使用以下命令将其安装到 Rust 存放二进制文件的地方:

powershell
1
2
3
git clone https://github.com/uutils/coreutils
cd coreutils
cargo install --path . --features windows

编译出的二进制文件一般在 %HomePath%/.cargo/bin 里面,应该是默认添加到 PATH 的。安装完成后可以开一个新的 PowerShell,运行 coreutils 以验证是否安装完成。

为 PowerShell 添加自动补全

自动补全的主要用处大概是输入参数的时候能够按 tab 补全,如 ls --di 按下 tab 可以补全为 ls --directory

有点类似于 Bash 的. bashrc 初始化脚本,PowerShell 的自定义设置由 profile 管理。用户 profile 的路径内置于 PowerShell 的 $profile 环境变量,可以直接在 PowerShell 中运行 notepad $profile 来编辑。

但自动补全文本量很大,可以将每个命令的补全各自写入一个文件,然后在 profile 中引用。对于把补全文件放在同一个文件夹内的情况,可以在 profile 中添加以下内容:

powershell
1
2
3
4
5
6
7
8
9
$completionsPath = "C:\path\to\completions"  # Replace with the path to your completions directory

# Get all completion script files in the specified directory
$completionScripts = Get-ChildItem -Path $completionsPath -Filter "*.ps1" -File

# Load each completion script
foreach ($script in $completionScripts) {
    . $script.FullName
}

这样就可以加载 C:\path\to\completions(自己定义)里的补全预设了。

然后回到 coreutils 的源代码文件夹中,用 PowerShell 执行以下命令:

powershell
1
2
3
foreach($cmd in'b2sum','b3sum','base32','base64','basename','basenc','cat','cksum','comm','cp','csplit','cut','date','dd','df','dir','dircolors','dirname','du','echo','env','expand','expr','factor','false','fmt','fold','hashsum','head','join','link','ln','ls','md5sum','mkdir','mktemp','more','mv','nl','numfmt','od','paste','pr','printenv','printf','ptx','pwd','readlink','realpath','relpath','rm','rmdir','seq','sha1sum','sha224sum','sha256sum','sha3-224sum','sha3-256sum','sha3-384sum','sha3-512sum','sha384sum','sha3sum','sha512sum','shake128sum','shake256sum','shred','shuf','sleep','sort','split','sum','tac','tail','tee','test','touch','tr','true','truncate','tsort','unexpand','uniq','unlink','vdir','wc','yes') {
    cargo run completion $cmd powershell > "C:\path\to\completions\$cmd.ps1"
}

这样就可以把自动补全输出到你自定义的补全预设目录(还是别忘了替换路径)。

取代 PowerShell 自带 cmdlet alias

现在每次都要先输一个 coreutils 命令才能调用,既不方便也没法用自动补全,干脆直接把命令映射过来。

直接 Set-Alias 是不成功的,因为 PowerShell 自带 alias。在 profile 中再添加以下内容:

powershell
1
2
3
4
foreach($cmd in'b2sum','b3sum','base32','base64','basename','basenc','cat','cksum','comm','cp','csplit','cut','date','dd','df','dir','dircolors','dirname','du','echo','env','expand','expr','factor','false','fmt','fold','hashsum','head','join','link','ln','ls','md5sum','mkdir','mktemp','more','mv','nl','numfmt','od','paste','pr','printenv','printf','ptx','pwd','readlink','realpath','relpath','rm','rmdir','seq','sha1sum','sha224sum','sha256sum','sha3-224sum','sha3-256sum','sha3-384sum','sha3-512sum','sha384sum','sha3sum','sha512sum','shake128sum','shake256sum','shred','shuf','split','sum','tac','tail','test','touch','tr','true','truncate','tsort','unexpand','uniq','unlink','vdir','wc','yes') {
    Remove-Alias $cmd -ErrorAction SilentlyContinue
    Set-Item function:$cmd -Value {coreutils $cmd $args}.GetNewClosure()
}

(修改自 这个 Gist

因为 sleepteesort 不允许修改,所以只好删掉了。

如果没有意外的话,再打开一个 PowerShell,就可以使用新的 uutils 了。

参考资料:

许可证:CC BY-SA 4.0
最后更新于 Oct 01, 2023 20:56 +0800