{
  "$type": "site.standard.document",
  "canonicalUrl": "https://rednafi.com/shards/2026/03/background-jobs-inherited-fd/",
  "description": "Why & backgrounds execution but doesn't stop output from flooding your terminal.",
  "path": "/shards/2026/03/background-jobs-inherited-fd/",
  "publishedAt": "2026-03-28T00:00:00.000Z",
  "site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
  "tags": [
    "Shell",
    "Unix"
  ],
  "textContent": "I keep a brew update && brew upgrade && brew cleanup alias around. Every now and then I\nwrap it in a subshell and put an & on the end, expecting it to go to the background and\ncome back when it's done:\n\nBut download progress, upgrade logs, and cleanup messages keep printing to the terminal\nwhile I'm trying to do other things. (sleep 5) & works the way I'd expect: it vanishes,\nand the shell prints [1] + done when it finishes.\n\n---\n\nWhen a shell forks a background job, the child process inherits the parent's [file\ndescriptors]. File descriptors 0, 1, and 2 (stdin, stdout, stderr) all point at the same\nterminal the parent is using. & tells the shell to run the command without waiting for it\nto finish. It says nothing about where output goes.\n\nsleep never writes to fd 1 or fd 2, so there's nothing to see and backgrounding feels\nclean. Any command that does write to those descriptors prints to the terminal, because\nthat's where they still point.\n\nThis script makes it visible. It writes to both stdout and stderr once a second for five\nseconds:\n\nSave it as noisy.sh and background it with sh noisy.sh &. Output keeps printing over\nwhatever you're doing at the prompt.\n\nThe fix is to redirect stdout and stderr before backgrounding. &> is shorthand for\n>/path 2>&1, and it points both descriptors somewhere other than the terminal:\n\nNow the job runs silently, and the shell prints [1] + done when it finishes. The GIF below\nshows both runs back to back:\n\n![noisy.sh backgrounded with and without redirection][img_1]\n\nIf you want to keep the output for later, redirect to a file instead of /dev/null:\n\nGoing back to the brew command from earlier:\n\n---\n\nThe same inheritance applies to fd 0 (stdin), but the kernel won't let a background process\nread from the terminal. If it tries, the kernel sends SIGTTIN and the job gets\n[suspended]:\n\nIt sits there until you bring it back with fg. Backgrounding something that might prompt\nfor a password or a y/n confirmation can stall this way. The command isn't stuck. It's\nwaiting for terminal input it's not allowed to read.\n\nIf the command won't need input, redirecting output is enough. If it might prompt, handle\nthe prompts first or don't background it.\n\n\n\n\n[img_1]:\n    https://blob.rednafi.com/static/images/background-jobs-inherited-fd/bg_noisy.gif\n\n[file descriptors]:\n    https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html\n\n[suspended]:\n    https://www.gnu.org/software/bash/manual/html_node/Job-Control-Basics.html",
  "title": "Background jobs and inherited file descriptors"
}