Capturing Return Cod
To wait for all background processes and ensure they exit successfully, you can capture each exit code and check if any process failed.
Approach: Capture Exit Codes and Verify Success
#!/bin/bash
# Start process 1 in the background
long_running_command_1 &
pid1=$!
# Start process 2 in the background
long_running_command_2 &
pid2=$!
# Wait for both processes and capture their exit codes
wait "$pid1"
exit_code_1=$?
wait "$pid2"
exit_code_2=$?
# Check exit codes
if [[ $exit_code_1 -ne 0 || $exit_code_2 -ne 0 ]]; then
echo "Error: One or more processes failed." >&2
exit 1 # Fail the script if any process failed
fi
echo "All processes completed successfully."
exit 0
Explanation:
- Start each process in the background and capture its PID (
$!
). - Use
wait $pid
to wait for each process and capture its exit code. - Check exit codes:
- If any exit code is non-zero, print an error and exit the script with
exit 1
. - If all are successful, print a success message and exit normally.
- If any exit code is non-zero, print an error and exit the script with
Alternative: Dynamic Handling for Multiple Processes
From Dynamic Capture Example
Go to text ā
GT-Sandbox-Snapshot
Code
#!/bin/bash
# Function to simulate a long-running process
simulate_process() {
sleep "$1"
exit "$2" # Simulates success (0) or failure (non-zero)
}
# Array to store process PIDs
pids=()
# Start multiple background processes with different durations and exit codes
simulate_process 2 0 & pids+=($!) # Simulates success
simulate_process 3 1 & pids+=($!) # Simulates failure
simulate_process 1 0 & pids+=($!) # Simulates success
# Track overall success/failure
exit_status=0
# Wait for all processes and capture their exit codes
for pid in "${pids[@]}"; do
wait "$pid"
code=$?
echo "Process $pid finished with exit code $code"
if [[ $code -ne 0 ]]; then
exit_status=1 # Mark as failure if any process fails
fi
done
# Final exit based on process results
if [[ $exit_status -ne 0 ]]; then
echo "Error: One or more processes failed." >&2
exit 1
fi
echo "All processes completed successfully."
exit 0
Command to reproduce:
gt.sandbox.checkout.commit 593e717795f8e4e8d785 \
&& cd "${GT_SANDBOX_REPO}/bash" \
&& cmd.run.announce "./main.sh"
Recorded output of command:
Process 5932 finished with exit code 0
Process 5933 finished with exit code 1
Process 5934 finished with exit code 0
Error: One or more processes failed.
GT-Sandbox-Snapshot: with wait for PIDs function
Code
#!/bin/bash
###############################################################################
# simulate_process
# Simulates a long-running process.
#
# Arguments:
# $1 - Duration to sleep (in seconds)
# $2 - Exit code to simulate (0 for success, non-zero for failure)
#
# Exits:
# Exits with the provided exit code.
###############################################################################
simulate_process() {
sleep "$1"
exit "$2" # Simulates success (0) or failure (non-zero)
}
###############################################################################
# wait_for_processes
# Waits for all background processes specified by their PIDs.
#
# Arguments:
# List of PIDs to wait for.
#
# Returns:
# 0 if all processes exit successfully, 1 if any process fails.
#
# Side Effects:
# Prints the exit status of each process.
###############################################################################
wait_for_processes() {
local pids=("$@")
local exit_status=0
for pid in "${pids[@]}"; do
# Wait for the process to complete
wait "$pid"
local code=$?
echo "Process $pid finished with exit code $code"
# If any process fails, mark overall status as failure
if [[ $code -ne 0 ]]; then
exit_status=1
fi
done
return $exit_status
}
# Array to store process PIDs
pids=()
# Start multiple background processes with different durations and exit codes
simulate_process 2 0 & pids+=("$!") # Simulates success
simulate_process 3 1 & pids+=("$!") # Simulates failure
simulate_process 1 0 & pids+=("$!") # Simulates success
# Wait for all processes and check the overall exit status
wait_for_processes "${pids[@]}"
result=$?
if [[ $result -ne 0 ]]; then
echo "Error: One or more processes failed." >&2
exit 1
fi
echo "All processes completed successfully."
exit 0
Command to reproduce:
gt.sandbox.checkout.commit 3fed59b2dc997683b5c4 \
&& cd "${GT_SANDBOX_REPO}/bash" \
&& cmd.run.announce "./main.sh"
Recorded output of command:
Process 6920 finished with exit code 0
Process 6921 finished with exit code 1
Process 6924 finished with exit code 0
Error: One or more processes failed.
Appears to work OK with interrupting
SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON=5
START_MILLIS=$(date +%s%3N)
echo_log() {
local millis_elapsed_since_start=$(( $(date +%s%3N) - START_MILLIS ))
echo_dim "[$BASHPID][elapsed: ${millis_elapsed_since_start:?}] ${*}"
}
export -f echo_log
###############################################################################
# simulate_process
# Simulates a long-running process.
#
# Arguments:
# $1 - Duration to sleep (in seconds)
# $2 - Exit code to simulate (0 for success, non-zero for failure)
#
# Exits:
# Exits with the provided exit code.
###############################################################################
simulate_process() {
local seconds_to_sleep="${1:?seconds_to_sleep}"
echo_log "simulate_process that will sleep for ${seconds_to_sleep:?} seconds: \$\$ = $$ (parent), \$BASHPID = $BASHPID (child)"
sleep "${seconds_to_sleep:?}"
if [[ "${seconds_to_sleep:?}" == "${SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON:?}" ]]; then
echo_log "Enough of sleeping, let's throw an error"
throw "throw on sleep of ${SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON:?} on purpose"
fi
exit "$2" # Simulates success (0) or failure (non-zero)
}
###############################################################################
# wait_for_processes
# Waits for all background processes specified by their PIDs.
#
# Arguments:
# List of PIDs to wait for.
#
# Returns:
# 0 if all processes exit successfully, 1 if any process fails.
#
# Side Effects:
# Prints the exit status of each process.
###############################################################################
wait_for_processes() {
local pids=("$@")
local exit_status=0
for pid in "${pids[@]}"; do
# Wait for the process to complete
wait "$pid"
local code=$?
echo_log "Process $pid finished with exit code $code"
# If any process fails, mark overall status as failure
if [[ $code -ne 0 ]]; then
exit_status=1
fi
done
return $exit_status
}
main() {
echo_log "Starting main script: \$\$ = $$, \$BASHPID = $BASHPID"
# Array to store process PIDs
pids=()
# Start multiple background processes with different durations and exit codes
simulate_process 1 0 & pids+=("$!") # Simulates success
simulate_process 2 0 & pids+=("$!") # Simulates success
simulate_process ${SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON:?} 0 & pids+=("$!") # Simulates failure
# Wait for all processes and check the overall exit status
wait_for_processes "${pids[@]}"
result=$?
if [[ $result -ne 0 ]]; then
echo_log "Error: One or more processes failed." >&2
exit 1
fi
echo_log "All processes completed successfully."
exit 0
}
main "${@}" || exit 1
Snapshot:
* 9405c20 - (0 seconds ago) [2025-01-31T22-10-16PST] Modified files: main.sh - nickolaykondratyev (HEAD -> bash)
GT-Sandbox-Snapshot: Final snapshot of playing with waiting for processes to complete.
Code
SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON=5
START_MILLIS=$(date +%s%3N)
echo_log() {
local millis_elapsed_since_start=$(( $(date +%s%3N) - START_MILLIS ))
echo_dim "[$BASHPID][elapsed: ${millis_elapsed_since_start:?}] ${*}"
}
export -f echo_log
###############################################################################
# simulate_process
# Simulates a long-running process.
#
# Arguments:
# $1 - Duration to sleep (in seconds)
# $2 - Exit code to simulate (0 for success, non-zero for failure)
#
# Exits:
# Exits with the provided exit code.
###############################################################################
simulate_process() {
local seconds_to_sleep="${1:?seconds_to_sleep}"
echo_log "simulate_process that will sleep for ${seconds_to_sleep:?} seconds, and exit with [${2}]: \$\$ = $$ (parent), \$BASHPID = $BASHPID (child)"
sleep "${seconds_to_sleep:?}"
if [[ "${seconds_to_sleep:?}" == "${SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON:?}" ]]; then
echo_log "Enough of sleeping, let's throw an error"
throw "throw on sleep of ${SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON:?} on purpose"
fi
exit "$2" # Simulates success (0) or failure (non-zero)
}
###############################################################################
# wait_for_processes
# Waits for all background processes specified by their PIDs.
#
# Example code:
# ```
# # Array to store process PIDs
# pids=()
#
# # Start multiple background processes with different durations and exit codes
# simulate_process 1 0 & pids+=("$!") # Simulates success
# simulate_process 2 0 & pids+=("$!") # Simulates success
# simulate_process 3 1 & pids+=("$!") # Simulates success
#
# # Wait for all processes and check the overall exit status
# if wait_for_processes "${pids[@]}"; then
# echo.green "All processes completed successfully."
# else
# echo.red "Error: One or more processes failed." >&2
# exit 1
# fi
# ```
# --------------------------------------------------------------------------------
# Arguments:
# List of PIDs to wait for.
#
# Returns:
# 0 if all processes exit successfully, 1 if any process fails.
#
# Side Effects:
# Prints the exit status of each process.
#
###############################################################################
wait_for_processes() {
local pids=("$@")
local exit_status=0
for pid in "${pids[@]}"; do
echo_log "Waiting for process $pid to complete"
# Wait for the process to complete
wait "$pid"
local code=$?
echo_log "Process $pid finished with exit code $code"
# If any process fails, mark overall status as failure
if [[ $code -ne 0 ]]; then
exit_status=1
fi
done
return $exit_status
}
main() {
echo_log "Starting main script: \$\$ = $$, \$BASHPID = $BASHPID"
# Array to store process PIDs
pids=()
# Start multiple background processes with different durations and exit codes
simulate_process 1 0 & pids+=("$!") # Simulates success
simulate_process 2 0 & pids+=("$!") # Simulates success
simulate_process 3 1 & pids+=("$!") # Simulates success
# simulate_process ${SLEEP_DELAY_THAT_WE_WANT_TO_THROW_ON:?} 0 & pids+=("$!") # Simulates failure
# Wait for all processes and check the overall exit status
if wait_for_processes "${pids[@]}"; then
echo.green "All processes completed successfully."
else
echo.red "Error: One or more processes failed." >&2
exit 1
fi
}
main "${@}" || exit 1
Command to reproduce:
gt.sandbox.checkout.commit 6337812d19525a437b28 \
&& cd "${GT_SANDBOX_REPO}/bash" \
&& cmd.run.announce "./main.sh"
Recorded output of command:
[11649][elapsed: 6] Starting main script: $$ = 11649, $BASHPID = 11649
[11652][elapsed: 13] simulate_process that will sleep for 1 seconds, and exit with [0]: $$ = 11649 (parent), $BASHPID = 11652 (child)
[11655][elapsed: 14] simulate_process that will sleep for 3 seconds, and exit with [1]: $$ = 11649 (parent), $BASHPID = 11655 (child)
[11653][elapsed: 14] simulate_process that will sleep for 2 seconds, and exit with [0]: $$ = 11649 (parent), $BASHPID = 11653 (child)
[11649][elapsed: 14] Waiting for process 11652 to complete
[11649][elapsed: 1037] Process 11652 finished with exit code 0
[11649][elapsed: 1046] Waiting for process 11653 to complete
[11649][elapsed: 2036] Process 11653 finished with exit code 0
[11649][elapsed: 2046] Waiting for process 11655 to complete
[11649][elapsed: 3038] Process 11655 finished with exit code 1
Error: One or more processes failed.
Children