bashbash-trap

Bash Trap SIGINT Multiple Times Doesn't Work


I don't know what's wrong but this script doesn't work. It should be always goes back to menu everytime I press CTRL+C.

#!/bin/bash

func_info()
{
    clear
    echo "This is info page."
    read -p "Press CTRL+C to back to menu or press enter exit..."
    exit
}

func_menu()
{
    clear
    trap - SIGINT
    echo "This is menu page."
    read -p "Press enter to go to info page..."
    trap func_menu SIGINT
    func_info
}

func_menu

It works for the first CTRL+C but the second times it just doesn't works.

I'm new to this so please don't judge me ;)

Any helps appreciated :) Thanks.

EDIT:

Actually, I found this works

#!/bin/bash

func_info()
{
    clear
    echo "This is info page."
    read -p "Press CTRL+C to back to menu or press enter exit..."
    exit
}

func_menu()
{
    clear
    echo "This is menu page."
    read -p "Press enter to go to info page..."
    ( trap exit SIGINT; func_info )
    func_menu
}

func_menu

But is that OK?


Solution

  • As a rule of thumb, you want to do as little as possible during signal handling. Especially if you're intending to recover from the signal instead of exiting, it is generally preferable to simply record that the signal was sent and then actually handle the signal in your "normal" code.

    So instead of recursively calling functions (inside traps) in order to navigate, let's keep track of the current page and display it in a loop. Then, all the SIGINT handler has to do is update the current-page variable.

    Here's a working demo of a three-page flow like your example (using select to navigate, but any approach would work if you don't like select):

    pages=(info news contact)
    page=
    
    info() { echo "For info, see the news page"; }
    news() { echo "$(date +%Y-%m-%d): No news today"; }
    contact() { echo "We cannot be reached."; }
    
    while true; do
      trap - SIGINT
      clear
      if [[ -n "$page" ]]; then
        trap 'page=; continue' SIGINT
        echo "This is the ${page} page"
        "$page" # invokes the function named $page
        ( read -p $'\nPress CTRL+C to go back to menu or press enter to exit...' )
        exit
      else
        echo "This is the menu page"
        echo "Select a page number and press enter to go there."
        select page in "${pages[@]}"; do
          if [[ -n "$page" ]]; then break; fi
        done
      fi
    done
    

    Give it a whirl! It works great on my system (Bash 4.3)