trap (or –on-signal function) triggers but only when using stdin

Summary

The issue at hand is related to signal handling in a shell script, specifically when using stdin versus running as a pipeline. The trap function is triggered when running as a pipeline, but not when using stdin directly.

Root Cause

The root cause of this issue lies in how signals are handled in shell scripts. When running as a pipeline, the script is able to catch the SIGINT signal (generated by Ctrl+C) and trigger the trap function. However, when using stdin, the signal is not propagated to the script, causing the trap function to not be triggered. The key causes are:

  • Signal propagation: Signals are not propagated to the script when using stdin.
  • Shell behavior: The shell’s behavior when running as a pipeline versus using stdin differs.

Why This Happens in Real Systems

This issue occurs in real systems due to the way signals are handled by the operating system and the shell. When a script is run as a pipeline, it is able to catch signals because it is the foreground process. However, when using stdin, the script is not the foreground process, and signals are not propagated to it. This can lead to unexpected behavior and difficulties in handling signals.

Real-World Impact

The real-world impact of this issue is significant, as it can cause scripts to not behave as expected when handling signals. This can lead to:

  • Unexpected termination: Scripts may not terminate cleanly, leading to unexpected behavior.
  • Resource leaks: Scripts may not release resources, such as file descriptors or locks, leading to resource leaks.
  • Difficulty in debugging: The unexpected behavior can make it difficult to debug and diagnose issues.

Example or Code

function args.or.stdin
  if count $argv > /dev/null
    printf "%s\n" $argv
  else
    cat
  end
end

function print
  if test -t 0
    printf "%s\n" $argv
  else
    tee /dev/tty
  end
end

function tmux.waitgroup
  set -l panes
  args.or.stdin $argv | while read -l cmd
    set -a panes (tmux new-window -d -P -F "#{pane_id}" "$cmd")
  end
end

function tmux.waitgroup.cleanup
  --on-signal SIGINT
  --on-signal SIGTERM
  --inherit-variable panes
  echo $panes
  for p_id in $panes
    if tmux has-session -t $p_id 2>/dev/null
      tmux kill-pane -t $p_id
    end
  end
end

functions -e tmux.waitgroup.cleanup

How Senior Engineers Fix It

Senior engineers fix this issue by understanding how signals are handled in shell scripts and using techniques such as:

  • Foreground process: Ensuring the script is the foreground process to catch signals.
  • Signal propagation: Using techniques to propagate signals to the script when using stdin.
  • Error handling: Implementing robust error handling to handle unexpected behavior.

Why Juniors Miss It

Junior engineers may miss this issue due to:

  • Lack of understanding: Not fully understanding how signals are handled in shell scripts.
  • Limited experience: Not having experience with signal handling and stdin.
  • Insufficient testing: Not thoroughly testing scripts to catch unexpected behavior.