Home > Software design >  Getting a shell script to wait for a specific response from python program
Getting a shell script to wait for a specific response from python program

Time:12-07

I'm trying to write a script that calls a python program, pauses until the program outputs a certain message (not when the program ends), then moves on.

If I have example.py like this that just runs indefinitely:

doSomething()
print('I did something')
doSomethingElseForever()

I want to have a script something like this:

echo "Running python program"
python3 example.py &
waitForOutput("I did something")
echo "Python program did something, it's still running in the background"
doSomeMoreScriptStuff

Ideally, I would just run the script and it would launch example.py, pause until example.py output "I did something", then continue on to launch more programs or whatever, without stopping example.py (hence the &).

I'm working in Ubuntu BTW.

Edit: I think in my particular application at the moment I could just change my python script to create a file instead of print(), but for the sake of learning (and because that seems like a convoluted way to do things), let's assume I can't edit the python file. So all I have to work with is the known output of a print() statement.

CodePudding user response:

You can do this with pexpect. I start a thread which calls pexpect.spawn() to run your child script. It waits for the output you want and then sets an event so the main program knows the event has occurrred. It then waits for the child to exit, which, in turn, allows the main program to exit:

#!/usr/bin/env python3

import pexpect
import time
import threading

doneIt = threading.Event()

def childHandler():
   """Spawns the child and manages its output"""
  
   global doneIt 
   print('HANDLER: Spawning child')
   child = pexpect.spawn('./example.sh')
   print('HANDLER: Child spawned, waiting for it to do something')
   child.expect('I did something')
   doneIt.set()
   print('HANDLER: Waiting for child to finish')
   child.expect(pexpect.EOF)
   print('HANDLER: Done')

if __name__ == "__main__":
   print('MAIN: Starting')
   cH = threading.Thread(target=childHandler)
   cH.start()

   # Wait for event and do other stuff, like printing
   while True:
      print('MAIN: waiting for child to do something')
      if doneIt.wait(1):
          break

   print('MAIN: Continuing...')

   # Wait till childHandler exits before exiting
   cH.join() 
   print('MAIN: Exit')

I used this example.sh as a mock-up of the Python script you call:

#!/bin/bash

LOG="run.txt"
date  'Running: %H:%M:%S' > "$LOG"
for ((i=8;i>0;i--)) ; do
  date  'Running: %H:%M:%S' >> "$LOG"
  sleep 1
done

date  'SUCCESS: %H:%M:%S' >> "$LOG"
echo "I did something"

for ((i=1;i<10;i  )) ; do
  date  'Continuing: %H:%M:%S' >> "$LOG"
  sleep 1
done

date  'DONE: %H:%M:%S' >> "$LOG"

And here is the log:

Running: 13:05:27
Running: 13:05:27
Running: 13:05:28
Running: 13:05:29
Running: 13:05:30
Running: 13:05:31
Running: 13:05:32
Running: 13:05:33
Running: 13:05:34
SUCCESS: 13:05:35
Continuing: 13:05:35
Continuing: 13:05:36
Continuing: 13:05:37
Continuing: 13:05:38
Continuing: 13:05:39
Continuing: 13:05:40
Continuing: 13:05:41
Continuing: 13:05:42
Continuing: 13:05:43
DONE: 13:05:44
  • Related