summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--doc/ranger.pod9
-rw-r--r--ranger/defaults/commands.py54
-rw-r--r--ranger/help/console.py5
3 files changed, 68 insertions, 0 deletions
diff --git a/doc/ranger.pod b/doc/ranger.pod
index 4bd12889..38b3ca17 100644
--- a/doc/ranger.pod
+++ b/doc/ranger.pod
@@ -493,6 +493,15 @@ Enable this if key combinations with the Alt Key don't work for you.
 
 =over 2
 
+=item bulkrename
+
+This command opens a list of selected files in an external editor.  After you
+edit and save the file, it will generate a shell script which does bulk
+renaming according to the changes you did in the file.
+
+This shell script is opened in an editor for you to review.  After you close
+it, it will be executed.
+
 =item cd [I<directory>]
 
 The cd command changes the directory.  The command C<:cd -> is equivalent to
diff --git a/ranger/defaults/commands.py b/ranger/defaults/commands.py
index 8ab9b353..cb39ddf9 100644
--- a/ranger/defaults/commands.py
+++ b/ranger/defaults/commands.py
@@ -684,6 +684,60 @@ class chmod(Command):
 			pass
 
 
+class bulkrename(Command):
+	"""
+	:bulkrename
+
+	This command opens a list of selected files in an external editor.
+	After you edit and save the file, it will generate a shell script
+	which does bulk renaming according to the changes you did in the file.
+
+	This shell script is opened in an editor for you to review.
+	After you close it, it will be executed.
+	"""
+	def execute(self):
+		import sys
+		import tempfile
+		from ranger.fsobject.file import File
+		from ranger.ext.shell_escape import shell_escape as esc
+		py3 = sys.version > "3"
+
+		# Create and edit the file list
+		filenames = [f.basename for f in self.fm.env.get_selection()]
+		listfile = tempfile.NamedTemporaryFile()
+
+		if py3:
+			listfile.write("\n".join(filenames).encode("utf-8"))
+		else:
+			listfile.write("\n".join(filenames))
+		listfile.flush()
+		self.fm.execute_file([File(listfile.name)], app='editor')
+		listfile.seek(0)
+		if py3:
+			new_filenames = listfile.read().decode("utf-8").split("\n")
+		else:
+			new_filenames = listfile.read().split("\n")
+		listfile.close()
+		if all(a == b for a, b in zip(filenames, new_filenames)):
+			self.fm.notify("No renaming to be done!")
+			return
+
+		# Generate and execute script
+		cmdfile = tempfile.NamedTemporaryFile()
+		cmdfile.write(b"# This file will be executed when you close the editor.\n")
+		cmdfile.write(b"# Please double-check everything, clear the file to abort.\n")
+		if py3:
+			cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \
+					for old, new in zip(filenames, new_filenames) if old != new).encode("utf-8"))
+		else:
+			cmdfile.write("\n".join("mv -vi " + esc(old) + " " + esc(new) \
+					for old, new in zip(filenames, new_filenames) if old != new))
+			cmdfile.flush()
+		self.fm.run(['vim', cmdfile.name])
+		self.fm.run(['/bin/sh', cmdfile.name], flags='w')
+		cmdfile.close()
+
+
 class filter(Command):
 	"""
 	:filter <string>
diff --git a/ranger/help/console.py b/ranger/help/console.py
index 2f3a75c8..7ae4df28 100644
--- a/ranger/help/console.py
+++ b/ranger/help/console.py
@@ -39,6 +39,11 @@ unambiguous name, e.g. ":chmod" can be written as ":ch" but not as ":c" since
 it conflicts with ":cd".
 
 
+:bulkrename
+      This command opens a list of selected files in an external editor.
+      After you edit and save the file, it will generate a shell script which
+      does bulk renaming according to the changes you did in the file.
+
 :cd <dirname>
       Changes the directory to <dirname>