1#!/usr/bin/env python3 2# 3# tee, but for pipes 4# 5# Example: 6# ./scripts/tee.py in_pipe out_pipe1 out_pipe2 7# 8# Copyright (c) 2022, The littlefs authors. 9# SPDX-License-Identifier: BSD-3-Clause 10# 11 12import os 13import io 14import time 15import sys 16 17 18def openio(path, mode='r', buffering=-1): 19 # allow '-' for stdin/stdout 20 if path == '-': 21 if mode == 'r': 22 return os.fdopen(os.dup(sys.stdin.fileno()), mode, buffering) 23 else: 24 return os.fdopen(os.dup(sys.stdout.fileno()), mode, buffering) 25 else: 26 return open(path, mode, buffering) 27 28def main(in_path, out_paths, *, keep_open=False): 29 out_pipes = [openio(p, 'wb', 0) for p in out_paths] 30 try: 31 with openio(in_path, 'rb', 0) as f: 32 while True: 33 buf = f.read(io.DEFAULT_BUFFER_SIZE) 34 if not buf: 35 if not keep_open: 36 break 37 # don't just flood reads 38 time.sleep(0.1) 39 continue 40 41 for p in out_pipes: 42 try: 43 p.write(buf) 44 except BrokenPipeError: 45 pass 46 except FileNotFoundError as e: 47 print("error: file not found %r" % in_path) 48 sys.exit(-1) 49 except KeyboardInterrupt: 50 pass 51 52 53if __name__ == "__main__": 54 import sys 55 import argparse 56 parser = argparse.ArgumentParser( 57 description="tee, but for pipes.", 58 allow_abbrev=False) 59 parser.add_argument( 60 'in_path', 61 help="Path to read from.") 62 parser.add_argument( 63 'out_paths', 64 nargs='+', 65 help="Path to write to.") 66 parser.add_argument( 67 '-k', '--keep-open', 68 action='store_true', 69 help="Reopen the pipe on EOF, useful when multiple " 70 "processes are writing.") 71 sys.exit(main(**{k: v 72 for k, v in vars(parser.parse_intermixed_args()).items() 73 if v is not None})) 74