To introduce this collection of technical notes, I thought I’d start with one of my favorite shell scripts. I wrote this over ten years ago, and I still use it very often. The idea is similar to rename, but interactive, so much easier to use.
It opens up a text editor on the filenames in the current directory (or directories given on the command line). You can edit the filenames using any editor functions you like. Obviously, regex search-and-replace is indispensable at this point, and I also find vim’s visual block mode quite useful.
Upon saving and exiting the editor, files are renamed according to your edits.
The script correctly handles swapping filenames (though not conflicts). Error
exists from the editor abort the operation (:cq
in vim). Renaming a file to
the empty string deletes it, so the script is also useful for interactively
cleaning up a directory.
The script should work in any Bourne-compatible shell, not just bash.
#!/bin/sh
# mvdir
# David Reiss <dnr@dnr.im>
# Created: 2002-07-21
# Last update: 2012-12-06
t1=`mktemp`
t2=`mktemp`
t3=`mktemp`
[ $# -gt 0 ] || set .
for d in "$@"; do
(
cd "$d" || continue
ls > $t1
cp -f $t1 $t2
${EDITOR:-vi} $t2
status=$?
[ $status -ne 0 ] && exit $status
if [ `< $t1 wc -l` -ne `< $t2 wc -l` ]; then
echo "line count mismatch"
rm -f $t1 $t2 $t3
exit 1
fi
< $t2 sed -e "s/^-/.\/-/" -e "s/'/'\\\\''/g" -e "s/.*/'&'/" > $t3
< $t1 sed -e "s/^-/.\/-/" -e "s/'/'\\\\''/g" -e "s/.*/'&'/" > $t2
paste -d, $t2 $t3 | sed '/^\(.*\),\1$/d' | nl -s, > $t1
(
< $t1 sed -ne "s/^ *\(.*\),'\(.*\)','\(\)'$/rm '\2'/p" \
-ne "s/^ *\(.*\),'\(.*\)','\(.*\)'$/mv '\2' '##mvdir\1'/p"
< $t1 sed -ne "s/^ *\(.*\),'\(.*\)','\(..*\)'$/mv '##mvdir\1' '\3'/p"
) | sh -v
rm -f $t1 $t2 $t3
)
status=$?
[ $status -ne 0 ] && echo "$0: aborted" && exit $?
done