Job Control in Nushell
Effective Shell 书中的 Job Control 章节,介绍了如何在 Bash 中进行任务控制。感觉是比较有用的。本文将介绍其在 Nushell 中的用法。
Job Control
什么是Job Control,可以参考 Effective Shell/Job-Control#What-is-job-control。
概括来讲,就是允许用户在前台和后台之间切换任务的能力
解决的问题
- 释放终端:将耗时任务转入后台运行,避免命令行被卡住,实现单窗口下的多任务并行。
- 挂起恢复:暂停当前程序(e.g. Vim)去处理突发事务,然后能无缝回到之前的工作状态。
P.S: 如果使用Tmux/Zellij,也可以尝试使用floating pane来处理突发事务,参考我的这篇文章 - 任务管理:列出所有任务清单,清楚掌握谁在运行,并能随时关闭指定任务。
Job Management Overview
for UNIX Shell Users
- 在命令末尾添加
&来在后台运行命令 - 使用
jobs列出后台运行的命令 - 按
Ctrl-Z将命令暂停并转为后台 - 使用
fg将后台运行的命令转入前台
Nushell built-in的job命令,提供了类似UNIX Shell的功能。使用help job可以查看其用法。
可以说Nushell和Powershell的命令十分语义化了。而UNIX Shell,设计偏简洁,会大量使用符号和缩写,依赖记忆,初学者难以理解其含义$ help jobVarious commands for working with background jobs.You must use one of the following subcommands. Using this command as-is will only produce this help message.Usage: > jobSubcommands: job flush - Clear this job's mailbox. job id - Get id of current job. job kill - Kill a background job. job list - List background jobs. job recv - Read a message from the mailbox. job send - Send a message to the mailbox of a job. job spawn - Spawn a background job and retrieve its ID. job tag - Add a description tag to a background job. job unfreeze - Unfreeze a frozen process job in foreground.Flags: -h, --help: Display the help message for this commandInput/output types: ╭───┬─────────┬────────╮ │ # │ input │ output │ ├───┼─────────┼────────┤ │ 0 │ nothing │ string │ ╰───┴─────────┴────────╯
本文结束, 下面简单挑几个我感兴趣的子命令来介绍。
Spawn Background Jobs🫃🏻
Spawn ,意为孵化,在 Nushell 中表示启动一个后台任务。
- P.S. 第一次接触这个单词,是给Cyberpunk打Mod,生成NPC用的就是Spawn这个单词 (😡你这NPC衣服怎么没加载啊)
使用方法无需过多赘述,直接看例子,hexo clean; hexo gen 命令执行需要~10s,期间会阻塞交互,将其放在后台执行会方便些# 启动后台任务$ job spawn { hexo clean; hexo gen }# 确认它正在后台默默工作$ job list╭───┬────┬────────┬──────────────╮│ # │ id │ type │ pids │├───┼────┼────────┼──────────────┤│ 0 │ 34 │ thread │ ╭───┬──────╮ ││ │ │ │ │ 0 │ 5359 │ ││ │ │ │ ╰───┴──────╯ │╰───┴────┴────────┴──────────────╯
关心任务的输出?暂时没找到Nushell对应的API,一个workaround是把stdout/stderr重定向到文件中:
hexo generate o> hexo.log e> hexo.err
Process Bound
当运行 job spawn { ... } 时,Nushell 实际上是在当前的 Shell 进程内部启动了一个新的Rust Thread来运行这个Closure[1],也就是说:
- 多个Nushell进程之间的jobs是相互独立的,无法跨进程管理
- 当前当你Exit Nushell后,后台任务也会被清理:
$ exitThere are still background jobs running (1).Running `exit` a second time will kill all of them.虽然 Nushell 会尝试清理任务,但发现像 job spawn {hexo server} 这种启动了外部子进程(node)清理得并不彻底,node进程不会死,而是变成了孤儿进程(Orphan Process)。在Github上提了个Issue job spawn terminates direct child but leaves grandchild processes orphaned · Issue #17378 · nushell/nushell
假设:
- Parent: Nushell进程的PID是53822,
- Child:
hexo server启动的bun进程PID是64759 - Grandchild: bun启动的node进程PID是64762
分别在exit nushell前后运行ps,查看它们的状态; 可以看到,Nushell退出后,bun进程被杀死了,但node进程的PPID变成了1(init进程),说明它并没有被清理掉
╭───┬───────┬──────┬──────┬────────┬──────┬─────────┬──────────┬─────────┬────────────────┬─────────┬──────────┬─────────────────┬──────────╮│ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │ command │ start_time │ user_id │ priority │ process_threads │ cwd │├───┼───────┼──────┼──────┼────────┼──────┼─────────┼──────────┼─────────┼────────────────┼─────────┼──────────┼─────────────────┼──────────┤│ 0 │ 53822 │ 5135 │ nu │ Sleep │ 0.00 │ 38.0 MB │ 445.7 GB │ -nu │ 21 minutes ago │ 501 │ 31 │ 5 │ /Users/g ││ │ │ │ │ │ │ │ │ │ │ │ │ │ jx/Proje ││ │ │ │ │ │ │ │ │ │ │ │ │ │ cts/dotf ││ │ │ │ │ │ │ │ │ │ │ │ │ │ iles │╰───┴───────┴──────┴──────┴────────┴──────┴─────────┴──────────┴─────────┴────────────────┴─────────┴──────────┴─────────────────┴──────────╯╭───┬───────┬───────┬──────┬────────┬──────┬────────┬──────────┬──────────────────────────────────┬───────────────┬─────────┬─────────┬─────╮│ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │ command │ start_time │ user_id │ priorit │ ... ││ │ │ │ │ │ │ │ │ │ │ │ y │ │├───┼───────┼───────┼──────┼────────┼──────┼────────┼──────────┼──────────────────────────────────┼───────────────┼─────────┼─────────┼─────┤│ 0 │ 64759 │ 53822 │ bun │ Sleep │ 0.00 │ 5.3 MB │ 446.8 GB │ /opt/homebrew/bin/bun run hexo s │ 2 minutes ago │ 501 │ 31 │ ... │╰───┴───────┴───────┴──────┴────────┴──────┴────────┴──────────┴──────────────────────────────────┴───────────────┴─────────┴─────────┴─────╯╭────┬────────┬────────┬───────┬────────┬──────┬──────────┬──────────┬─────────┬───────────────┬─────────┬──────────┬─────────────────┬─────╮│ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │ command │ start_time │ user_id │ priority │ process_threads │ cwd │├────┼────────┼────────┼───────┼────────┼──────┼──────────┼──────────┼─────────┼───────────────┼─────────┼──────────┼─────────────────┼─────┤│ 0 │ 64762 │ 64759 │ node │ Sleep │ 0.00 │ 229.3 MB │ 455.6 GB │ hexo │ 2 minutes ago │ 501 │ 31 │ 15 │ │╰────┴────────┴────────┴───────┴────────┴──────┴──────────┴──────────┴─────────┴───────────────┴─────────┴──────────┴─────────────────┴─────╯╭────────────╮│ empty list │╰────────────╯╭────────────╮│ empty list │╰────────────╯╭───┬───────┬──────┬──────┬────────┬──────┬──────────┬──────────┬─────────┬───────────────┬─────────┬──────────┬─────────────────┬─────╮│ # │ pid │ ppid │ name │ status │ cpu │ mem │ virtual │ command │ start_time │ user_id │ priority │ process_threads │ cwd │├───┼───────┼──────┼──────┼────────┼──────┼──────────┼──────────┼─────────┼───────────────┼─────────┼──────────┼─────────────────┼─────┤│ 0 │ 64762 │ 1 │ node │ Sleep │ 0.00 │ 229.6 MB │ 455.6 GB │ hexo │ 2 minutes ago │ 501 │ 31 │ 15 │ │╰───┴───────┴──────┴──────┴────────┴──────┴──────────┴──────────┴─────────┴───────────────┴─────────┴──────────┴─────────────────┴─────╯Usage Scenarios
- Uploading files
- 一个具体的例子,Atuin的Nushell的脚本中,就使用了
job spawn来创建上传的异步任务 [2]
- 一个具体的例子,Atuin的Nushell的脚本中,就使用了
- Downloading files
- Compile Code etc.
Freeze 🥶
就像在 UNIX Shell 中一样,你可以Freeze正在运行的前台进程:
- 在前台运行
hx ./test.js - 按 Ctrl+Z 或给进程发送
SIGTSTP信号; Nushell 报告:Job 20 is frozen - Do something else…
- 使用
job unfreeze 20可以将其恢复到前台继续运行
被冻结的任务不再消耗 CPU 资源,但仍然占用内存,并且保持着打开的文件句柄,等待用户下一步指令
| Bash / Zsh | Nushell | |
|---|---|---|
| 恢复到前台 | fg %1 | job unfreeze 20 |
| 杀死任务 | kill %1 | job kill 20 |
| 解冻并转后台跑 | bg %1 | ❌ Issue #15196 |
An anonymous function, often called a lambda function, which accepts parameters and closes over (i.e., uses) variables from outside its scope Closure | Nushell ↩︎
Atuin 是一款流行的Shell工具,它将Shell History Command持久化到SQLite数据库中,并支持将其同步到服务器,实现跨Shell+跨设备的历史命令同步 ↩︎
Job Control in Nushell