view unixSoft/bin/sednames.py @ 518:62e62b885540

merge
author Augie Fackler <raf@durin42.com>
date Wed, 08 Dec 2021 12:06:45 -0500
parents 91dbdbea15e5
children
line wrap: on
line source

#!/usr/bin/env python

from itertools import imap as map
from itertools import izip as zip

import re
whitespace_chars = {
	'\a': r'\a',
	'\b': r'\b',
	'\t': r'\t',
	'\n': r'\n',
	'\v': r'\v',
	'\f': r'\f',
	'\r': r'\r',
}
whitespace_chars_exp = re.compile('[' + ''.join(whitespace_chars) + ']')
chars_to_escape = dict(zip(map(chr, xrange(0x00, 0x20)), ('\\' + ch for ch in map(chr, xrange(0x00, 0x20)))))
for ch in '{}' "'" '"' '$*?':
	chars_to_escape[ch] = '\\' + ch
for ch in whitespace_chars:
	del chars_to_escape[ch] # Let whitespace_chars handle these.
chars_to_escape_exp = re.compile('[][\\' + ''.join(chars_to_escape) + ']')
chars_to_escape['['] = '\['
chars_to_escape[']'] = '\]'
chars_to_escape['\\'] = '\\\\'
minimal_chars_to_escape_exp = re.compile("[\\']")

def quote_argument(arg):
	count = 0
	match = chars_to_escape_exp.search(arg)
	while match:
		count += 1
		match = chars_to_escape_exp.search(arg, match.end())
	else:
		del match

	if count > 2:
		arg = "'" + re.sub(minimal_chars_to_escape_exp, lambda match: '\\' + match.group(0), arg) + "'"
	else:
		arg = re.sub(chars_to_escape_exp, lambda match: chars_to_escape[match.group(0)], arg)

	arg = re.sub(whitespace_chars_exp, lambda match: whitespace_chars[match.group(0)], arg)
	return arg

def custom_rename(option, opt_str, value, parser):
	for i, arg in enumerate(parser.rargs):
		if arg == ';':
			break
	parser.values.ensure_value('custom_rename', parser.rargs[:i])
	del parser.rargs[:i + 1]

import optparse
parser = optparse.OptionParser(description="This program batch-renames files, optionally using a VCS (such as svn, hg, or bzr) or other external program to do the renaming. You specify the rename operations using sed commands, which you specify using the -e option.")
parser.add_option('-e', '--program', action='append', default=[''])
parser.add_option('--vcs', metavar='VCS', help='Version-control system with which to rename files, using <VCS> mv <SRC> <DST>; overridden by          --custom-rename')
parser.add_option('--custom-rename', help='Custom command to rename the file (e.g., hg mv); works like find(1) -exec (but with two {}, which are src and dst in that order), or you can use {SRC} and {DST}, or you can omit {...} entirely in which case src and dst will be appended in that order', action='callback', type=None, callback=custom_rename, default=None)
parser.add_option('-q', '--quiet', action='store_true', default=False)
opts, args = parser.parse_args()

try:
	custom_rename = opts.custom_rename
except AttributeError:
	try:
		custom_rename = [opts.vcs, 'mv']
	except AttributeError:
		custom_rename = None

def prefix_with_dash_e(seq):
	for arg in seq:
		yield '-e'
		yield arg

import subprocess
sed = subprocess.Popen(['sed', '-El'] + list(prefix_with_dash_e(opts.program)), stdin=subprocess.PIPE, stdout=subprocess.PIPE)

import os, sys
for src in args:
	sed.stdin.write(src + '\n')
	sed.stdin.flush()
	dst = sed.stdout.readline().rstrip('\n')
	if src == dst:
		if not opts.quiet:
			print >>sys.stderr, 'sed program did not transform %r - skipping' % (src,)
	else:
		if custom_rename:
			src_and_dst = [src, dst]
			rename_cmd = list(custom_rename)
			for i, arg in enumerate(rename_cmd):
				if arg == '{SRC}':
					rename_cmd[i] = src
				elif arg == '{DST}':
					rename_cmd[i] = dst
				elif arg == '{}':
					try:
						rename_cmd[i] = src_and_dst.pop(0)
					except IndexError:
						sys.exit('Too many {} arguments in custom rename command %r' % (custom_rename,))
			else:
				rename_cmd += src_and_dst

			if not opts.quiet:
				print >>sys.stderr, ' '.join(map(quote_argument, rename_cmd))
			subprocess.check_call(rename_cmd)
		else:
			if not opts.quiet:
				print >>sys.stderr, 'Renaming %r to %r' % (src, dst)
			os.rename(src, dst)

sed.stdin.close()
sys.exit(sed.wait())