Using for Loop to Move Files from Subdirectories to Parent Directories

Using for loop to move files from subdirectories to parent directories

Small change. change

subs=ls $dir1

to

subs=`ls $dir1`

Notice the backquotes. Backquotes actually execute the bash command and return the result. If you issue echo $subs after the line, you'll find that it correctly lists folder1, folder2.

Second small change is to remove double quotes in the mv command. Change them to

mv $dir1/$i/*/* $dir1/$i

Double quotes take literal file names while removing quotes takes the recursive directory pattern.

After that, your initial for loop is indeed correct. It will move everything from sub1 and sub2 to folder1 etc.

Move All Files from all Subfolders to Parent Folder using batch script

The best solution is using just one batch file stored in a directory of user´s choice and use a shortcut file in the SendTo folder to run the batch file for a folder of user´s choice by clicking with secondary (usually right) pointing device (mouse) button on a folder in Windows File Explorer to open the context menu and clicking with primary (usually left) pointing device button in submenu Send to on the menu item according to file name of the shortcut file.

1. Batch file for file movement

There can be created a batch file %USERPROFILE%\MoveFilesToFolder.cmd with the following single command line:

@if not "%~1" == "" for /F "delims=" %%I in ('dir "%~1\*" /AD-L /B /S 2^>nul') do @move "%%I\*" "%~1\"

The two @ left to the commands if and move are just for avoiding the output of the parsed command lines before execution which is done in a multi-line batch file with @echo off at top of the batch file.

The IF condition evaluates to false if the batch file is started without passing an argument string to the batch file or with passing just "" to the batch file (empty argument string) like on double clicking on the batch file itself. That condition makes sure not doing anything on batch file started without the required folder path as first argument string.

The for /F loop results in starting in background one more Windows command processor in addition to the cmd.exe process which is processing already the batch file with %ComSpec% /c and the command line within ' as additional arguments. So if the Windows is installed into C:\Windows and the batch file was started with "C:\Temp\Development & Test(!)_100%" as first argument string, there is executed in background:

C:\Windows\System32\cmd.exe /c dir "C:\Temp\Development & Test(!)_100%\*" /AD-L /B /S 2>nul

DIR searches

  • in the specified directory C:\Temp\Development & Test(!)_100%
  • only for directories including directories with hidden attribute with ignoring junctions and directory symbolic links because of option /AD-L (attribute directory and not a link)
  • and all its subdirectories because of option /S and
  • outputs all found directories matching the wildcard pattern * (any) in bare format because of option /B which means just folder name with full path because of option /S to handle STDOUT of the background command process.

It is possible that DIR fails to find any matching file system entry because of the specified directory does not have subdirectories at all or the batch file was started with passing a file name instead of a directory name to the batch file. In this case would be output to handle STDERR just an error message by DIR which is suppressed by redirecting the error message to device NUL.

Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.

The Windows command processor instance processing the batch file captures all lines output to handle of STDOUT of background command process and waits for its self-termination before for /F is processing the captured output line by line with ignoring all empty lines.

There is by default split up each non-empty line into substrings using normal space and horizontal tab as string delimiters, then is checked if the first space/tab delimited substring starts with a semicolon (default end of line character) in which case the line is ignored for further processing and otherwise just the first substring is assigned to the specified loop variable and then FOR runs the command(s) in its body.

The captured folder names with full path cannot contain a horizontal tab character and there cannot be a semicolon at beginning of the folder names with full path, but there can be one or more spaces in the fully qualified folder names. For that reason the option delims= is used to define an empty list of string delimiters which results in each folder name is assigned completely to the specified loop variable I.

The command MOVE moves is run with the current folder name and moves nearly all files in that folder to the folder passed to the batch file with first argument string.

The command MOVE does not move files which

  • have the hidden attribute set or
  • are currently opened by an application with sharing access denied or
  • exist already in the destination directory and the user denies overwriting of the file in the destination folder on prompt or
  • exist already in the destination directory and the user confirms overwriting of the file in the destination folder, but the existing file in destination folder has the read-only attribute set.

The usage of the MOVE option /Y would avoid the prompt for the third case with file existing already in the destination folder. But the overwrite prompt appears even with using option /Y on existing file in destination folder with same name as a file in a subfolder has the read-only attribute set. It does not matter which option is chosen by the user on prompt as the read-only file in destination folder is nevertheless not replaced by the file in source folder.

The file movement is done which just updating the file allocation table of the file system of the drive. No file data is copied and deleted.

There could be used on Windows Vista and newer Windows client versions and on Windows Server 2003 and newer Windows server versions also:

@if not "%~1" == "" for /F "delims=" %%I in ('dir "%~1\*" /AD-L /B /S 2^>nul') do @%SystemRoot%\System32\robocopy.exe "%%I" "%~1" /MOV /NDL /NFL /NJH /NJS /R:1 /W:1 >nul

ROBOCOPY with the used options moves always all files including hidden files and read-only files independent on existing files in destination folder to the folder passed as argument string to the batch file also with just updating the file allocation table of the file system. But please note that the single command line solution using ROBOCOPY as posted here does not work on folder path ending with a backslash like on passing the root directory of a drive like D:\ to the batch file. The backlash at end is in this case interpreted by ROBOCOPY as escape character for " marking end of destination path and ROBOCOPY interprets therefore all options up to /W:1 as part of the destination path. The destination path with " and : would be interpreted as invalid by the file system and so no files are moved in this case. There would be additional code necessary in the batch file to handle also such a use case with passed folder path ending with a backslash on using ROBOCOPY.

2. Shortcut file for file movement

There is necessary next a shortcut file with file extension .lnk in the SendTo folder of the user account.

Such a shortcut file can be created by clicking with secondary pointing device button on the batch file %USERPROFILE%\MoveFilesToFolder.cmd and clicking with primary pointing device button in submenu Send to on the item Desktop (create shortcut).

The user´s desktop has now a shortcut file with name MoveFilesToFolder.cmd.lnk whereby the file extension .lnk is not visible on desktop. There should be first clicked with secondary pointing device button on this shortcut and use from context menu Rename to change the file name, for example, to Move files which is later the name of the menu item in the Send to context submenu.

Then a secondary pointing device button click on the shortcut Move files is needed to open again the context menu and clicking this time on last menu item Properties to open the properties window for the shortcut file.

The property Target should be modified to:

 %SystemRoot%\System32\cmd.exe /D /E:ON /V:OFF /C "%USERPROFILE%\MoveFilesToFolder.cmd"

The property Start in should be cleared whereby the directory specified here does not really matter and could be also kept as is. It defines the current directory on execution of cmd.exe which is not important for the batch file which works independent on which directory is the current directory of cmd.exe processing the batch file. There could be used also %SystemRoot%\System32 for Start in which is the directory containing cmd.exe for an even more fail-safe configuration.

The property Run can be configured with selecting Minimized to run cmd.exe for processing the batch file with a minimized window. Please note that when the minimized window does not disappear, the command MOVE prompts the user for overwriting a file. In this case the window must be restored and the prompt answered. There can be used in the batch file as second line also @pause on using the solution with command MOVE and use Normal window for Run in the shortcut file to always see the results of the file movements.

All other properties can be configured by the user according to personal preferences. The selection of a nice icon would be good and a good comment before clicking on button OK.

The shortcut file is now ready for being cut with Ctrl+X and pasted with Ctrl+V into the folder opened by entering in address bar of Windows File Explorer the string %APPDATA%\Microsoft\Windows\SendTo and hitting RETURN or ENTER on Windows Vista and newer Windows client versions. The SendTo directory is on Windows XP and Windows Server 2003 the directory %USERPROFILE%\SendTo.

3. How to use the shortcut and the batch file?

The user can now click with secondary pointing device button in Windows File Explorer on a folder and click in opened context menu in submenu Send to on the menu item Move files.

explorer.exe calls now the Windows kernel library function CreateProcess to run cmd.exe with all the arguments specified with property Target in the shortcut file with appending to the end the fully qualified name of the clicked folder as additional argument. The property Start in in shortcut file defines the string of which function parameter lpCurrentDirectory points to. Most of the other properties in the shortcut file determine other values passed via the STARTUPINFO structure to function CreateProcess.

A user has a lot of control about how cmd is started for processing a batch file with the usage of a shortcut file which is the reason why I recommend using never directly a batch file on desktop, in Windows Start menu, or in special folders like SendTo or Startup.

It could happen that a user clicks with secondary pointing device button on a file and clicks by mistake on menu item Move files in the context submenu Send to instead of the menu item which should be really clicked. The batch file is designed to do nothing in this case caused by running the batch file by mistake with a fully qualified file name as argument string.

How to move files from subdirectory to a directory using a for loop

Like that:

for dir in */*/
do
echo mv "$dir"* "${dir%/*/}" # Drop the echo after tested it
done

Note that this will move all files and directories under $dir to the parent directory of the $dir. If you want to move a specific file, replace "$dir"* with "$dir"file where file is the filename of the specific file. Also be very careful where and how you use this code after echo removed (running in the wrong directory would be catastrophic)

How to move files from subfolders to their parent directory (unix, terminal)

You could try the following bash script :

#!/bin/bash

#needed in case we have empty folders
shopt -s nullglob

#we must write the full path here (no ~ character)
target="/path/to/photos"

#we use a glob to list the folders. parsing the output of ls is baaaaaaaddd !!!!
#for every folder in our photo folder ...
for dir in "$target"/*/
do
#we list the subdirectories ...
for sub in "$dir"/*/
do
#and we move the content of the subdirectories to the parent
mv "$sub"/* "$dir"
#if you want to remove subdirectories once the copy is done, uncoment the next line
#rm -r "$sub"
done
done

Here is why you don't parse ls in bash

Loop through subfolders in a parent folder in Makefile

Recipes are expanded by make before they are executed by the shell. So $(find something) is expanded and, as there is no make macro named find something, it is replaced by the empty string. Double the $ sign (or use backticks) just like you did for shell variable f:

build.all.images:
for f in $$(find $(image_folder) -type d -maxdepth 1 -mindepth 1 -exec basename {} \;); do echo $${f}; done

But using a for loop in a make recipe is frequently a not that good idea. Makefiles are not shell scripts. With your solution (after fixing the $ issue) you will not benefit from the power of make. Make analyzes dependencies between targets and prerequisites to redo only what needs to. And it also has parallel capabilities that can be very useful to speed-up your building process.

Here is another more make-ish solution. I changed a bit the logic to find Dockerfiles instead of sub-directories but it is easy to adapt if you prefer the other way.

DOCKERFILES := $(shell find $(image_folder) -type f -name Dockerfile)
TARGETS := $(patsubst %/Dockerfile,%.done,$(DOCKERFILES))

.PHONY: build.all.images clean

build.all.images: $(TARGETS)

$(TARGETS): %.done: %/Dockerfile
printf 'sub-directory: %s, Dockerfile: %s\n' "$*" "$<"
touch $@

clean:
rm -f $(TARGETS)

Demo:

$ mkdir -p {a..d}
$ touch {a..d}/Dockerfile
$ make -s -j4 build.all.images
sub-directory: c, Dockerfile: c/Dockerfile
sub-directory: a, Dockerfile: a/Dockerfile
sub-directory: d, Dockerfile: d/Dockerfile
sub-directory: b, Dockerfile: b/Dockerfile

With this approach you will rebuild an image only if its Dockerfile changed since the last build. The date/time of the last build of image FOO is the last modification date/time of the empty file named FOO.done that the recipe creates or touches after the build. So, that is less work to do.

Moreover, as the static pattern rule is equivalent to as many independent rules as you have images to build, make can build the outdated images in parallel. Try make -j 8 build.all.images if you have 8 cores and see.

Powershell: Loop through sub-directories and move files

This will accomplish what you are attempting, I'm pretty sure. Your original script doesn't actually recurse, despite specifying that you want it to (Get-ChildItem has some finicky syntax around that), so I fixed that. Also fixed my suggestion (I forgot that the Extension property includes the preceding dot, so 'FileName.jpg' has '.jpg' as the extension). I added in some checking, and have it throw warnings if the file already exists at the destination.

$Directory = dir D:\Temp\IMG\ -Directory
foreach ($d in $Directory) {
Write-Host "Working on directory $($d.FullName)..."
Get-ChildItem -Path "$($d.fullname)\*" -File -Recurse -filter '*.jpg' |
Where{$_.Directory.Name -ne $_.Extension.TrimStart('.')}|
ForEach-Object {
$Dest = join-path $d.FullName $_.Extension.TrimStart('.')
If (!(Test-Path -LiteralPath $Dest))
{New-Item -Path $Dest -ItemType 'Directory' -Force|Out-Null}

If(Test-Path ($FullDest = Join-Path $Dest $_.Name)){
Write-Warning "Filename conflict moving:`n $($_.FullName)`nTo:`n $FullDest"
}Else{
Move-Item -Path $_.FullName -Destination $Dest -Verbose
}
}
}

Loop subfolders with different parent folder

Get-ChildItem allows the use of wildcards in the -Path parameter.

Get-ChildItem -Path 'S:\CLIENT FILES\*\Special File\PRIVATE' | Get-Acl


Related Topics



Leave a reply



Submit