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)
- Deep rename all the matching files to the new names (rename)
- 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 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
Command: it may be one of
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
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
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:
We want to copy all the files whose name contains the word:
sendmail into new files that in place of
Here is a partial list of
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*). 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
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
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:
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:
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!
if [ "$#" -eq 0 ] || ( [ "$1" != "preview" ] && [ "$1" != "copy" ] && [ "$1" != "rename" ] )
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"
for f in $(find "$2" -type f -name "$3" -name "*$4*" )
z=`echo "$f" | sed s/"$4"/"$5"/`
case "$1" in
"preview") echo "$f => $z"
"copy") cp -p "$f" "$z"
"rename") mv "$f" "$z"