1

since I'm on a mac, the mv is a bit different which doesn't support the --backup attribute, the best I can come up with is

find . -mindepth 2 -type f -print -exec mv {} . \;     
  • 1
    There is nothing stopping you (as far as I know) from installing GNU coreutils via e.g. Homebrew, on your macOS system. That would give you access to the GNU mv that you seem to be familiar with. – Kusalananda Feb 21 at 7:26
1

You'll need to do a bit of scripting. Something like:

find . -mindepth 2 -type f -print | while read x; do
    y=$(basename "$x")
    if [ -f "$y" ]; then
        mv "$y" "$y".backup
    fi
    mv "$x" "$y"
done
  • It's exactly what GNU mv does. I'm also assuming that other non-regular files will simply be backuped like any regular file, I see no reason why that shouldn't be done. – wurtel Feb 21 at 12:47
  • Good one! That should probably be if ! [ -d "$y" ]; then in that case. – wurtel Feb 21 at 13:26
  • Again you're right. I'll stop making comments, I'm not having a very bright day apparently... – wurtel Feb 21 at 13:35
  • No worries. It's fiddly stuff. – Kusalananda Feb 21 at 13:38
1

To create a simple backup of the target of the mv command, unless it's a directory:

if [ -e "$target" ] && [ ! -d "$target" ]; then
    mv "$target" "$target.backup"
fi

This would obviously have unwanted consequences if $target.backup is an existing directory. Also, we overwrite any old $target.backup if it exists.

It would be safer to use an enumerated backup scheme.

To create an enumerated backup of the target of the mv command, unless it's a directory:

suffix=1
if [ -e "$target" ] && [ ! -d "$target" ]; then
    while [ -e "$target.$suffix" ]; do
        suffix=$(( suffix + 1 ))
    done

    mv "$target" "$target.$suffix"
fi

This will rename $target to $target.N where N is the first positive integer that makes the filename $target.N unused.

Note, though, that there is a race condition here whereby another instance of this code (or a totally unrelated process) may create $target.$suffix in-between detecting that this name is unused and moving $target to that name.

To avoid that:

suffix=1
if [ -e "$target" ] && [ ! -d "$target" ]; then
    while [ -d "$target.$suffix" ] ||
          ! ln "$target" "$target.$suffix" 2>/dev/null; do
        suffix=$(( suffix + 1 ))
    done
fi

Now we're using ln to create a new name (hard link) for $target. We also skip over any integer that would generate the name of an existing directory. This will fail until a free new name is found.

You can then do

mv "$source" "$target"

Applying this with find to move all regular files as you did:

find . -mindepth 2 -type f -print -exec sh -c '
    for source do
        target=${source##*/}
        suffix=1

        if [ -e "$target" ] && [ ! -d "$target" ]; then
            while [ -d "$target.$suffix" ] ||
                  ! ln "$target" "$target.$suffix" 2>/dev/null; do
                suffix=$(( suffix + 1 ))
            done
        fi

        mv "$source" "$target"
    done' sh {} +

Testing:

$ tree
.
|-- dir1
|   `-- file
|-- dir2
|   `-- file
`-- dir3
    `-- file

3 directories, 3 files
$ # the find command goes here
./dir1/file
./dir2/file
./dir3/file
$ tree
.
|-- dir1
|-- dir2
|-- dir3
|-- file
|-- file.1
`-- file.2

3 directories, 3 files

In this example, file would be the most recently found file file, file.2 the one found before that, and file.1 the one found first by find.


Unfortunately, the implementation of mv -n on macOS does not return a usable exit status. If it returned non-zero when the mv could not be performed, this could be used to solve this issue in an easier way with less race conditions.

-1

this is what I end up

num=1
find . -mindepth 2 -type f -print | while read x; do
    y=$(basename "$x")
    if [ -f "$y" ]; then
        mv "$y" "$y"."$num"
        num=$(( $num + 1 ))
    fi
    mv "$x" "$y"
done

so instead of using .backup, it could still happen if there's another same name. thanks for the inspiration!

  • You are potentially overwriting your backups if you run this several times. Also the numbers will be applied in a strange way, creating 1.another, then 2.file, if another and file are already present in the current directory. – Kusalananda Feb 21 at 10:55
  • yea, it should be "$y"."$num", right after I ran the script and I corrected it in my computer, but forgot ti edit it back here. thanks for pointing that out! – Jim Feb 22 at 15:48
  • That's not quite what I meant. If a file called another and another file called file is present in the current directory, and two files with the same names are moved there, your code would move the old another file to another.1 and then the file file to file.2 (not to file.1). – Kusalananda Feb 22 at 16:00
  • I see! you're correct! but for me, my lower end solution worked for me because I want to avoid the file overwrite situation. Your solution is much better / thoughtful! – Jim Feb 22 at 16:04

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.