ogr
ogr

Reputation: 690

How to immediately trap a signal to an interactive Bash shell?

I try to send a signal from one terminal A to another terminal B. Both run an interactive shell.

In terminal B, I trap signal SIGUSR1 like so :

$ trap 'source ~/mycommand' SIGUSR1

Now in terminal A I send a signal like so :

$ kill -SIGUSR1 pidOfB

Unfortunately, nothing happens in B. If I want to have my command executed, I need to switch to B and either input a new command or press enter.

How can I avoid this drawback and immediately execute my command instead ?

EDIT :

It's important to note that I want to interact directly with the interactive shell in terminal B from terminal A. For this reason, every solution where the trap command would be executed in a subshell would not work for me...

Also, terminal B must stay interactive.

Upvotes: 4

Views: 1642

Answers (4)

Bill Ashmanskas
Bill Ashmanskas

Reputation: 11

I use a similar trap so that periodically I can (from a separate cron job) force all idle bash processes to do a 'history -a'. I found that if I trap SIGALRM instead of SIGUSR1, then the bash blocking read seems not to be a problem: the trap runs now, rather than next time one hits return. I tried SIGINT, but that caused an annoying "^C", followed by a new prompt line, to be displayed. I haven't yet found any drawbacks of using SIGALRM, but perhaps they will arise.

Upvotes: 1

ogr
ogr

Reputation: 690

Based on the answers and my numerous attempt to solve this, I don't think it's possible to catch a trap signal immediately in an interactive bash terminal.

For it to trigger, there must be an interaction from the user.

This is due to the readline program blocks until a newline is entered. And there is no way to stop this read.

My solution is to use dtach, a small program that emulate the detach feature of screen.

This program can run a fully interactive shell and features in its last version a way to communicate via a custom socket to this shell (or whatever program you launch)

To start a new dtach session running an interactive bash, in terminal B :

$ dtach -a /tmp/MySocket bash -i

Now from terminal A, we can send a message to the bash session in terminal B like so :

$ echo 'echo hello' | dtach -p /tmp/MySocket

In terminal B, we now see :

$ echo hello
hello

To expand on that if I now do in terminal A :

$ trap 'echo "cd $(pwd)" | dtach -p /tmp/MySocket' DEBUG

I'll have the directory of the two terminals synced

PS :I'd still like to know if there is a way to do this in pure bash

Upvotes: 2

chepner
chepner

Reputation: 531055

The shell may simply be stuck in a blocking read, waiting for command-line input. Hitting enter causes the handler to execute before the entered command. Running a non-blocking command like wait:

$ sleep 60 & wait

then sending the signal causes wait to terminate immediately, followed by the output of the handler.

Upvotes: 1

Paul Hodges
Paul Hodges

Reputation: 15273

It may be buffering.

As a test, try installing a loop trigger. In window A:

{ trap 'ls' USR1; while sleep 1; do echo>/dev/null;done } &
[1] 7316

in window B:

 kill -usr1 7316

back in window A the ls is firing when the loop does an echo. Don't know if that will help, but it's something.

Upvotes: 0

Related Questions