def main(): atexit.register(_cleanup_resources) signal.signal(signal.SIGPIPE, signal.SIG_DFL) mapping, excluded = load_mapping() if excluded: print( 'Found invalid entries in the mapping:\n' + excluded + '\nRun ' + style.cmd('dtags clean') + ' to remove them' + '\n' ) # Parse the optional arguments parallel = False args = sys.argv[1:] if not args: finish(USAGE + DESCRIPTION) head, tail = args[0], args[1:] if head == '--help': finish(USAGE + DESCRIPTION) elif head == '--version': finish('Version ' + VERSION) elif head == '-p' and not tail: abort(USAGE + 'Missing argument: ' + style.bad('<targets>')) elif head == '-p' and tail: parallel = True head, tail = tail[0], tail[1:] elif head.startswith('-'): abort(USAGE + 'Invalid argument: ' + style.bad(head)) # Parse the positional arguments if not tail: abort(USAGE + 'Missing argument: ' + style.bad('<command>')) directories = collections.defaultdict(set) for target in head.split(','): if not target: continue elif target in mapping: for directory in sorted(mapping[target]): directories[directory].add(target) else: path = expand(target) if os.path.isdir(path): directories[path].add(None) else: abort(USAGE + 'Invalid target: ' + style.bad(target)) command = sp.list2cmdline(tail) # Check which shell is in use (e.g. zsh, bash, fish) shell = os.environ.get('SHELL') if shell is None: abort('Undefined environment variable: ' + style.bad('SHELL')) # Execute the command in the targeted directories msg_head = style.msg('Executing command ') + style.cmd(command) if parallel: global temp_file, process, processes # Add signal handlers to terminate child processes gracefully signal.signal(signal.SIGINT, _handle_signal) signal.signal(signal.SIGABRT, _handle_signal) signal.signal(signal.SIGTERM, _handle_signal) # Execute in parallel and pipe the output to temporary files sys.stdout.write(msg_head + style.msg(' in parallel...\n\n')) for directory in sorted(directories): tags = directories[directory] temp_file = tempfile.TemporaryFile(mode='w+t') process = sp.Popen( [shell, '-i', '-c', command], cwd=directory, stdout=temp_file, stderr=temp_file, preexec_fn=os.setsid ) processes.append((directory, tags, process, temp_file)) # Read from the temporary files back to stdout line by line for directory, tags, process, temp_file in processes: status = process.wait() tags = [style.tag(tag) for tag in tags if tag] sys.stdout.write('{} {}{}\n'.format( style.msg('in'), style.path(directory), ((' ' + ' '.join(tags)) if tags else '') + style.msg(':') )) temp_file.seek(0) for line in temp_file: sys.stdout.write(line) temp_file.close() sys.stdout.write(style.msg('Exit status: {}\n\n'.format(status))) else: # Generate the command string and execute all in one subprocess call commands = [] status_printf = 'printf "{}\n\n"'.format(style.msg( 'Exit status: ' + ('$status' if '/fish' in shell else '$?') )) for directory in sorted(directories): tags = [style.tag(tag) for tag in directories[directory] if tag] commands.append('printf "{} {}{}\n"; cd "{}"; {};{}'.format( style.msg('in'), style.path(directory), ((' ' + ' '.join(tags)) if tags else '') + style.msg(':'), directory, command, status_printf )) sys.stdout.write(msg_head + style.msg(' in sequence...\n\n')) sys.stdout.flush() # flush for printing chronologically sp.call([shell, '-i', '-c', ';'.join(commands)], stderr=sp.STDOUT)
DESCRIPTION = """ Arguments: dir The directory path tag The directory tag Options: --help Display the help menu --version Display the version Change directory. If the destination is not specified it defaults to the home directory. If multiple directories are associated with the same tag, a selection menu is displayed. """ ARG_ERR = '%sd: invalid argument: %s\n' CHOICE = '%s: %s\n' DEST_ERR = 'd: invalid destination: %s\n' INDEX_ERR = 'd: index out of range: %s\n' INPUT_ERR = 'd: invalid input: %s\n' PROMPT = '\nSelect directory (1 - %s): ' # Style the messages if the output is going to a terminal tty = True ARG_ERR_TTY = '%sd: invalid argument: ' + style.bad('%s\n', tty=tty) CHOICE_TTY = style.msg('%s: ', tty=tty) + style.path('%s\n', tty=tty) DEST_ERR_TTY = 'd: invalid destination: ' + style.bad('%s\n', tty=tty) INDEX_ERR_TTY = 'd: index out of range: ' + style.bad('%s\n', tty=tty) INPUT_ERR_TTY = 'd: invalid input: ' + style.bad('%s\n', tty=tty) PROMPT_TTY = style.msg('\nSelect directory (1 - %s): ', tty=tty)
def main(): atexit.register(close_stdio) signal.signal(signal.SIGPIPE, signal.SIG_DFL) mapping, excluded = load_mapping() if excluded: print( 'Found invalid entries in the mapping:\n' + excluded + '\nRun ' + style.cmd('dtags clean') + ' to remove them' + '\n' ) # Parse the optional arguments interactive = False args = sys.argv[1:] if not args: finish(USAGE + DESCRIPTION) head, tail = args[0], args[1:] if head == '--help': finish(USAGE + DESCRIPTION) elif head == '--version': finish('Version ' + VERSION) elif head == '-i' and not tail: abort(USAGE + 'e: missing argument: ' + style.bad('<targets>')) elif head == '-i' and tail: interactive = True head, tail = tail[0], tail[1:] elif head.startswith('-'): abort(USAGE + 'e: invalid argument: ' + style.bad(head)) # Parse the positional arguments if not tail: abort(USAGE + 'e: missing argument: ' + style.bad('<command>')) directories = collections.defaultdict(set) for target in head.split(','): if not target: continue elif target in mapping: for directory in sorted(mapping[target]): directories[directory].add(target) else: path = expand(target) if os.path.isdir(path): directories[path].add(None) else: abort(USAGE + 'e: invalid target: ' + style.bad(target)) cmd = sp.list2cmdline(tail) # Check which shell is in use (e.g. zsh, bash, fish) shell = os.environ.get('SHELL') if shell is None: abort('e: undefined environment variable: ' + style.bad('SHELL')) # Generate the command string and execute all in one subprocess call cmds = [] if 'fish' in shell: status_printf = ( 'if [ $status = 0 ]; printf "\n\n"; else; printf "{}\n\n"; end' .format(style.bad('Exit status: $status')) ) else: status_printf = ( 'if [ $? -eq 0 ]; then printf "\n\n"; else printf "{}\n\n"; fi' .format(style.bad('Exit status: $?')) ) for directory in sorted(directories): tags = [style.tag(tag) for tag in directories[directory] if tag] cmds.append('printf "{} {}{}\n"; cd "{}"; {}; {}'.format( style.msg('in'), style.path(directory), ((' ' + ' '.join(tags)) if tags else '') + style.msg(':'), directory, cmd, status_printf )) sys.stdout.write('\n') sys.stdout.flush() # flush for printing chronologically if interactive: sp.call([shell, '-i', '-c', ';'.join(cmds)], stderr=sp.STDOUT) else: sp.call([shell, '-c', ';'.join(cmds)], stderr=sp.STDOUT)
def main(): global temp_file, process, processes atexit.register(_cleanup_resources) signal.signal(signal.SIGPIPE, signal.SIG_DFL) mapping, excluded = load_mapping() if excluded: print( 'Found invalid entries in the mapping:\n' + excluded + '\nRun ' + style.cmd('dtags clean') + ' to remove them' + '\n' ) # Parse the optional arguments interactive = False args = sys.argv[1:] if not args: finish(USAGE + DESCRIPTION) head, tail = args[0], args[1:] if head == '--help': finish(USAGE + DESCRIPTION) elif head == '--version': finish('Version ' + VERSION) elif head == '-i' and not tail: abort(USAGE + 'p: missing argument: ' + style.bad('<targets>')) elif head == '-i' and tail: interactive = True head, tail = tail[0], tail[1:] elif head.startswith('-'): abort(USAGE + 'p: invalid argument: ' + style.bad(head)) # Parse the positional arguments if not tail: abort(USAGE + 'p: missing argument: ' + style.bad('<command>')) directories = collections.defaultdict(set) for target in head.split(','): if not target: continue elif target in mapping: for directory in sorted(mapping[target]): directories[directory].add(target) else: path = expand(target) if os.path.isdir(path): directories[path].add(None) else: abort(USAGE + 'p: invalid target: ' + style.bad(target)) # Check which shell is in use (e.g. zsh, bash, fish) shell = os.environ.get('SHELL') if shell is None: abort('p: undefined environment variable: ' + style.bad('SHELL')) if interactive: cmd = [shell, '-i', '-c', sp.list2cmdline(tail)] else: cmd = [shell, '-c', sp.list2cmdline(tail)] # Add signal handlers to terminate child processes gracefully signal.signal(signal.SIGINT, _handle_signal) signal.signal(signal.SIGABRT, _handle_signal) signal.signal(signal.SIGTERM, _handle_signal) # Execute in parallel and pipe the output to temporary files for directory in sorted(directories): tags = directories[directory] temp_file = tempfile.TemporaryFile(mode='w+t') process = sp.Popen( cmd, cwd=directory, stdout=temp_file, stderr=temp_file, preexec_fn=os.setsid ) processes.append((directory, tags, process, temp_file)) # Read from the temporary files back to stdout line by line sys.stdout.write('\n') for directory, tags, process, temp_file in processes: status = process.wait() tags = [style.tag(tag) for tag in tags if tag] sys.stdout.write('{} {}{}\n'.format( style.msg('in'), style.path(directory), ((' ' + ' '.join(tags)) if tags else '') + style.msg(':') )) temp_file.seek(0) for line in temp_file: sys.stdout.write(line) temp_file.close() if status != 0: sys.stdout.write(style.bad('Exit status: {}\n\n'.format(status))) else: sys.stdout.write('\n\n')
def main(): atexit.register(_cleanup_resources) signal.signal(signal.SIGPIPE, signal.SIG_DFL) mapping, excluded = load_mapping() if excluded: print( 'Found invalid entries in the mapping:\n' + excluded + '\nRun ' + style.cmd('dtags clean') + ' to remove them' + '\n' ) # Parse the optional arguments parallel = False args = sys.argv[1:] if not args: finish(USAGE + DESCRIPTION) head, tail = args[0], args[1:] if head == '--help': finish(USAGE + DESCRIPTION) elif head == '--version': finish('Version ' + VERSION) elif head == '-p' and not tail: abort(USAGE + 'e: missing argument: ' + style.bad('<targets>')) elif head == '-p' and tail: parallel = True head, tail = tail[0], tail[1:] elif head.startswith('-'): abort(USAGE + 'e: invalid argument: ' + style.bad(head)) # Parse the positional arguments if not tail: abort(USAGE + 'e: missing argument: ' + style.bad('<command>')) directories = collections.defaultdict(set) for target in head.split(','): if not target: continue elif target in mapping: for directory in sorted(mapping[target]): directories[directory].add(target) else: path = expand(target) if os.path.isdir(path): directories[path].add(None) else: abort(USAGE + 'e: invalid target: ' + style.bad(target)) command = sp.list2cmdline(tail) # Check which shell is in use (e.g. zsh, bash, fish) shell = os.environ.get('SHELL') if shell is None: abort('e: undefined environment variable: ' + style.bad('SHELL')) # Execute the command in the targeted directories msg_head = style.msg('Executing command ') + style.cmd(command) if parallel: global temp_file, process, processes # Add signal handlers to terminate child processes gracefully signal.signal(signal.SIGINT, _handle_signal) signal.signal(signal.SIGABRT, _handle_signal) signal.signal(signal.SIGTERM, _handle_signal) # Execute in parallel and pipe the output to temporary files sys.stdout.write(msg_head + style.msg(' in parallel...\n\n')) for directory in sorted(directories): tags = directories[directory] temp_file = tempfile.TemporaryFile(mode='w+t') process = sp.Popen( [shell, '-i', '-c', command], cwd=directory, stdout=temp_file, stderr=temp_file, preexec_fn=os.setsid ) processes.append((directory, tags, process, temp_file)) # Read from the temporary files back to stdout line by line for directory, tags, process, temp_file in processes: status = process.wait() tags = [style.tag(tag) for tag in tags if tag] sys.stdout.write('{} {}{}\n'.format( style.msg('in'), style.path(directory), ((' ' + ' '.join(tags)) if tags else '') + style.msg(':') )) temp_file.seek(0) for line in temp_file: sys.stdout.write(line) temp_file.close() sys.stdout.write(style.msg('Exit status: {}\n\n'.format(status))) else: # Generate the command string and execute all in one subprocess call commands = [] status_printf = 'printf "{}\n\n"'.format(style.msg( 'Exit status: ' + ('$status' if '/fish' in shell else '$?') )) for directory in sorted(directories): tags = [style.tag(tag) for tag in directories[directory] if tag] commands.append('printf "{} {}{}\n"; cd "{}"; {};{}'.format( style.msg('in'), style.path(directory), ((' ' + ' '.join(tags)) if tags else '') + style.msg(':'), directory, command, status_printf )) sys.stdout.write(msg_head + style.msg(' in sequence...\n\n')) sys.stdout.flush() # flush for printing chronologically sp.call([shell, '-i', '-c', ';'.join(commands)], stderr=sp.STDOUT)