Skip to content

Add Background(): start a process that shares nothing with the caller - #10

Merged
tomlm merged 4 commits into
mainfrom
background
Nov 4, 2026
Merged

tomlm merged 4 commits into
mainfrom
background

Conversation

@tomlm

@tomlm tomlm commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Adds Background(program, args...) (on CShell and Globals) for servers and daemons that should outlive the script, and bumps the version to 3.3.0.

int pid = Background("dotnet", "MyServer.dll", "--port", "8080");
Background(opt => opt.WorkingDirectory(dir).EnvironmentVariable("X", "1"), "dotnet", "run");

Why

Start() and Run() can't promise that the new process stays out of the caller's way:

  • On Windows, Process.Start passes every handle the caller can share to the child. That includes the stdout pipe of whoever is capturing the script's output, so $(script), pipes and test harnesses wait until the child exits.
  • On Unix, the child shares the caller's stdin, stdout, stderr and session.

Start() keeps its current behaviour, which is right for apps you open and leave.

What it does

  • Returns the program's own process id straight away.
  • No window, and none of the caller's handles or streams. The program's output goes nowhere.
  • Detached from the terminal:
    • Windows: CreateProcess with bInheritHandles=FALSE, CREATE_NO_WINDOW and CREATE_NEW_PROCESS_GROUP. The process gets a console no one can see, and programs it starts don't each open a window.
    • Linux: /bin/sh points the program's input and output at /dev/null, then replaces itself with setsid so the program leads its own session.
    • macOS: there's no setsid command, so it runs under nohup. It survives the terminal closing, but not a Ctrl+C sent while the script is still running. This is documented.
  • Like the other process methods: it uses CurrentFolder (or WorkingDirectory), takes environment variables, follows Echo, and throws Win32Exception for a program that can't be found.
  • Docs: the README gets a fourth row in the "ways to run something" table and a Background() section.

Tests

Tests/BackgroundHost is a small helper program that records what it was started with. BackgroundTests checks:

  • the returned id is the program's own;
  • it runs in the shell's folder, and the WorkingDirectory option overrides that;
  • environment variables arrive;
  • awkward arguments arrive unchanged: quotes, trailing backslashes, empty strings, tabs, non-ASCII text;
  • a caller whose output is captured can start a 60-second background process and exit, and reading its output finishes promptly. Flipping bInheritHandles to TRUE makes this test fail, so it does catch a leak;
  • on Linux, the process leads its own session.

Results:

  • Windows: the full suite passes, 321 tests with 1 skipped.
  • Linux (WSL, Ubuntu 24.04): the 7 Background tests pass on two runs. I didn't run the full suite there.
  • macOS: not tested.

🤖 Generated with Claude Code

tomlm and others added 4 commits November 4, 2026 08:15
No window, none of the caller's handles or streams (so $(script), pipes and test
harnesses are not held open), and cut loose from the terminal: its own process
group on Windows (CreateProcess with bInheritHandles=FALSE, CREATE_NO_WINDOW),
its own session on Linux (setsid), nohup on macOS. Returns the program's own pid.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tomlm
tomlm merged commit b7a1dbc into main Nov 4, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant