Reputation: 15
Using some great help (Create subdirectory under each directory containing a file) I added a \pre subdirectory in any directory that contained a .jpg photo.
I want to move any .jpg files from their current directory into the \pre subdirectory. The script I tried is:
FOR /R c:\temp %G IN (*.JPG) DO pushd %~dpG && if exist *.jpg move *.jpg pre\ && popd
The script moved the .jpg files. The problem is that the script moves the files then follows to the \pre directory and tries to do the move again.
The pre directories have been created using the script linked to in the first paragraph.
For example, directory A\B\C was processed to give A\B\C\pre. This script scans A/B/C and moves the .jpgs to \A\B\C\pre. It then follows the directory tree into A\B\C\pre and tries to move the .jpg files again
Upvotes: 0
Views: 184
Reputation: 49216
I suggest to use this batch file for the file moving task.
@echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "delims=" %%I in ('dir "C:\Temp\*.jpg" /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V /C:"\\pre\\" /C:"\\post\\"') do (
if not exist "%%~dpIpre\" md "%%~dpIpre"
move /Y "%%I" "%%~dpIpre\"
)
endlocal
FOR starts in background one more command process with %ComSpec% /c
and the specified command line appended as additional arguments. So there is executed with Windows installed into C:\Windows
in background:
C:\Windows\System32\cmd.exe /c dir "C:\Temp\*.jpg" /A-D /B /S 2>nul | %SystemRoot%\System32\findstr.exe /I /L /V /C:"\\pre\\" /C:"\\post\\"
DIR executed by the background command process searches
C:\Temp
and all its subdirectories because of option /S
/A-D
(attribute not directory)*.jpg
/B
/S
.This list of file names is redirected from STDOUT (standard output) of background command process with redirection operator |
to STDIN (standard input) of FINDSTR which searches
/I
\pre\
or the literal string \post\
/V
, i.e. the lines not containing \pre\
or \post\
.So FINDSTR is used here as filter to get from the list of *.jpg file names output by DIR with full path just those file names which do not have \pre\
or \post\
in their path to exclude the JPEG files which are already in one of the two subdirectories with name pre
or post
.
2>nul
after the arguments of command DIR suppresses the error message output by DIR if it cannot find any *.jpg file name in C:\Temp
and its subdirectories by redirecting the error message written to STDERR (standard error) to the device NUL.
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul
. The redirection operators >
and |
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 findstr
with using a separate command process started in background.
FOR with option /F
captures everything written to handle STDOUT of started command process by FINDSTR and processes this output line by line after started cmd.exe
terminated itself. It is very important here to process a captured list of file names and do not iterate over one file name after the other returned by the file system because of the files matched by wildcard pattern *.jpg are moved during each loop iteration within the directory structure. So the directory entries matching *.jpg changes on each loop iteration and therefore it is required that a list of file names is processed loaded into memory before moving the files.
FOR with option /F
ignores empty lines which do not occur here.
FOR with option /F
would split up each line into substrings using normal space and horizontal tab as string delimiters and would assign just first space/tab delimited string to specified loop variable I
if not starting with default end of line character ;
in which case the line would be completely ignored like an empty line.
A file name with full path cannot start with ;
. So default eol=;
must not be modified here. But the line splitting behavior is counterproductive because of a full qualified file name can contain one or more spaces. For that reason the option delims=
is used to define an empty list of string delimiters which disables completely the line splitting behavior.
Therefore each full file name output by DIR not containing \pre\
or \post\
in path as filtered out by FINDSTR is assigned to loop variable I
one after the other.
It is checked next if there is for the current JPEG file a subfolder pre
and this folder is created if not already existing. Then the current JPEG file is moved into the subdirectory pre
with overwriting the file in pre
with exactly the same file name.
So this batch file can be executed multiple times on C:\Temp
as it ignores all *.jpg files in all subdirectories pre
and post
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
dir /?
echo /?
endlocal /?
findstr /?
for /?
if /?
md /?
move /?
setlocal /?
Upvotes: 1
Reputation: 34989
What about the following script:
@echo off
rem // Enumerate the directory tree:
for /D /R "C:\TEMP" %%G in ("*") do (
rem // Check whether current directory is not named `pre`:
if /I not "%%~nxG" == "pre" (
rem // Check whether there are files:
if exist "%%~G\*.jpg" (
rem // Create sub-directory called `pre`:
md "%%~G\pre" 2> nul
rem // Move files into the sub-directory:
move "%%~G\*.jpg" "%%~G\pre"
)
)
)
Or directly in command prompt:
@for /D /R "C:\TEMP" %G in ("*") do @if /I not "%~nxG" == "pre" if exist "%~G\*.jpg" md "%~G\pre" 2> nul & move "%~G\*.jpg" "%~G\pre"
Upvotes: 2
Reputation: 16266
The problem of doing the operation in the new "pre" directory is solved by getting the list of directories before enumerating over them.
It might be nice to have a one-liner command to do it, but this is getting a bit more complex than what is easy to do in a one-liner.
Here is a PowerShell script that will do it. If you are on a supported Windows platform, PowerShell will be available. This script requires PowerShell 5.1 or higher. If you cannot get to the current PowerShell, the code can be changed to make it work. When you are satisfied that the correct files will be moved, remove the -WhatIf
from the mkdir
and Move-Item
commands.
=== Move-JpegToPre.ps1
$dirs = Get-ChildItem -Directory -Path "C:/src/t"
$extent = 'jpg'
foreach ($dir in $dirs) {
if (Test-Path -Path "$($dir.FullName)/*.$($extent)") {
if (-not (Test-Path -Path "$($dir.FullName)/pre")) {
mkdir "$($dir.FullName)/pre"
}
Move-Item -Path "$($dir.FullName)/*.$($extent)" -Destination "$($dir.FullName)/pre" -WhatIf
}
}
In a cmd.exe shell it can be invoked by:
powershell -NoLogo -NoProfile -File Move-JpegToPre.ps1
Upvotes: 1