3

I need to rename files on my SD card, to be able to copy the contents of multiple sub folders to my HDD. First I need to find them by their extension .MOD (I can do that using find ./path/*.MOD2, as they are in different directories).

I need to rename them because they all have names like MOV001.MOD, MOV002.MOD, in other directory are other videos, but similarly named. This was created by my camera. After renaming, I could copy them by one command to my HDD.

Because of their actual names, when I use cp ./path/*.MOD new-path, the result would be that I overwrite similarly named files.

How can I solve this?

  • What would you like the new names to be? – Zanna Nov 7 '16 at 19:00
  • I would like them to be 1.MOD, 2.MOD, 3.MOD, 4.MOD. Or if it is not possible due to shortness of "string", they could be vid1.MOD, vid2.MOD or anything with different names. – Mikulas Kuzmiak Nov 7 '16 at 19:04
  • Ah, sorry, forgot about your extension, edited. – Jacob Vlijm Nov 7 '16 at 19:53
  • Thanks for the answers, I will try to understand them. Jacob Vlijm, thanks for your script, can I write multiple source directories when writing command to run script? (python3 /path/to/nodupes.py /path/to/sourcedir {here to insert multiple dirs and how to separate them} /path/to/outputdir). I'm sorry, but I'm newbie. Zanna - thanks, I'm trying to understand your answer, it looks more comprehensive for me, must study a little – Mikulas Kuzmiak Nov 7 '16 at 21:45
  • @MikulasKuzmiak The idea is that you do not need to use use multiple directories as argument. The script searches all sub directories of your card, or am I misunderstanding? – Jacob Vlijm Nov 8 '16 at 7:09
3

This tiny script renames the files without moving them

n=0; for files in dir1/*.MOD dir2/*.MOD dir3/*.MOD; do printf -v new "${files/MOV*./%02d.}" "$((++n))"; echo mv -v "$files" "$new"; done

or more readably:

#!/bin/bash
n=0
for files in dir1/*.MOD dir2/*.MOD dir3/*.MOD; do
   printf -v new "${files/MOV*./%02d.}" "$((++n))"
   echo mv -v -- "$files" "$new"
done

Replace dir1 etc with the actual paths to your directories with the files

Remove echo (it's just to test) after checking that this gives you what you want, to actually rename the files.

This numbers the files 01.MOD, 02.MOD, etc. If you have more than 99 files, replace %02d with %03d to get 001.MOD etc


Explanation:

n=0 This just sets n as 0 which is where I want bash to start counting from.

for files in dir1/*.MOD dir2/*.MOD dir3/*.MOD

A for loop can execute commands iteratively on each file in turn. The syntax is for [variable] in [these things I want to do something to] ; do [command(s)] $[variable]; done I have called the variable files and found your files using a glob: *.MOD is expanded by the shell to any file whose name ends in .MOD (* matches any characters)

printf -v new

printf can format the new numbers with a fixed width for easier sorting. new is another variable - this is the new name for the files.

"${files/MOV*./%02d.}" "$((++n))"

Referencing the variable files from earlier and replacing MOV* (remember * is any character) with the result of the incrementing number $((++n)) (this is n from the first line of the script, go up by one each time the loop is executed on a file) formatted using the code %02d which to printf means a decimal number of a fixed width of two digits 01, 02 etc. The search and replace pattern includes the . to stop * matching the whole filename and thus removing the extension.

echo mv -v -- "$files" "$new"

I add the echo just for testing - this shows what will be done instead of actually doing it. Remove the echo when happy with the result to actually execute the command.

mv renames or moves files; its syntax is mv oldname newname. I added the -v flag which tells mv to be "verbose" and report each action. I added -- just to be safe - this tells mv not to accept any further options, which stops any filenames starting with - from being interpreted as options to the command - not needed in your case, but good practice.

Each file specified by the files variable is mved to a unique name created in the new variable. We use $ to reference variables and these should be in "double quotes" to prevent the shell from doing any other expansions on the filenames - this stops special characters or spaces in filenames from causing problems.

  • Thank you, your (script?) worked well, just tried it with echo, result is precisely what I was trying to do. Now I must learn the syntax and usage of your commands, I'm trying to learn as well as just use. PLease, if you have will and time, can you explain only little about parts of your command or script? Only to help me better find whereabouts and give clues. – Mikulas Kuzmiak Nov 7 '16 at 22:01
  • @MikulasKuzmiak :) I will edit with a full explanation now. I was on mobile earlier and so slow to type – Zanna Nov 7 '16 at 22:04
  • @MikulasKuzmiak there, editing done, feel free to ask if it is not clear :) – Zanna Nov 7 '16 at 22:44
  • I misunderstood your answer :) +1 – Jacob Vlijm Nov 8 '16 at 7:21
  • @JacobVlijm oh thanks! :D maybe I didn't explain clearly, hmm. The upvote on yours is from me, very nice as always :) – Zanna Nov 8 '16 at 7:24
1

You can copy them from a recursive directory (including all sub directories), search for extension and rename them in one step with the script below.

Taken from scripts I use to make sure not to overwrite files, when copying them from a recursive directory:

#!/usr/bin/env python3
import os
import shutil
import sys

dr = sys.argv[1]
new_dir = sys.argv[2]

for root, dirs, files in os.walk(dr):
    for name in files:
        if name.lower().endswith(".mod"): # for your extension
            n = 1; name_orig = name
            while os.path.exists(new_dir+"/"+name):
                name = "duplicate_"+str(n)+"_"+name_orig
                n = n+1
            newfile = new_dir+"/"+name
            shutil.copy(root+"/"+name_orig, newfile)

Copy the script into an empty file, save it as nodupes.py, run it with the input (source-) and output directory as arguments:

python3 /path/to/nodupes.py /path/to/sourcedir /path/to/outputdir

It will rename similarly named files like:

enter image description here

Of course, instead of duplicate_, the script can be changed to use anything numbered (or just numbers).

A generalized version, not searching for one extension only, would be:

#!/usr/bin/env python3
import os
import shutil
import sys

dr = sys.argv[1]
new_dir = sys.argv[2]

for root, dirs, files in os.walk(dr):
    for name in files:
        n = 1; name_orig = name
        while os.path.exists(new_dir+"/"+name):
            name = "duplicate_"+str(n)+"_"+name_orig
            n = n+1
        newfile = new_dir+"/"+name
        shutil.copy(root+"/"+name_orig, newfile)
  • Thanks, I will try to use your script as an another method of doing job done. – Mikulas Kuzmiak Nov 7 '16 at 22:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.