14

I have a bunch folders within folders. Like this

Root Folder
    -- Folder 1
    -- Folder 2
    -- Folder 3
    -- Folder 4

I want to collapse all folders so that all files under folder1, folder 2, folder 3, ... folder n exist under root folder.

What is the quickest way to do this in windows?

  • 5
    It depends, how would you want to handle duplicate file names? – Mark Allen Feb 22 '13 at 22:45
16

There's no reason to use special tools or even scripting. Just use the search function in Explorer.

  1. Open the folder you wish to flatten in Explorer.
  2. Create a new folder, select all files (but not folders), and drag them to that folder (This will avoid getting two copies of those files.)
  3. In the search box (the one on the right of the address bar), type in * and press Enter.
  4. After the list populates, right click and select all, then right click and Cut.
  5. Press the back button to get back to the original folder, then right-click and paste.
  6. If you have any duplicate names, you will be prompted on how to deal with this. Choose the most appropriate option for your use case. (I would assume it would be to copy both files.) To avoid being prompted again, be sure to apply this to all files with the checkbox at the bottom.
  7. Select all folders, make sure they are empty by looking at the popup about the number of files selected, and then delete them.

If you would rather have a script so you can just start it and forget it, that can also be done. The command to replace mv *\* . is pretty easy (for /r %f in (*.*) do @move "%~f" .) but I'm sure you'd like to handle duplicates, without leaving them behind as in @Nicole Hamilton's answer. Appending a number to the filename of duplicates is probably the easiest way to do it.

Copy and paste the following batch file into Notepad and save it as "flatten.cmd" (including the quotation marks):

@echo off
cd "%~1"
md tempryfolder
move *.* tempryfolder
for /r %%f in ("*.*") do call :START "%%~f"
for /r /d %%f in (*.*) do rd "%%~f" 
GOTO :EOF
:START
setlocal
If exist "%~n1%~x1" set /a num=2
:LOOP
If exist "%~n1%num%%~x1" (
    set /a num+=1
    goto loop
)
move "%~1" "%~n1%num%%~x1"

To use the script, simply drag the folder you wish to flatten on top of it.

  • 1
    Similar but easier to understand: superuser.com/a/523854/433830 – aparente001 Feb 9 '18 at 13:32
  • Note: this script is essentially a fork bomb. I'm using it on a folder with thousands of subdirectories and tens of thousands of subfiles and the memory footprint is slowly increasing. I don't think I'm going to run into OOM issues, but it's something to keep in mind. – Nzall Nov 23 '18 at 7:53
  • @Nzall I have no problem believing that it could do that, but I would blame Microsoft, not my code. All I do is call a subroutine in the batch file multiple times. And I never run more than one at time: each subroutine runs until completion, running only one process at a time. How much memory are we talking about being used? – trlkly Nov 24 '18 at 0:37
  • @trlkly At one point it went up to 2 GB, although I did have a quite large amount of folders and subfolders, around 75,000 files and thousands of folders. A few moments after it dropped back down to around 8-900 MB. There were loads of duplicate files though. I was flattening 12 copies of a 7,500 file Javascript library so I could test how certain software would handle a folder with tens of thousands of files. – Nzall Nov 25 '18 at 21:35
  • @Nzall That's incredibly weird to me. Nothing the script does should do that. It's one subroutine that reuses a single variable to increment the file. I may have to ask what could cause such large memory usage on SO or something. – trlkly Nov 26 '18 at 5:06
5

You can flatten in Windows Explorer by navigating to the folder you wish to flatten and searching for System.Kind:<>folder to exclude folders from the results.

You can then select all the files with CTRL+A and copy and paste them into a new folder. Note that Windows Explorer will ask you how to deal with duplicate filenames.

enter image description here

  • 1
    note that for this to work you need to type "folder" in the corresponding localized translation (for example, in italian is System.Kind:<>cartella) – JohnKiller Oct 8 '18 at 10:49
0

This kind of thing is easy with a Unix shell. Here's what you could do with my own Hamilton C shell (including the free version.) You could do something very similar with Cygwin bash.

mv *\* .

If your directory is really huge, it's possible that could wildcard into too long a command line for Windows to pass to a child process. (The Windows CreateProcess call limits the argument list to 32K Unicode characters.) But the C shell has no limit internally, so you could iterate over the whole list this way:

foreach i ( *\* )
   mv $i .
end

To @MarkAllen's point above, this kind of assumes there are no duplicate names. If there are, the later copies in the list will overwrite the older ones. If that's a concern, you could iterate over the files, mv'ing them only if you're not overwriting. The :t operator is the tail operator. The -e operator is the file exists test.

foreach i ( *\* )
   if ( -e $i:t ) then
      echo -2 $i not moved because it already exists
   else
      mv $i .
   end
end

This still leaves the empty directories, which maybe you don't want after they've been emptied. That could be the next iteration if it mattered.

  • Using cygwin >mv * . mv: cannot stat `*': No such file or directory – More Than Five Feb 23 '13 at 0:00
  • In Cygwin bash, mv */* . works for me. – Nicole Hamilton Feb 23 '13 at 0:18
  • 3
    Some people are so fixed in UNIXism... If you really want to do scripting in Windows 7, there's PowerShell, you don't need some bogus bash simulator like Cygwin. – user127350 Feb 23 '13 at 23:53
  • You don't even need PowerShell. The regular command line still exists and can do the same things. See my answer below. – trlkly Apr 16 '14 at 3:56

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.