14

I'm moving a file to a different folder and would like to add some kind of index to the newly moved file if a file with the same name exists already (the old one should remain untouched). For example, if file.pdf existed I would prefer something like file1.pdf or file_1.pdf for the next file with the same name.

Here I've found a variant for the opposite idea — but I don't want to make a "backup".

Does mv have some parameters out of the box for that scenario? I use Ubuntu Linux.

5
  • 2
    That is exactly what the solution in your linked question's answer does. Have you tried it? Commented Jun 15, 2017 at 19:40
  • It gave me something like this: file.pdf and file.pdf.~1~ - I don't know what to think of it
    – Qohelet
    Commented Jun 15, 2017 at 19:48
  • file.pdf already existed in the destination directory. file.pdf.~1~ is the first file moved that would have klobbered file.pdf if it had not been renamed.
    – DopeGhoti
    Commented Jun 15, 2017 at 19:52
  • Doesn't it actually add the suffix to the original? Commented Jun 15, 2017 at 19:54
  • I'd prefer something like file1.pdf or file_1.pdf
    – Qohelet
    Commented Jun 15, 2017 at 19:56

5 Answers 5

14

As the answer to the question you linked already states, mv can suffix files that would otherwise get overwritten by the file you move with a number to give them a unique file name:

mv --backup=t <source_file> <dest_file>

The command works by appending the next unused number suffix to the file that was first in the destination directory. The file you are moving will keep its original name.

However, this will appends suffixes like .~1~, which seems to be not what you want:

$ ls
file.pdf
file.pdf.~1~
file.pdf.~2~

You can rename those files in a second step though to get the names in a format like file_1.pdf instead of file.pdf.~1~, e.g. like this:

rename 's/((?:\..+)?)\.~(\d+)~$/_$2$1/' *.~*~

This takes all files that end with the unwanted backup suffix (by matching with the shell glob *.~*~) and lets the rename tool try to match the regular expression ((?:\..+)?)\.~(\d+)~$ on the file name. If this matches, it will capture the index from the .~1~-like suffix as second group ($2) and optionally, if the file name has an extension before that suffix like .pdf, that will be captured by the first group ($1). Then it replaces the complete matched file name part with _$2$1, inserting the captured values instead of the placeholders though.

Basically it will rename e.g. file.pdf.~1~ to file_1.pdf and something.~42~ to something_42, but it can not detect whether a file has multiple extensions, so e.g. archive.tar.gz.~5~ would become archive.tar_5.gz

4
  • "Files that were originally present in the destination folder do not get modified in any way" - sure about that? try echo 'This is foo' > foo ; echo 'This is bar' > bar ; mv --backup=t foo bar ; cat bar (it's the original bar that becomes bar.~1~) Commented Jun 15, 2017 at 22:20
  • @steeldriver Oops, you're right. That's quite counter-intuitive behaviour in my opinion though. Editing... Commented Jun 15, 2017 at 22:30
  • Yeah I don't know the history of why it was done that way Commented Jun 15, 2017 at 22:45
  • 4
    also --backup=numbered can be used instead of --backup=t Commented Jun 16, 2017 at 20:51
2

I had the issue that a BASH script was encountering / overwriting same-named files during a mv operation.

The mv --backup=t <src> <dest> solution here, and https://serverfault.com/questions/267255/mv-rename-if-exists works well. However I also wanted to preserve the file extension.

Here is a solution.

[victoria@victoria out]$ ls -l

total 20
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 apple1.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 apple2.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 apple3.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 apple4.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 apple5.txt

[victoria@victoria out]$ for f in *; do mv -f --backup=t "$f" banana.txt; done

[victoria@victoria out]$ ls -l
total 20
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana.txt.~1~
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana.txt.~2~
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana.txt.~3~
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana.txt.~4~

[victoria@victoria out]$ for f in *; do mv 2>/dev/null -v "$f" "`echo $f | sed -r 's/(.*)\.(.*)\.~([0-9]{1,})~$/\1_\3.\2/'`"; done

renamed 'banana.txt.~1~' -> 'banana_1.txt'
renamed 'banana.txt.~2~' -> 'banana_2.txt'
renamed 'banana.txt.~3~' -> 'banana_3.txt'
renamed 'banana.txt.~4~' -> 'banana_4.txt'

[victoria@victoria out]$ ls -l

total 20
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana_1.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana_2.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana_3.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana_4.txt
-rw-r--r-- 1 victoria victoria 6 May 23 18:13 banana.txt

[victoria@victoria out]$ 

Explanation:

  • Using a sed regex command I capture the three parts of the source filename,

/(.*)\.(.*)\.~([0-9]{1,})~$/

i.e., in sequence match:

  • everything: (.*) # captured as group \1
  • period \.
  • everything: (.*) # captured as group \2
  • period \.
  • tilde: ~
  • any number of numbers: ([0-9]{1,}) # captured as group \3
  • the second tilde and end of line: ~$

and rearrange them:

/\1_\3.\2/ # note the placement of the period, not necessary to -escape here`

Note the use of backticks ` at the start/end of the "-quoted echo command.

2>/dev/null ignores any spurious warnings, if they occur.

Leave out the -v flag if you don't want verbose output from the mv command.


See also:

0
0

Ok, don't judge me by my not poor bash-skills but this solution worked for me. (I appreciated Byte Commanders solution yet it's due to its restrictions not what I was looking for)

So here's my small script that does the job...

  • $2 is the path
  • $3 the filename
  • $punktpdf is just ".pdf"

So here's my little script...

if [ -s $2$3$punktpdf ]; then
    for i in `seq 1 100000`;
        do
            if [ ! -s $2$3$i$punktpdf ]; then
                if mv scan.pdf $2$3$i$punktpdf; then                        
                    echo $3$i$punktpdf
                    exit 0
                    break
               else                        
                    echo 1
                    break
               fi
           fi
        done
    else
        #Regular mv
    fi
0
#!/bin/bash
outputFile="/videos/test/test.mp4"
version=0
preservedOutputFile="${outputFile%.*}-prev-v$version.mp4"
while [ -f "$preservedOutputFile" ]
do
    echo "$preservedOutputFile exist!"
    version=$((version+1))
    preservedOutputFile="${outputFile%.*}-prev-v$version.mp4"
done

if [ $version -ne 0 ]; then
    echo "Preservering existing \"$outputFile\" by renaming it to \"$preservedOutputFile\"!"
    #rename file
    mv "$outputFile" "$preservedOutputFile"
else
    echo "$outputFile doesn't exist! No need to preserve!"
fi
-1

I created move command. move command add index if file exists.

npm i -g @gauravnumber/move

Example

Folder Structure

.
├── 1.txt
├── 2.txt
├── 3.txt
├── 4.txt
├── 5.txt
└── 6.txt

Type below command.

move 1.txt 2.txt

2.txt file already exists. move command append index on the file. After running above command. Folder structure looks like this.

.
├── 2_1.txt
├── 2.txt
├── 3.txt
├── 4.txt
├── 5.txt
└── 6.txt

move command also support multiple files.

move *.jpg dirname
move *.png *.jpg dirname
move dirname1 dirname2
move dirname1/* dirname2

Here is link to repo

1
  • (1) Please describe how to use the software. (2) If you wrote this software, you must say so in the answer. Commented Jul 30, 2022 at 7:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .