I have a script called backup.sh that changes to /starting/path, reads folder names from active-services.txt, and enters each folder to run that folder's own backup.sh if one exists. It works correctly when launched from an interactive terminal, but a nightly cron run repeatedly logs the same folder and appears to create a fork bomb. The script uses Bash-specific commands such as pushd and popd, but it does not currently have a shebang. Why does its behavior differ under cron, and what should I change to make it reliable?
3 Answers
Cron is not an interactive terminal. It may use a different shell and does not load your normal login configuration, so PATH, HOME, aliases, and other environment settings can differ. Use absolute paths for commands and files, quote variable expansions, check whether cd and other important commands succeed, and consider running ShellCheck. Also, a loop like `for FOLDER in $(cat "$SERVICES")` is fragile; use a safer line-reading loop if folder names can contain whitespace.
The main problem is the missing shebang. Cron commonly runs commands using /bin/sh rather than Bash, and pushd/popd are Bash features. Add a shebang such as #!/usr/bin/env bash (or the exact Bash path on the system), and invoke the script with an absolute path from cron. Cron also provides a much smaller environment, so do not rely on the interactive shell's PATH or startup files.
After adding the shebang, the script worked as expected. I also used ShellCheck and changed several other parts based on its suggestions.
The recursion happens because each child script is effectively the same top-level script. It first changes back to the absolute starting directory, then finds the same backup.sh again and executes it. Since the child never returns, the parent never reaches popd or continues its loop. The failed pushd under the wrong shell leaves the working directory unchanged, making this especially easy to trigger. A separate launcher script, or distinct names for the dispatcher and per-folder scripts, would make the structure clearer.

Using cd is simpler here. You can also run the directory change in a subshell, which avoids needing pushd and popd at all.