示例#1
0
文件: filepull.py 项目: voussoir/cmd
def filepull(pull_from='.', autoyes=False):
    files = list(spinal.walk(pull_from))
    cwd = os.getcwd()
    files = [f for f in files if os.path.split(f.absolute_path)[0] != cwd]

    if len(files) == 0:
        print('No files to move')
        return

    duplicate_count = {}
    for f in files:
        basename = f.basename
        duplicate_count.setdefault(basename, [])
        duplicate_count[basename].append(f.absolute_path)

    duplicates = [
        '\n'.join(sorted(copies))
        for (basename, copies) in duplicate_count.items() if len(copies) > 1
    ]
    duplicates = sorted(duplicates)
    if len(duplicates) > 0:
        raise Exception('duplicate names:\n' + '\n'.join(duplicates))

    for f in files:
        print(f.basename)

    if autoyes or interactive.getpermission(f'Move {len(files)} files?'):
        for f in files:
            local = os.path.join('.', f.basename)
            os.rename(f.absolute_path, local)
示例#2
0
def prune_shortcuts(recurse=False, autoyes=False):
    if recurse:
        lnks = [file for file in spinal.walk('.') if file.extension == 'lnk']
    else:
        lnks = pathclass.cwd().glob('*.lnk')

    stale = []
    for lnk in lnks:
        shortcut = winshell.Shortcut(lnk.absolute_path)
        # There are some special shortcuts that do not have a path, but instead
        # trigger some action based on a CLSID that Explorer understands.
        # I can't find this information in the winshell.Shortcut object, so for
        # now let's at least not delete these files.
        if shortcut.path == '':
            continue
        if not os.path.exists(shortcut.path):
            stale.append(lnk)

    if not stale:
        return

    print(f'The following {len(stale)} will be recycled:')
    for lnk in stale:
        print(lnk.absolute_path)
    print()

    if autoyes or interactive.getpermission('Is that ok?'):
        for lnk in stale:
            print(lnk.absolute_path)
            send2trash.send2trash(lnk.absolute_path)
示例#3
0
文件: rejpg.py 项目: voussoir/cmd
def rejpg_argparse(args):
    patterns = pipeable.input_many(args.patterns, skip_blank=True, strip=True)
    files = spinal.walk(recurse=args.recurse, glob_filenames=patterns)

    files = [f.absolute_path for f in files]

    bytes_saved = 0
    remaining_size = 0
    for filename in files:
        print(''.join(c for c in filename if c in string.printable))
        bytesio = io.BytesIO()
        i = PIL.Image.open(filename)

        i = imagetools.rotate_by_exif(i)

        i.save(bytesio, format='jpeg', quality=args.quality)

        bytesio.seek(0)
        new_bytes = bytesio.read()
        old_size = os.path.getsize(filename)
        new_size = len(new_bytes)
        remaining_size += new_size
        if new_size < old_size:
            bytes_saved += (old_size - new_size)
            f = open(filename, 'wb')
            f.write(new_bytes)
            f.close()

    print('Saved', bytestring.bytestring(bytes_saved))
    print('Remaining are', bytestring.bytestring(remaining_size))
示例#4
0
def contentreplace_argparse(args):
    if args.recurse:
        files = spinal.walk('.', yield_files=True, yield_directories=False)
        files = (f for f in files
                 if winglob.fnmatch(f.basename, args.filename_glob))
    else:
        files = pathclass.cwd().glob(args.filename_glob)
        files = (f for f in files if f.is_file)

    if args.clip_prompt:
        replace_from = input('Ready from')
        if not replace_from:
            replace_from = pyperclip.paste()
        replace_to = input('Ready to')
        if not replace_to:
            replace_to = pyperclip.paste()
    else:
        replace_from = codecs.decode(args.replace_from, 'unicode_escape')
        if args.do_regex:
            replace_to = args.replace_to
        else:
            replace_to = codecs.decode(args.replace_to, 'unicode_escape')

    for file in files:
        try:
            contentreplace(
                file,
                replace_from,
                replace_to,
                autoyes=args.autoyes,
                do_regex=args.do_regex,
            )
        except UnicodeDecodeError:
            log.error('%s encountered unicode decode error.',
                      file.absolute_path)
示例#5
0
def brename(transformation,
            autoyes=False,
            do_naturalsort=False,
            recurse=False):
    if recurse:
        walker = spinal.walk('.', yield_files=True, yield_directories=True)
        olds = list(walker)
    else:
        olds = cwd.listdir()

    if do_naturalsort:
        olds.sort(key=lambda x: natural_sorter(x.absolute_path))
    else:
        olds.sort()

    pairs = []
    for (index, old) in enumerate(olds):
        # These variables are assigned so that you can use them in your
        # transformation string.
        x = old.basename
        parent = old.parent
        noext = old.replace_extension('').basename
        ext = old.extension.no_dot
        dot_ext = old.extension.with_dot
        index1 = index + 1

        new = eval(transformation)
        new = parent.with_child(new)
        if new.basename == old.basename:
            continue
        pairs.append((old, new))

    if not pairs:
        print('Nothing to replace')
        return

    loop(pairs, dry=True)

    if not (autoyes or interactive.getpermission('Is this correct?')):
        return

    # Sort in reverse so that renaming a file inside a directory always
    # occurs before renaming the directory itself. If you rename the
    # directory first, then the path to the file is invalid by the time
    # you want to rename it.
    pairs = sorted(pairs, reverse=True)
    loop(pairs, dry=False)
示例#6
0
def pathtree_argparse(args):
    from voussoirkit import safeprint
    from voussoirkit import spinal
    paths = spinal.walk()
    paths = [{'path': path.absolute_path, 'size': path.size} for path in paths]
    tree = from_paths(paths, '.')
    recursive_get_size(tree)

    if args.output_file:
        output_file = open(args.output_file, 'w', encoding='utf-8')
    else:
        output_file = None

    for line in recursive_print_node(tree, use_html=args.use_html):
        if output_file:
            print(line, file=output_file)
        else:
            safeprint.safeprint(line)
示例#7
0
def inputrename_argparse(args):
    if args.recurse:
        files = (file for file in spinal.walk('.')
                 if args.keyword in file.basename)
    else:
        files = (file for file in pathclass.cwd().listdir()
                 if args.keyword in file.basename)
    prev = None
    for file in files:
        print(file.relative_path)
        this = input('> ')
        if this == '' and prev is not None:
            this = prev
        if this:
            new_name = file.basename.replace(args.keyword, this)
            new_name = file.parent.with_child(new_name)
            os.rename(file.absolute_path, new_name.absolute_path)
        prev = this
示例#8
0
def prune_dirs(starting):
    starting = pathclass.Path(starting)
    walker = spinal.walk(starting, yield_directories=True, yield_files=False)

    double_check = set()

    def pruneme(directory):
        if directory == starting or directory not in starting:
            return
        if len(directory.listdir()) == 0:
            print(directory.absolute_path)
            os.rmdir(directory.absolute_path)
            double_check.add(directory.parent)

    for directory in walker:
        pruneme(directory)

    while double_check:
        directory = double_check.pop()
        pruneme(directory)
示例#9
0
def sole_lift_argparse(args):
    starting = pathclass.Path(args.starting)
    queue = collections.deque()
    queue.extend(spinal.walk(starting, yield_files=False, yield_directories=True))
    while len(queue) > 0:
        directory = queue.popleft()

        if not directory.exists:
            log.debug('%s no longer exists.', directory)
            continue

        if directory not in starting:
            log.debug('%s is outside of starting.', directory)
            continue

        children = directory.listdir()
        child_count = len(children)
        if child_count != 1:
            log.debug('%s has %d children.', directory, child_count)
            continue

        child = children[0]

        if not child.is_dir:
            log.debug('%s contains a file, not a dir.', directory)
            continue

        log.info('Lifting contents of %s.', child.absolute_path)
        # child is renamed to random hex so that the grandchildren we are about
        # to lift don't have name conflicts with the child dir itself.
        # Consider .\abc\abc where the grandchild can't be moved.
        temp_dir = directory.with_child(passwordy.urandom_hex(32))
        os.rename(child.absolute_path, temp_dir.absolute_path)
        for grandchild in temp_dir.listdir():
            shutil.move(grandchild.absolute_path, directory.absolute_path)

        if temp_dir.listdir():
            raise Exception()

        os.rmdir(temp_dir.absolute_path)
        queue.append(directory.parent)
示例#10
0
def search(
        *,
        yes_all=None,
        yes_any=None,
        not_all=None,
        not_any=None,
        case_sensitive=False,
        content_args=None,
        do_expression=False,
        do_glob=False,
        do_regex=False,
        do_strip=False,
        line_numbers=False,
        local_only=False,
        only_dirs=False,
        only_files=False,
        root_path='.',
        text=None,
    ):
    terms = {
        'yes_all': yes_all,
        'yes_any': yes_any,
        'not_all': not_all,
        'not_any': not_any
    }
    terms = {k: ([v] if isinstance(v, str) else v or []) for (k, v) in terms.items()}
    #print(terms, content_args)

    do_plain = not (do_glob or do_regex)

    if all(v == [] for v in terms.values()) and not content_args:
        raise NoTerms('No terms supplied')

    def term_matches(line, term):
        if not case_sensitive:
            line = line.lower()

        if do_expression:
            return term.evaluate(line)

        return (
            (do_plain and term in line) or
            (do_regex and re.search(term, line)) or
            (do_glob and winglob.fnmatch(line, term))
        )

    if do_expression:
        # The value still needs to be a list so the upcoming any() / all()
        # receives an iterable as it expects. It just happens to be 1 tree.
        trees = {}
        for (term_type, term_expression) in terms.items():
            if term_expression == []:
                trees[term_type] = []
                continue
            tree = ' '.join(term_expression)
            tree = expressionmatch.ExpressionTree.parse(tree)
            if not case_sensitive:
                tree.map(str.lower)
            trees[term_type] = [tree]
        terms = trees

    elif not case_sensitive:
        terms = {k: [x.lower() for x in v] for (k, v) in terms.items()}

    if text is None:
        search_objects = spinal.walk(
            root_path,
            recurse=not local_only,
            yield_directories=True,
        )
    elif isinstance(text, (list, tuple)):
        search_objects = text
    else:
        search_objects = text.splitlines()

    for (index, search_object) in enumerate(search_objects):
        # if index % 10 == 0:
        #     print(index, end='\r', flush=True)
        if isinstance(search_object, pathclass.Path):
            if only_files and not search_object.is_file:
                continue
            if only_dirs and not search_object.is_dir:
                continue
            search_text = search_object.basename
            result_text = search_object.absolute_path
        elif isinstance(search_object, HeaderedText):
            search_text = search_object.text
            result_text = search_object.with_header
        else:
            search_text = search_object
            result_text = search_object

        if not all_terms_match(search_text, terms, term_matches):
            continue

        if do_strip:
            result_text = result_text.strip()

        if line_numbers:
            result_text = f'{index+1:>4} | {result_text}'

        if not content_args:
            yield result_text
            continue

        filepath = pathclass.Path(search_object)
        if not filepath.is_file:
            continue

        if filepath.extension == 'lnk' and winshell:
            yield from search_contents_windows_lnk(filepath, content_args)
        else:
            yield from search_contents_generic(filepath, content_args)
示例#11
0
from voussoirkit import spinal

basenames = {}

walker = spinal.walk('.', yield_directories=True, yield_files=False)
for directory in walker:
    basenames.setdefault(directory.basename, []).append(directory)

for (basename, dupes) in basenames.items():
    if len(dupes) == 1:
        continue
    for dupe in dupes:
        print(dupe.absolute_path)
    print()
示例#12
0
import collections

from voussoirkit import spinal

counts = collections.Counter()
extensions = {}
walker = spinal.walk()
for file in walker:
    extensions.setdefault(file.extension, []).append(file)
    counts[file.extension] += 1

for (extension, count) in counts.most_common():
    files = extensions[extension]
    print(f'{extension.with_dot}: {len(files)}')
    if len(files) < 5:
        for file in files:
            print(f'    {file.absolute_path}')