Skip to content

Instantly share code, notes, and snippets.

@devilholk
Created November 25, 2020 21:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devilholk/46a0477bea4d0bf836563199afb18723 to your computer and use it in GitHub Desktop.
Save devilholk/46a0477bea4d0bf836563199afb18723 to your computer and use it in GitHub Desktop.
Watch command output on linux [WIP]
import sys, time, subprocess, threading, queue, os, fcntl
'''
For some reason, even with all the flushing, if python is running buffered (without -u), it will sometimes not flush!
Until I can figure this one out I'll use -u
Even with -u it sometimes will output stdout and stderr in different order, some buffering somewhere is messing things up.
As is this is still a decent tool for watching output that contains ANSI escapes watch -c won't manage.
'''
clear_cmd = b'\x1b[H\x1b[2J\x1b[3J'
output_lock = threading.Lock()
class buffer_node(threading.Thread):
def __init__(self, source_fd, dest_stream, pending, output_lock):
super().__init__()
self.source = os.fdopen(source_fd, 'rb', buffering=0)
self.dest = dest_stream
self.pending = pending
self.output_lock = output_lock
def run(self):
while 1:
try:
data = self.source.read()
except ValueError:
break
except OSError:
break
if self.pending.empty():
self.dest.write(data)
else:
with self.output_lock:
while True:
try:
pending_data, pending_flush = self.pending.get_nowait()
self.dest.write(pending_data)
if pending_flush:
self.dest.flush()
except queue.Empty:
break
self.dest.write(data)
def run_command(cmd, header=None):
#Create queue
pending = queue.Queue()
#Add clear ANSI command and header
if header:
pending.put((clear_cmd + bytes(header, sys.getdefaultencoding()), True))
else:
pending.put((clear_cmd, True))
#Create pipes
stderr_r, stderr_w = os.pipe2(0)
stdout_r, stdout_w = os.pipe2(0)
#Create buffer processors
stdout_buffer = buffer_node(stdout_r, sys.stdout.buffer, pending, output_lock)
stderr_buffer = buffer_node(stderr_r, sys.stderr.buffer, pending, output_lock)
#Start threads
stdout_buffer.start()
stderr_buffer.start()
#Create output streams
child_stderr = os.fdopen(stderr_w, 'wb', buffering=0)
child_stdout = os.fdopen(stdout_w, 'wb', buffering=0)
#Execute command
subprocess.call(cmd, stderr=child_stderr, stdout=child_stdout, stdin=subprocess.DEVNULL, bufsize=0)
child_stderr.flush()
child_stdout.flush()
child_stderr.close()
child_stdout.close()
stdout_buffer.source.flush()
stderr_buffer.source.flush()
stdout_buffer.source.close()
stderr_buffer.source.close()
sys.stdout.flush()
sys.stderr.flush()
wait = 1
cmd = sys.argv[1:]
while True:
run_command(cmd, f'\x1b[3m{time.ctime()}\x1b[23m - Every \x1b[1m{wait}\x1b[21m seconds, execute: \x1b[3;1m{" ".join(cmd)}\x1b[23;21m\n\n')
time.sleep(wait)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment