6

I have a file system tree and on different locations within it are files with the same name. I tried the following command on the command line:

find / -name "HAHA" -exec mv {} /home \;

It only worked for one file, for the others I received an error message for having the same name.

Can I change the command in a way that for each file name a number will be attached to distinguish them?

enter image description here

3
  • If you don't need to keep the original name, you can always offload the action to a script that does something like mv "$0" /home/$(uuidgen) to give each file a new, unique name. Would such a solution be acceptable?
    – user
    Feb 14, 2015 at 14:45
  • sorry, I do not understand $0 and $(uuidgen) Feb 14, 2015 at 15:12
  • I tried to create a function to be given to find but it did not accept it. Creating a script worked but that did not allow the number to be incremented. I am interested in the solution myself now.
    – unxnut
    Feb 14, 2015 at 15:26

4 Answers 4

11

I can think of two possible solutions:

  1. If you have installed mv from GNU coreutils (which probably is the case), then the following command...

    find / -name "HAHA" -type f -exec mv --backup=numbered "{}" /home \;
    

    ...will move all files called HAHA to /home. The --backup=numbered option of mv ensures that every time the mv command executes, it will check if there already exists a file named HAHA in the target directory -- and if it does, then it first renames it to HAHA.~n~ (where n is an increasing number) before moving the new file to the /home. In the end, you will end up with files named like HAHA, HAHA.~1~, HAHA.~2~ etc. in /home.

  2. This shell script should do the trick, but it is not resistant against paths containing newlines:

    IFS="
    "      # if you are using bash, you can just use IFS=$'\n' here
    i=1
    for file in $(find / -name "HAHA" -type f); do
      mv "${file}" "/home/HAHA${i}"
      i=$((${i} + 1))
    done
    

    This iterates over all the HAHA files and moves each one to /home/HAHAn, where n again is an increasing number, starting with 1.

4

I found a simple solution with a small script. The script is called cpf (or whatever name you give it) and is as follows:

#!/bin/bash

dir=xyz       # Directory where you want the files
num=1

for file in "$@"
do
    base=`basename -- "$file"`
    mv -- "$file" "$dir/$base.$num"
    num=$(($num+1))
done

You execute the command as follows:

find . -name HAHA -print0 | xargs -0x cpf
2
  • 9
    Using xargs -x is highly recommended here. Otherwise, xargs might silently decide it would like to start several sequencial cpf processes, each of which would have their own counter and start overwriting the haha's already moved. Feb 14, 2015 at 18:16
  • 1
    Or just remove xargs entirely and pipe the filenames to cpf (and modify cpf to read them from standard input)
    – user253751
    Feb 14, 2015 at 21:59
3

I would use while loop:

i=1
find / -name 'HAHA' -print0 | while read -d '' -r file; do mv "$file" "/home/${file##.*/}$((i++))"; done

Important here is print0 option of find which (together with -d '' option of read) treats properly files with white spaces in their names. If you need to do such operation only once then first line which sets i=1 is not needed since bash (and most of other shells) will simply created this variable automatically.

1

Possible using GNU parallel

find / -name "HAHA" -type f | parallel 'echo mv {}
 ./dest-dir/{/}_`stat -c%i {}`

{/}  ............. returns dirname
{/.} ............. dirname with no extension
stat -c%i {} ..... gets the inode for each file "whic is unique"

OBS: I used echo for sake of testing, just remove it if it meets your needs

You must log in to answer this question.

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