6

My wrong command :

find . -type f -name '*2019*' -exec mv {} ./backup_2019 \;

result :

mv: ‘./backup_2019/2019-A.txt’ and ‘backup_2019/2019-A.txt’ are the same file
mv: ‘./backup_2019/2019-B.txt’ and ‘backup_2019/2019-B.txt’ are the same file
mv: ‘./backup_2019/2019-C.txt’ and ‘backup_2019/2019-C.txt’ are the same file

I think it finds the file that it previously moved. Can I solve this without using maxdepth? Is there a way to exclude only the target directory?

0

4 Answers 4

6

You should ignore the target directory you want to exclude

find . -path './backup_2019' -prune -o -type f -name '*2019*' -exec mv {} ./backup_2019 \;

The implicit AND between operators binds more tightly than the OR (-o), so it translates as (skip if the path matches ./backup_2019) OR (mv if we have files matching *2019*).

0
5

You will need to ignore the backup directory so that find does not enter into it. There is already an answer showing how to do this.

However, you may run the risk of deleting data if you back up files in this way. If two or more files, in different subdirectories, have the same names, they would over-write each other on the destination, in the backup directory.

It would be better to use some real backup software to back up the data, such as restic. If that is not possible, use a solution that preserves the relative path to the files that you are backing up.

The following command uses rsync to copy (not move) all files that have names containing the substring 2019 into the directory backup_2019:

rsync --itemize-changes --archive --prune-empty-dirs \
    --exclude='/backup_2019/***' --include='*/' --include='*2019*' --exclude='*' \
    ./ ./backup_2019

This would avoid looking inside ./backup_2019 for files or directories to transfer, but would otherwise copy all things that contains the substring 2019. Directories on the target that end up empty are removed. Everything that is copied is copied into a location under backup_2019 that is the same as the file's location under the current directory:

Example:

$ tree -F
.
|-- dir1/
|   |-- file-1
|   |-- file-2019-A
|   `-- subdir/
|       |-- file-2
|       `-- file-2019-B
|-- dir2/
|   |-- file-1
|   |-- file-2019-A
|   `-- subdir/
|       |-- file-2
|       `-- file-2019-B
`-- dir3/
    |-- file-1
    |-- file-2019-A
    `-- subdir/
        |-- file-2
        `-- file-2019-B
$ rsync --itemize-changes --archive \
    --prune-empty-dirs \
    --exclude='/backup_2019/***' --include='*/' --include='*2019*' --exclude='*' \
    ./ ./backup_2019
cd+++++++++ ./
cd+++++++++ dir1/
>f+++++++++ dir1/file-2019-A
cd+++++++++ dir1/subdir/
>f+++++++++ dir1/subdir/file-2019-B
cd+++++++++ dir2/
>f+++++++++ dir2/file-2019-A
cd+++++++++ dir2/subdir/
>f+++++++++ dir2/subdir/file-2019-B
cd+++++++++ dir3/
>f+++++++++ dir3/file-2019-A
cd+++++++++ dir3/subdir/
>f+++++++++ dir3/subdir/file-2019-B
$ tree -F
.
|-- backup_2019/
|   |-- dir1/
|   |   |-- file-2019-A
|   |   `-- subdir/
|   |       `-- file-2019-B
|   |-- dir2/
|   |   |-- file-2019-A
|   |   `-- subdir/
|   |       `-- file-2019-B
|   `-- dir3/
|       |-- file-2019-A
|       `-- subdir/
|           `-- file-2019-B
|-- dir1/
|   |-- file-1
|   |-- file-2019-A
|   `-- subdir/
|       |-- file-2
|       `-- file-2019-B
|-- dir2/
|   |-- file-1
|   |-- file-2019-A
|   `-- subdir/
|       |-- file-2
|       `-- file-2019-B
`-- dir3/
    |-- file-1
    |-- file-2019-A
    `-- subdir/
        |-- file-2
        `-- file-2019-B

13 directories, 18 files

You may add --remove-source-files to the list of rsync options to perform a "move" rather than "copy" of the files that you back up.

0

If you haven't found a solution for this yet, try maxdepth option. Or try backing up to a different folder which is not child folder of current search.

Ex- find . -maxdepth 1 -name "2019" -exec mv '{}' ./backup_2019 ;

4
  • Please ignore me for ignoring "Can I solve this without using maxdepth?"
    – kaz
    Aug 30, 2022 at 2:15
  • One of the reasons for not using -maxdepth could be that the files we're looking for may be located deep down in some directory structure. Restricting the search depth would then obviously prevent us from finding those files.
    – Kusalananda
    Aug 30, 2022 at 5:54
  • Agree. However, as per his attempt, creating folder outside of the folder structure would be a good option I guess as I encountered same issue ("are the same file" error) when I tried mv zip files from a folder containing 40+ millions log/zip files to a subfolder and maxdepth helped in my case. Not sure if my understanding is incorrect or there is bug in find, It seems to capture current zip file in the Find search.
    – kaz
    Aug 30, 2022 at 7:18
  • It's not a bug in find.
    – Kusalananda
    Aug 30, 2022 at 7:21
-1

Well that was an unexpected bad surprize. Anyway, you could forget about -exec and try something else instead.

find . -type f -name '*2019*' -print0 | while read -d $'\0' fpath; do mv "$fpath" ./backup_2019; done

Here I am adding -print0 and -d $'\0' to have the null character as delimiter instead of the space (which is default) so that the script would support filenames that have spaces in them.

When you use pipes you are creating subshells and losing access to "global" script variables.

If you need to access a "global variable" inside the loop then you can use process substitution instead of pipes.

myvar=""
while read -d $'\0' fpath 
do 
  mv "$fpath" ./backup_2019
  myvar="Hello World" 
done < <(find . -type f -name '*2019*' -print0)

You must log in to answer this question.

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