bash break from a read inside trap

Viewed 61

Trying to restore echo when user interrupts a read from this bash shell function that they call from their shell:

setsecret() { stty -echo; read $1; stty echo; }

If you type ctrl-c during the read, the stty echo is not called, so I have to do it manually from the shell. Not a huge deal, but would prefer to not have to do this.

So far I have this:

break_from_setsecret() { 
    stty echo
    trap - INT
    echo "Press ENTER alone to cancel command (instead of Ctrl-C, which would mess up your terminal echo)"
}
setsecret() { 
    trap break_from_setsecret INT
    echo "Type in a secret and press ENTER (press ENTER alone to cancel the commmand):"
    stty -echo
    read $1 
    stty echo
}

This is a net improvement over having to type "stty echo" while echo is off in the shell. But not perfect:

  • the read actually resumes after the handler returns; so you have to press ENTER (per the message I added in handler)
  • if instead of pressing ENTER, you type ctrl-c a second time (ie after the handler echo), the shell exits (but if you press enter and the command ends, you can type ctrl-c fine in the shell, everything is back to normal).

I tried cancelling the read by "re-raising" the signal from the break_from_setsecret via kill -n INT $$ but that did not work (it exits my shell). Is there a way to do that?

Alternatively, if there was a way to register a cleanup function that setsecret() would call when it ends (even from ctrl-c), but I don't think bash supports this. I tried with trap ERR instead of INT but handler does not get called on a ctrl-c during read (eventhough if you ctrl-c during a normal read, the exit code of read is non-zero).

EDIT: Latest workaround is to gobble up the ctrl-c by changing the handler to this:

break_from_setsecret() { 
    echo "Press ENTER alone to cancel command (instead of Ctrl-C, which would mess up your terminal echo)"
}

But it would still be nice to have the cleanup approach work.

1 Answers

You can set trap on SIGNT and RETURN to set stty echo back:

setsecret() { 
    stty -echo
    trap 'stty echo; trap - SIGINT RETURN; echo; return' SIGINT RETURN
    read -p 'Type in a secret (pressing ENTER or ctrl-C will cancel this commmand): ' "$1"
}

Related