Skip to content

Instantly share code, notes, and snippets.

@danijar
Last active April 29, 2018 12:50
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 danijar/c845de537b799fcdd8824da60ced7afc to your computer and use it in GitHub Desktop.
Save danijar/c845de537b799fcdd8824da60ced7afc to your computer and use it in GitHub Desktop.
Text based tool move and delete directories
#!/usr/bin/python3
"""Move or delete directories via your text editor.
Installation:
- Install sh.py via `pip3 install sh`.
- Save this file into a directory in your $PATH, for example `~/bin`.
"""
import argparse
import tempfile
import os
import sh
__author__ = 'Danijar Hafner'
def user_edit_text(text):
editor = sh.Command(os.environ.get('EDITOR', 'vim'))
with tempfile.NamedTemporaryFile('w+', suffix='.tmp') as file_:
file_.write(text)
file_.flush()
editor(file_.name, _fg=True)
file_.seek(0)
return file_.read()
def read_changes(dirs, edited):
edited = [line.strip() for line in edited.strip().split('\n')]
edited = [line.split(' ', 1) for line in edited if line]
edited = {int(index): dir_ for index, dir_ in edited}
keeps, moves, deletes = [], [], []
for index, dir_ in enumerate(dirs):
if index not in edited:
deletes.append(dir_)
elif edited[index] != dir_:
moves.append((dir_, edited[index]))
else:
keeps.append(dir_)
return keeps, moves, deletes
def preview_changes(keeps, moves, deletes):
if keeps:
print('Unchanged:')
print(''.join('- {}\n'.format(x) for x in keeps))
if moves:
print('Move:')
print(''.join('- {}\n {}\n'.format(*x) for x in moves))
if deletes:
print('Delete:')
print(''.join('- {}\n'.format(x) for x in deletes))
try:
if input('Looks good? [Ny]: ') == 'y':
return True
except KeyboardInterrupt:
print('')
print('No changes made.')
return False
def execute_changes(moves, deletes, args):
move = sh.Command(args.move_cmd.split(' ', 1)[0])
params = args.move_cmd.split(' ', 1)[1]
commands = []
for source, target in moves:
commands.append(move(*params.format(source, target).split(), _bg=True))
delete = sh.Command(args.delete_cmd.split(' ', 1)[0])
params = args.delete_cmd.split(' ', 1)[1]
for dir_ in deletes:
commands.append(delete(*params.format(dir_).split(), _bg=True))
failures = 0
for command in commands:
try:
command.wait()
except sh.ErrorReturnCode as e:
print(e)
failures += 1
return failures
def read_directory_list(content_cmd):
content = sh.Command(content_cmd.split(' ', 1)[0])
params = content_cmd.split(' ', 1)[1]
dirs = content(*params.format(args.root).split()).strip().split('\n')
text = ''.join('{} {}\n'.format(*x) for x in enumerate(dirs))
return dirs, text
def main(args):
dirs, initial = read_directory_list(args.content_cmd)
current = initial
while True:
edited = user_edit_text(current)
if initial.strip() == edited.strip():
print('No changes made.')
return
keeps, moves, deletes = read_changes(dirs, edited)
if not preview_changes(keeps, moves, deletes):
return
failures = execute_changes(moves, deletes, args)
message = '{} changes failed. Re-edit change list? [Ny]: '
if failures and input(message.format(failures)) == 'y':
dirs, initial = read_directory_list(args.content_cmd)
current = edited
continue
else:
break
print('Done.')
if __name__ == '__main__':
description = 'Move or delete directories via your text editor.'
parser = argparse.ArgumentParser(description=description)
parser.add_argument('root', type=str)
parser.add_argument(
'--content-cmd', type=str, default='ls {}')
parser.add_argument(
'--move-cmd', type=str, default='mv {} {}')
parser.add_argument(
'--delete-cmd', type=str, default='rm -rf {}')
args = parser.parse_args()
main(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment