0

I'm working on developing some tools in Python to be used in Linux via Bash or another CLI. These tools all print info to the terminal to let the user know what's going on. Some have progress percentages where the same line is printed to repeatedly.

I've got everything working how I'd like with print statements, however, occasionally these tools may be sent to the background in Bash. This is causing problems, since the print function seems to print to the foreground regardless of where the process is running.

How do I only print to the console when the script in the foreground. The script should keep running and just not print to the foreground when sent to the background. I imagine there's some whole topic I'm missing to do this, as I am new to programming as a whole. Even if I could get a pointer in the right direction, that would likely get me there.

Ehrack
  • 3
  • 3
  • when you send the process to the background it will stopped when try yo send output: `[1]+ Stopped your-scrpipt.py` – Diego Torres Milano Mar 08 '22 at 22:09
  • 1
    @Diego, no that's incorrect. You can stop it with Ctrl+Z but then you send it to the background with the `bg` command. At that point its stdout is still the terminal. – glenn jackman Mar 08 '22 at 22:20
  • @glennjackman, thanks for the correction. I should have added that depends on the TTY configuration (cbreak). – Diego Torres Milano Mar 08 '22 at 22:35
  • @glennjackman This situation is exactly when I'm running into this. Then and when running with & – Ehrack Mar 08 '22 at 23:27
  • 1
    Does this answer your question? [Shell Script That Can Check if it Was Backgrounded at Invocation](https://stackoverflow.com/questions/3609999/shell-script-that-can-check-if-it-was-backgrounded-at-invocation) – pjh Mar 08 '22 at 23:33

2 Answers2

1

Enabling TOSTOP in termios should work for your case

#! /usr/bin/env python3

import sys
import termios
import tty

try:
    [iflag, oflag, cflag, lflag, ispeed, ospeed, ccs] = termios.tcgetattr(sys.stdout)
    termios.tcsetattr(sys.stdout.fileno(), termios.TCSANOW, [iflag, oflag, cflag, lflag | termios.TOSTOP, ispeed, ospeed, ccs])
except termios.error:
    pass

...
# your script here
...
# restore the original state when done

so if your script tries to print to stdout when in background it's going to receive a SIGTTOU signal and stops.

This should work either if you start it with & or you send it to background later using job control (CTRL+Z).

edit

If you need it to continue running handle the SIGCONT signal

signal.signal(signal.SIGCONT, handler)

and in handler switch a flag

def handler(signum, frame):
    global do_print
    do_print = False
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • Nearly there, but not quite. I need to keep the script itself running and somehow only suppress the terminal output. This may put me on the path to that, though. Thanks! – Ehrack Mar 09 '22 at 16:53
  • Ahh, I think that edit will do it. I would think that since we switch that flag when it continues in the background that we would no longer need to enable TOSTOP, since it won't be attempting to print in the first place. Would that be the case? I may be misinterpreting the SIGCONT signal, though. These are new to me. – Ehrack Mar 10 '22 at 15:31
0

I'm not sure this is the most ideal or elegant solution, but here's what I've gotten to work based upon this answer to a similar question:

#! /usr/bin/env python3
import time, sys, os

def termianl_output(data): #Function to print to console ONLY if process in foreground
    if os.getpgrp() == os.tcgetpgrp(sys.stdout.fileno()):
        print(data)

count = 0
while count < 20:
    count += 1
    terminal_output(count)
    time.sleep(2)

I've simply put together a new function to handle printing that first checks whether the process group it's running in is the process group that has control of the terminal. If it's not, it does not print. This could be easily modified to instead output to a log file or something, though I don't need that in my case.

Ehrack
  • 3
  • 3
  • It doesn't work when the process changes its state (i.e. CTRL+Z + bg)... – Diego Torres Milano Mar 10 '22 at 01:15
  • @DiegoTorresMilano I've had success with this when using CTRL+Z + bg and it once again prints when I call it back to the foreground. It also works when adding & to the end of the command. Is there something in particular you see that would cause a problem? I'll give your suggestion a go shortly. Thanks for your input! – Ehrack Mar 10 '22 at 02:53
  • The indentation confused me and I thought you were evaluating `tcgetpgrp()` only once, but if you do it for every print should work. – Diego Torres Milano Mar 10 '22 at 03:13
  • @DiegoTorresMilano Ahh, I see now that I have it all goofy there. I'll try to fix that. – Ehrack Mar 10 '22 at 14:33