Simple Approach
find + perl + xargs + mv
xargs -n2
makes it possible to print two arguments per line. When combined with Perl's print $_
(to print the $STDIN first), it makes for a powerful renaming tool.
find . -type f | perl -pe 'print $_; s/input/output/' | xargs -d "\n" -n2 mv
Results of perl -pe 'print $_; s/OldName/NewName/' | xargs -n2
end up being:
OldName1.ext NewName1.ext
OldName2.ext NewName2.ext
OldName3.ext NewName3.ext
OldName4.ext NewName4.ext
I did not have Perl's rename
readily available on my system.
How does it work?
find . -type f
outputs file paths (or file names...you control what gets processed by regex here!)
-p
prints file paths that were processed by regex, -e
executes inline script
print $_
prints the original file name first (independent of -p
)
-d "\n"
cuts the input by newline, instead of default space character
-n2
prints two elements per line
mv
gets the input of the previous line
Advanced Approach
This is my preferred approach, because it is rebust. The advanced part is the added complexity of using null bytes in the standard output of each executable in the pipeline, and dealing with them in the standard inputs of subsequent executables. Why do this?: This avoids issues with spaces and newlines in filenames.
Let's say I want to rename all ".txt" files to be ".md" files:
find . -type f -printf '%P\0' | perl -0 -l0 -pe 'print $_; s/(.*)\.txt/$1\.md/' | xargs -0 -n 2 mv
The magic here is that each process in the pipeline supports the null byte (0x00) that is used as a delimiter as opposed to spaces or newlines. The first aforementioned method uses newlines as separators. Note that I tried to easily support find .
without using subprocesses. Be careful here (you might want to check your output of find
before you run in through a regular expression match, or worse, a destructive command like mv
).
How it works (abridged to include only changes from above)
- In
find
: -printf '%P\0'
print only name of files without path followed by null byte. Adjust to your use case-whether matching filenames or entire paths.
- In
perl
and xargs
: -0
stdin delimiter is the null byte (rather than space)
- In
perl
: -l0
stdout delimiter is the null byte (in octal 000)