5

I have a number of subdirectories and I would like to move any files contained therein to the current directory using a one line Terminal command on the Mac. I know that my file names are not all unique and would like to add a suffix before the file extension.

I found a similar question Q: Move all files in sub-directories to current directory? which suggests using:

find ./*/* -type f -print0 | xargs -0 -J % mv % . 

But that's only helpful with unique filenames. For example, given the following tree:

.
├── bar
│   ├── test1.jpg
│   ├── test2.jpg
│   └── test3.jpg
├── foo
│   ├── test1.jpg
│   ├── test2.jpg
│   └── test3.jpg
├── qux
│   └── test3.jpg
└── corge
    └── test3.jpg

I would like to see a result similar to:

.
├── bar
├── foo
├── qux
├── corge
├── test1.jpg
├── test1a.jpg
├── test2.jpg
├── test2a.jpg
├── test3.jpg
├── test3a.jpg
├── test3b.jpg
└── test3c.jpg

Can anyone help?

3 Answers3

1

Get a recursive list of all regular files in the current directory for each file, cut the leading "." and slashes in it to concatenate the directory and file names into one unique string, then move it to the current directory

list=$(find . -type f)
for file in $list; do
   echo -n "$file to "
   echo $file|cut -d '.' -f2-99| tr -d '/'
   mv "$file" ./"$(echo $file|cut -d '.' -f2| tr -d '/')"
done
1

Copy this script to your top directory, make it executable and run it:

#!/bin/bash

Get a list of all files

list=$(find . -mindepth 2 -type f -print) nr=1

Move all files that are unique

find . -mindepth 2 -type f -print0 | while IFS= read -r -d '' file; do mv -n "$file" ./ done list=$(find . -mindepth 2 -type f -print)

Checking which files need to be renamed

while [[ $list != '' ]] ; do ##Remaming the un-moved files to unique names and move the renamed files find . -mindepth 2 -type f -print0 | while IFS= read -r -d '' file; do current_file=$(basename "$file") mv -n "$file" "./${nr}${current_file}" done

Incrementing counter to prefix to file name

nr=$((nr+1)) list=$(find . -mindepth 2 -type f -print) done

warhansen
  • 389
0

Unless the strict sequencial naming in your example is super-important to you, I would propose the following "keep it simple" / "quick & dirty" solution instead:

find . -type f |
while read filename; do
    basename=$(basename "$filename");
    newname=$(echo "$basename" | sed "s/^\(.*\)\(\.[^\.]\+\)$/\1(XXXX)\2/");
    printf "mv \"%s\" \"%s\";\n" "$filename" "$(mktemp -u "$newname")";
done

This only prints the commands that would be executed, so check for sanity by reviewing the output, then copy & paste into commandline, if all is to your satisfaction.

In english: find me all regular files, transform me their filenames from "test1.jpg" into form "test1(XXXX).jpg", where "XXXX" will be picked up and replaced by mktemp with random characters, then create the "mv" commandline for this action.

To decrease filename collision risk further, simply add more "X"es to it.

Have fun.

Paxsali
  • 313