Job Control in Nushell
Job Control
For what job control is, see Effective Shell/Job-Control#What-is-job-control.
In short, it is the ability to move tasks between the foreground and the background.
Problems It Solves
- Freeing the terminal: move a long-running task into the background so the command line doesn’t get stuck, letting you multitask in a single window.
- Suspend and resume: pause the current program (e.g. Vim) to handle something urgent, then seamlessly return to where you were.
P.S: if you use Tmux/Zellij, a floating pane also works for handling interruptions, see my post on that - Task management: list every task, know exactly what is running, and kill any of them at will.
Job Management Overview
for UNIX Shell Users
- Append
&to a command to run it in the background - Use
jobsto list background commands - Press
Ctrl-Zto suspend a command and send it to the background - Use
fgto bring a background command to the foreground
Nushell’s built-in job command provides functionality similar to UNIX shells. Run help job to see how it works.
You could say Nushell and PowerShell commands are quite self-explanatory. UNIX shells, by contrast, lean toward brevity, relying heavily on symbols and abbreviations that you have to memorize, which makes them hard for beginners to decipher.$ 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 │ ╰───┴─────────┴────────╯
End of post, below I’ll pick a few subcommands I find interesting.
Spawn Background Jobs🫃🏻
Spawn means to hatch; in Nushell it means starting a background job.
- P.S. I first ran into this word while modding Cyberpunk, where spawning NPCs uses exactly this word (😡why aren’t this NPC’s clothes loading)
Usage needs little explanation, so straight to an example. hexo clean; hexo gen takes ~10s and blocks the prompt while it runs, so it’s more convenient to push it into the background:# Start a background job$ job spawn { hexo clean; hexo gen }# Confirm it is quietly working in the background$ job list╭───┬────┬────────┬──────────────╮│ # │ id │ type │ pids │├───┼────┼────────┼──────────────┤│ 0 │ 34 │ thread │ ╭───┬──────╮ ││ │ │ │ │ 0 │ 5359 │ ││ │ │ │ ╰───┴──────╯ │╰───┴────┴────────┴──────────────╯
Care about the job’s output? I haven’t found a corresponding Nushell API yet; one workaround is to redirect stdout/stderr to files:
hexo generate o> hexo.log e> hexo.err
Process Bound
When you run job spawn { ... }, Nushell actually starts a new Rust thread inside the current shell process to run the closure[1], which means:
- Jobs in different Nushell processes are independent of each other; you can’t manage them across processes
- When you exit Nushell, background jobs get cleaned up too:
$ exitThere are still background jobs running (1).Running `exit` a second time will kill all of them.Although Nushell tries to clean up jobs, I found that with something like job spawn {hexo server}, which launches external child processes (node), the cleanup isn’t thorough. The node process doesn’t die; it becomes an orphan process. I filed an issue on GitHub: job spawn terminates direct child but leaves grandchild processes orphaned · Issue #17378 · nushell/nushell
Suppose:
- Parent: the Nushell process has PID 53822
- Child: the bun process started by
hexo serverhas PID 64759 - Grandchild: the node process started by bun has PID 64762
Run ps before and after exiting Nushell to check their state. You can see that after Nushell exits, the bun process is killed, but the node process’s PPID becomes 1 (the init process), meaning it was never cleaned up
Usage Scenarios
- Uploading files
- A concrete example: Atuin’s Nushell script uses
job spawnto create an async upload task [2]
- A concrete example: Atuin’s Nushell script uses
- Downloading files
- Compile Code etc.
Freeze 🥶
Just like in a UNIX shell, you can freeze a running foreground process:
- Run
hx ./test.jsin the foreground - Press Ctrl+Z or send the process a
SIGTSTPsignal; Nushell reports:Job 20 is frozen - Do something else…
- Use
job unfreeze 20to bring it back to the foreground and resume
A frozen job no longer consumes CPU, but it still occupies memory and keeps its file handles open, waiting for your next instruction.Bash / Zsh Nushell Resume in foreground fg %1job unfreeze 20Kill the job kill %1job kill 20Unfreeze into background 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 is a popular shell tool that persists shell history commands into a SQLite database and can sync them to a server, giving you cross-shell, cross-device history sync ↩︎