This little script comes to help exactly in this situation.
Given a starting directory, a file name pattern, an original file name portion to change and the destination file name portion to use, it will:
- Deep copy all the matching files preserving attributes over new ones (copy)
or
- Deep rename all the matching files to the new names (rename)
or
- Just show a preview list of the potentially affected files (preview).
The last option is expecially useful because selecting the right match that covers a mass of files with names variations or extensions variations may quicky become a challenge. Avoid getting a complete mess by previewing the changes before applying them!
The script is very simple and has NO pretense about being the best, the quickest, the cleanest, the safest. It's what works for me and I have decided to share it in hope somebody else will take benefit.
If you start it with no parameters or a wrong command, it will show some brief instructions:
./copy_multi_files.sh
Usage:
./copy_multi_files.sh command path name_pattern src dest
If command is 'copy' then the script copies src to dest
If command is 'rename' then the script renames src to dest
If command is 'preview' then the script just shows the matched files
Inside path, for every file name matching name_pattern, it copies every src file to dest file
Parameters:
Command: it may be one of copy
, rename
or preview
. Copy performs the recursive deep copy onto new files, rename just mass renames existing files, preview just shows which files will be affected and how.
Path: it's the starting directory. I suggest doing experiments on small directories to get confidence with the script before trying on your 4 peta-trillion entries file system.
Name_pattern: this decides which files shall be affected, so it's important to pick a sensible value. In example, if I wanted to select a lot of scripts whose name starts with "sendmail" I could specify s*
for this parameter. For those who know about shell commands, this pattern is internally passed to find
.
src: this is the portion, a template of file name to replace with dest when forming the copied files names.
dest: this is what replaces src in the new copied files names.
A bit of sanity check is performed so that name_pattern
and src
produce consistent results. That is, only files that match name_pattern
and also contain src
are selected.
Confusing? Don't worry, I have prepared some examples and "gotchas".
Simple, one folder example:
We have some files in a sub-directory called:
apps/res/
We want to copy all the files whose name contains the word: sendmail
into new files that in place of sendmail
have newthing
.
Here is a partial list of apps/res/
contents:
robot.php
sendmail_test.php
sendmail_test2.php
sendmail_test3.php
showinfo.php
show_server_vars.php
version.php
We may issue the following command:
./copy_multi_files.sh preview apps/res/ s* sendmail newthing
This command line tells us that we just want a preview
; the starting directory is apps/res/
(that is, it's under our current directory), that we are going to search for all files beginning with s
(s*
). Among the found files, we are going to pick only those whose name contains sendmail
and copy them to files residing in the same directories but with sendmail
replaced by newthing
.
Here is the output of this preview:
apps/res/sendmail_test.php => apps/res/newthing_test.php
apps/res/sendmail_test2.php => apps/res/newthing_test2.php
apps/res/sendmail_test3.php => apps/res/newthing_test3.php
You see, even if we specified s*
as search pattern, the script was "smart" enough to just pick the subset of "beginning by s" files that also contains "sendmail".
Let's run the command:
./copy_multi_files.sh copy apps/res/ s* sendmail newthing
ls apps/res/
will show how now we have both the original files but also the same files with newthing
. If we used "rename" in the line above, we'd only have the newthing
files.
newthing_test.php
newthing_test2.php
newthing_test3.php
robot.php
sendmail_test.php
sendmail_test2.php
sendmail_test3.php
showinfo.php
show_server_vars.php
version.php
Let's change the files again. This time we rename them:
./copy_multi_files.sh rename apps/res/ new* new even_newer_
Notice the values used for the various parameters. The directory now is the following:
even_newer_thing_test.php
even_newer_thing_test2.php
even_newer_thing_test3.php
robot.php
sendmail_test.php
sendmail_test2.php
sendmail_test3.php
showinfo.php
show_server_vars.php
version.php
More complex example: let's move to OpenCart's top install folder and return to the the bank transfer MVC files. We need to create copies of those files, each in its respective directory. They will be used to implement a direct debit payment module. Here's the command line and an extract of the output:
./copy_multi_files.sh preview . ban* bank_transfer.* direct_debit
./catalog/view/language/russian/payment/bank_transfer.php => ./catalog/view/language/russian/payment/direct_debit
./catalog/view/language/czech/payment/bank_transfer.php => ./catalog/view/language/czech/payment/direct_debit
./catalog/view/theme/default/template/payment/bank_transfer.tpl => ./catalog/view/theme/default/template/payment/direct_debit
Oops! Something went wrong! Lucky us, for having used the preview. What's wrong in the above command? The "src" argument is the wrong one. In fact, bank_transfer.*
is internally passed to a direct "search and replace" function (sed
). It's not expanded with a "file globbing" function but as regular expression. Therefore all the files in the preview are transformed into: direct_debit
.
Now that we know about this gotcha, we can rewrite the command line to achieve the desired result:
/var/www/copy_multi_files.sh preview . ban* bank_transfer direct_debit
./catalog/view/language/russian/payment/bank_transfer.php => ./catalog/view/language/russian/payment/direct_debit.php
./catalog/view/language/czech/payment/bank_transfer.php => ./catalog/view/language/czech/payment/direct_debit.php
./catalog/view/theme/default/template/payment/bank_transfer.tpl => ./catalog/view/theme/default/template/payment/direct_debit.tpl
Now it works! Now extensions are preserved, even when they change (.php and .tpl).
Follows the little script source code. I strongly suggest to save backups before doing "live" renames and to always use the preview functionality. Remember, this is a recursive Unix script, its effects may be extremely nasty and irreversible!
Source code:
#!/bin/sh
if [ "$#" -eq 0 ] || ( [ "$1" != "preview" ] && [ "$1" != "copy" ] && [ "$1" != "rename" ] )
then
echo "Usage:"
echo "$0 command path name_pattern src dest"
echo "If command is 'copy' then the script copies src to dest"
echo "If command is 'rename' then the script renames src to dest"
echo "If command is 'preview' then the script just shows the matched files"
echo "Inside path, for every file name matching name_pattern, it copies every src file to dest file"
echo
exit 1
fi
for f in $(find "$2" -type f -name "$3" -name "*$4*" )
do
z=`echo "$f" | sed s/"$4"/"$5"/`
case "$1" in
"preview") echo "$f => $z"
;;
"copy") cp -p "$f" "$z"
;;
"rename") mv "$f" "$z"
;;
esac
done