2

I have a shell program in Ubuntu 16.04, named foo, that outputs status update when I press any button. It's located in /usr/local/bin/foo, so I can call the program anywhere. The program works like this:

$ foo
Welcome

after I press a key, it will show:

Time now is 01:23:45

If I press Ctrl-C, it will exit just like most of the other shell programs.

I would like this foo program to be run in multiple instances. I know GNU Screen command can switch between tasks, but is it the correct way to do so?

Alternatively, I know that I can put the program to run in the background like this:

$ foo &
[1]  123456

Job ID 1 is created in the background and I can call it out by its Job ID:

$ fg %1

However, I need to put it back to background after obtaining the latest output of the program (by pressing any button: is there any method to do this programmatically?). How can I achieve this?

1

From what I can see, your requirements are to be able to:

  1. Run several instances of the program concurrently.
  2. Get terminal control over any of the instances at any time.
  3. Send instances back to background as soon as they output.

If we stick to the & approach, then yes, there is a shortcut for (3). When a program has control over a terminal, it can be sent to the background using a SIGTSTP signal. Conveniently enough, that's usually what Ctrl + Z does.

You could therefore have something like this:

$ foo &
Welcome
[1] 10

$ foo &
Welcome
[2] 11

$ fg %1
<ENTER>
Time now is 01:23:45
<Ctrl+Z>
[1]+ Stopped

To my knowledge, there is no such shortcut for fg (and how would you indicate which instance to bring back anyway?)

Note that the SIGTSTP signal does not actually need to come from the shell which started the process. If you kill -TSTP the process by PID in a second shell, you'll also be able to get it back with fg in the first.

I need to put it back to background after obtaining the latest output of the program (by pressing any button: is there any method to do this programmatically?)

If by programmatically you mean that you have access to foo's source code, then you can use what we've just seen. The raise system call can be used by a program to send itself a signal. If we take a very simplistic version of foo, we can have something like this:

#include <stdio.h>
#include <signal.h>

int main(void)
{
    printf("Welcome");
    while(getchar()) { /* Use termios to switch to unbuffered mode */
        fprintf(stdout, "The time is... something");
        fflush(stdout);
        fflush(stdin);
        raise(SIGTSTP);
    }

    return 0;
}

Now while this is all reasonably nice, I would agree that screen is probably a nicer way to do all this, mostly because you can name your screen sessions and therefore never need to remember a job or process ID. Just like the & approach, it also has the advantage of not requiring changes to the source code.

$ screen -S foo1
$ ./foo
Welcome
<Ctrl-a d>
[detached from 10.foo1]

$ screen -s foo1
<ENTER>
Time now is 01:23:45
<Ctrl-a d>
[detached from 10.foo1]

Another advantage here is that you do not need to stop your program before detaching. That's pretty convenient if you want it to do stuff in the background while you're not looking. In the previous approach, you would need to run bg every time you stop foo.

You can also get a list of running screens with screen -ls. On my machine, I've setup a few aliases in ~/.bashrc to account for my extra laziness:

alias S='screen -S'
alias s='screen -r'
alias sls='screen -ls'

This way you could simply do things like S foo1, s foo1, ... Some might argue than one-character aliases can be a little annoying when you misplace a space or something in your commands, but I think screen is one of those programs which are so convenient that you can afford to make an exception.

Now assuming you're going with the screen approach, here's how you could send a key to a session and fetch its last line of output from a shell. There are ways to tweak this (and I've allowed myself some shortcuts) but anyway. You can define the following function in ~/.bashrc :

screen_key_tail() {
    screen -S $1 -X stuff "^M"
    tmp="$(mktemp)"
    screen -S $1 -X hardcopy "$tmp"
    grep -v ^$ "$tmp" | tail -n1
    rm "$tmp"
}

and call it with:

$ screen_key_tail foo1

where foo1 is the name of the screen session you're targetting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.