简体   繁体   中英

What is the elegant way of running and monitoring multiple shell commands together?

Here is an example of what I am doing; this shell snippet runs a django web server as well as an npm process that runs webpack (both monitor for file changes and reload/recompile, etc). I've combined them for convenience into one command I can run via npm, but I feel like I'm forgetting something simple that looks better than this:

p=0; q=0; endit() { kill -9 $p; kill -9 $q; }; trap endit INT; records/manage.py runserver & p=$!; npm run watch & wait; q=$!; wait $p; wait $q; endit

Help me and my aging memory: :)

You can get rid of p and q variables by using kill $(jobs -p) to terminate all running jobs. Similarly, wait without arguments will wait until all running jobs finish.

2 more notes on your code:

  • You should call exit in trap command, otherwise your script will continue even after it receives SIGINT (causing the final wait call to fail, since processes have been already terminated).
  • You should not call trap command ( endit() ) in the end of the script, because at this point processes have already finished due to wait call.

I tested the code with a demonstrative sleep command:

trap 'kill $(jobs -p); exit' INT;
sleep 5 &
sleep 5 &
wait

This is the simplest version of what I was thinking of, I guess. Usage in my case would be:

runon.py 'records/manage.py runserver' 'npm run watch'

This works, just feels like there's still something simpler.

#!/usr/bin/env python3

import sys
import argparse
import subprocess
import time
import shlex

parser = argparse.ArgumentParser(
    description='Manage multiple running commands.')
parser.add_argument('cmds', metavar="'cmdline -opts'", type=str, nargs='+',
                    help='a shell command to run')
args = parser.parse_args()

calls = {}.fromkeys(args.cmds)
for cmd in calls.keys():
    calls[cmd] = subprocess.Popen(shlex.split(cmd), text=True)
    print'start(%d): %s' % (calls[cmd].pid, cmd)

while calls:
    finished = []
    for name, call in calls.items():
        ret = call.poll()
        if ret is not None:
            print('return(%d): %s' % (ret, name))
            finished.append(name)
    for n in finished:
        calls.pop(n)
    time.sleep(0.5)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM