Command prompt: loop and run command on all files - for-loop

I am new to using command prompt. I have a large amount of zip files I need to run a program on.
Running the program on one of the files in the command prompt looks like this:
Tabulate.exe -i S:\Packages\ZipFolderName_1.zip -o S:\Output\ZipFolderName_1
which spits out a csv.
I have found these posts helpful, but cannot seem to implement for my situation:
Iterate all files in a directory using a 'for' loop
Loop on files and run command

Read entire for /? output, pay your attention to ~ modifiers. In the following cmd compound command is used ECHO to merely show Tabulate.exe lines to be executed:
for %I in ("S:\Packages\*.zip") do #ECHO Tabulate.exe -i %~fI -o S:\Output\%~nI
To use the FOR command in a batch program, specify %%variable
instead of %variable. Variable names are case sensitive, so %i is
different from %I.
#ECHO OFF
SETLOCAL EnableExtensions
for %%I in ("S:\Packages\*.zip") do (
rem explaining comments:
rem %~fI expands %I to a fully qualified path name
rem %~nI expands %I to a file name only ↓↓↓↓
ECHO Tabulate.exe -i %%~fI -o S:\Output\%%~nI
rem ↑↑↑↑ remove `ECHO` no sooner than debugged
)

Related

Removing first n characters from folder names

I'm fairly new to batch, my problem is the following:
I have a long list of folders and need to delete the first 3 characters from each of their names. Think 01_Folder1, 02_Folder2, 03_Folder3 and so on. I've tried patching together pieces of CMD commands I've found on the web but could not come up with a script that does what I want it to do. I've even tried using VBScript as I'm more familiar with VB in general but failed to find a solution as well.
Is there an easy way to solve this?
Edit:
Here's my attempt; it's giving me a syntax error but as I am not versed enough in CMD, I cannot really see why:
setlocal enableextensions enabledelayedexpansion
for /d %%i in ("%~dp0*") do (set name=%%i && ren "!name!" "!name:~3!")
endlocal
The FOR command line does not work because of assigned to loop variable i is the name of a directory with full path and so removing the first three characters results in removing drive letter, colon and backslash from path of the directory and not the first three characters from directory name. Further the full qualified directory name is assigned with an additional space to environment variable name because of the space between %%i and operator &&.
One solution would be:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /d %%i in ("%~dp0*") do set "name=%%~nxi" && ren "%%i" "!name:~3!"
endlocal
The disadvantage of this solution is that directory names with one or more exclamation marks in name or path are not processed correct because of enabled delayed expansion resulting in interpreting ! in full directory name as begin/end of a delayed expanded environment variable reference.
Another solution is quite simple with using just the command line:
#for /D %%i in ("%~dp0*_*") do for /F "tokens=1* delims=_" %%j in ("%%~nxi") do #ren "%%i" "%%k"
The outer FOR searches in directory of the batch file for non-hidden subdirectories matching the pattern *_*.
For each directory name assigned with full path to loop variable i one more FOR command is used which processes just the string after last backlash (directory name without path) and splits the string up into substrings (tokens).
The string delimiter is an underscore as defined with option delims=_. The option tokens=1* tells FOR to assign first underscore delimited string to specified loop variable j and everything after one or more underscores after first underscore delimited string to next but one loop variable k according to ASCII table.
The inner FOR would ignore a directory name on which first substring starts with a semicolon as being the default end of line character. But in this case no directory has ; at beginning of its name.
There is one problem remaining with this command line. It does not work on drives with FAT32 or exFAT as file system, just by chance on drives with NTFS file system. The reason is that the list of non-hidden directories changes in file system while the outer FOR iterates over the directory entries matching the pattern.
A better solution loads first the list of directories to rename into memory of Windows command process which is processing the batch file before starting with renaming the directories.
#for /F "delims=" %%i in ('dir "%~dp0*_*" /AD-H /B 2^>nul') do for /F "tokens=1* delims=_" %%j in ("%%i") do #ren "%~dp0%%i" "%%k"
FOR executes in this case in background one more command process with %ComSpec% /c and the command line within ' appended as additional arguments. So executed in background is with Windows installed to C:\Windows:
C:\Windows\System32\cmd.exe /c dir "C:\Batch\File\Path\*_*" /AD-H /B 2>nul
DIR searches in directory of the batch file for
non-hidden directories because of option /AD-H (attribute directory and not hidden)
matching the wildcard pattern *_*
and outputs just the directory names in bare format because of option /B without path to handle STDOUT (standard output) of background command process.
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.
FOR respectively the Windows command process processing the batch file captures everything written to standard output handle of background command process and starts processing it as described above after started cmd.exe terminated itself. So there is in memory already a list of directory names and so the executed REN command does not result anymore in a changed list of directory names on processing one after the other.
Please note that a directory with name 01__Underscore_at_beginning is renamed to Underscore_at_beginning and not to _Underscore_at_beginning by both single line solutions.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
endlocal /?
for /?
ren /?
set /?
setlocal /?

Using FOR /R for recursive search only in a subset of folder hierarchy

I want to create a batch file able to apply some processing on each JPG file in a folder hierarchy. The following script file works very well for that case (here I only echo the name of each file, but this should be replaced by some more complex statements in the real application):
:VERSION 1
#echo off
set "basefolder=C:\Base"
for /r %basefolder% %%f in (*.jpg) do echo %%f
Actually, I don't want to explore all the folder hierarchy under %basefolder%, but only a given list of subfolders. This modified script is able to deal with that case :
:VERSION 2
#echo off
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
pushd %basefolder%\%%~s"
for /r %%f in (*.jpg) do echo %%f
popd
)
Is there a solution to remove the pushd/popd pair of statements, to get something closer to the initial script. I thought that one of the following scripts would do the job:
:VERSION 3
#echo off
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
for /r %basefolder%\%%~s" %%f in (*.jpg) do echo %%f
)
or, using delayed expansion:
:VERSION 4
#echo off
setlocal enabledelayedexpansion
set "basefolder=C:\Base"
set "subfolders=A B C"
for %%s in (%subfolders%) do (
set "folder=%basefolder%\%%~s"
echo !folder!
for /r !folder! %%f in (*.jpg) do echo %%f
)
but none of them is working. When running the second one, the echo !folder! command in the external loop shows C:\Base\A, C:\Base\B and C:\Base\C as expected, but the inner loop doesn't echo any JPG file, so I guess that the recursive for /r command does not run correctly.
What am I doing wrong ?
Final edit after answers :
Thanks to #aschipfl who provided a link to the answer posted by #jeb on another question, quoted below:
The options of FOR, IF and REM are only parsed up to the special character phase. Or better the commands are detected in the special character phase and a different parser is activated then. Therefore it's neither possible to use delayed expansion nor FOR meta-variables in these options.
In other words, my versions 3 and 4 do not work because when defining the root folder of the FOR /R command, neither the %%~s nor the !folder! are correctly expanded by the expression parser. There is no way to change that, as this is a parser limitation. As I said in a comment below: the root folder option in the FOR /R command is basically only syntactic sugar to avoid the use of pushd/popd before and after the command. As this syntactic sugar is incomplete, we have to stick to the original syntax for some specific use cases, as the one presented here. The alternatives proposed by #Gerhard (using a subroutine CALL) or by #Mofi (parsing the result of a DIR command) are working, but they are neither more readable nor more efficient than the simple pushd/popd version I proposed initially.
My Approach for this would be really straight forward:
#echo off
set "basedir=C:\Base"
set "subfolders="A","B","C""
for %%i in (%subfolders%) do for /R "%basedir%" %%a in ("%%~i\*.jpg") do echo %%~fa
The double quotes inside of the subfolders variable is important here, it will ensure that folder names with whitespace are not seen as separators for the folder names. For instance:
set "subfolders="Folder A","Folder B","Folder C""
Edit
#echo off
set "basedir=C:\Base"
set "subfolders="A","B","C""
for %%i in (%subfolders%) do call :work "%%~i"
goto :eof
:work
for /R "%basedir%\%~1" %%a in (*.jpg) do echo %%~fa
It is in general not advisable to assign the value of a loop variable to an environment variable and next use the environment variable unmodified without or with concatenation with other strings being coded in batch file or defined already above the FOR loop within body of a FOR loop. That causes just problems as it requires the usage of delayed expansion which results in files and folders with one or more ! are not correct processed anymore inside body of the FOR loop caused by double parsing of the command line before execution, or command call is used on some command lines, or a subroutine is used called with call which makes the processing of the batch file much slower.
I recommend to use this batch file for the task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "basefolder=C:\Base"
set "subfolders=A B C "Subfolder D" SubfolderE"
for %%I in (%subfolders%) do for /F "delims=" %%J in ('dir "%basefolder%\%%~I\*.jpg" /A-D /B /S 2^>nul') do echo %%J
endlocal
The inner FOR loop starts for each subfolder defined in subfolders in background one more command process with %ComSpec% /c and the DIR command line appended as additional arguments. So executed is with Windows installed to C:\Windows for example for the first subfolder:
C:\Windows\System32\cmd.exe /c dir "C:\Base\A\*.jpg" /A-D /B /S 2>nul
The command DIR searches
in specified directory C:\Base\A and all it subdirectories because of option /S
for files because of option /A-D (attribute not directory) including those with hidden attribute set
matching the pattern *.jpg in long or short file name
and outputs to handle STDOUT of background command process just the matching file names because of option /B (bare format)
with full path because of option /S.
The error message output by DIR on nothing found matching these criteria is redirecting from handle STDERR to device NUL to suppress it.
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 output to handle STDOUT of background command process is captured by FOR respectively the command process which is processing the batch file. FOR processes the captured output line by line after started cmd.exe terminated itself. This is very often very important. The list of files to process is already in memory of command process before processing the first file name. This is not the case on using for /R as this results in accessing file system, getting first file name of a non-hidden file matching the wildcard pattern, run all commands in body of FOR and accessing the file system once again to get next file name. The for /R approach is problematic if the commands in body of FOR change a file to process like deleting, moving, modifying, copying it in same folder, or renaming a found file because of the entries in file system changes while for /R is iterating over these entries. That can easily result in some files are skipped or some files are processed more than once and it could result also an endless running loop, especially on FAT file system like FAT32 or exFAT. It is never good to iterate over a list of files on which the list changes on each iteration.
Command FOR on usage of /F ignores empty lines which do not occur here. A non-empty line is split up into substrings using a normal space and a horizontal tab as string delimiters by default. This line splitting behavior is not wanted here as there could be full qualified file names containing anywhere inside full name one or more spaces. For that reason delims= is used to define an empty list of delimiters which disables the line splitting behavior.
FOR with option /F would also ignore lines on which first substring starts with ; which is the default end of line character. This is no problem here because of command DIR was used with option /S and so each file name is output with full path which makes it impossible that any file name starts with ;. So the default eol=; can be kept.
FOR with option /F assigns by default just first substring to specified loop variable as tokens=1 is the default. This default can be kept here as splitting the lines (full file names) into substrings is disabled already with delims= and so there is always the full file name assigned to the loop variable.
This example uses just echo %%I to output the file names with full path. But it is now safe to replace this single command by a command block which does more with the JPEG files because of the list of JPEG files for each specified subfolder tree in base folder is always already completely in memory of command process processing the batch file.

Windows batch file with loop through subfolders

I didnt succeed writing an approriate batch file and would appreciate some help.
Let's say I have an exe in D:\Test\ called script.exe.
To execute this script.exe it requires an additional argument of a .bin files (e.g. bin01.bin, bin02.bin).
Therefore it would be like: script.exe -i bin01.bin, script.exe -i bin01
I want the script to execute with all .bin files from all subfolders
D:\Test\script.exe
D:\Test\Folder01\bin01.bin
D:\Test\Folder01\bin02.bin
D:\Test\Folder02\bin01.bin
D:\Test\Folder03\bin01.bin
anyone could help me here?
Thanks a lot in advance.
For direct execution from within a command prompt window:
for /R "D:\Test" %I in (*.bin) do #D:\Test\script.exe -i "%I"
And the same command line for usage in a batch file:
#for /R "D:\Test" %%I in (*.bin) do #D:\Test\script.exe -i "%%I"
The command FOR searches recursive in directory D:\Test and all subdirectories for files matching the wildcard pattern *.bin.
The name of each found file is assigned with full path to case-sensitive loop variable I.
FOR executes for each file the executable D:\Test\script.exe with first argument -i and second argument being the name of found file with full path enclosed in double quotes to work for any *.bin file name even those containing a space or one of these characters: &()[]{}^=;!'+,`~.
# at beginning of entire FOR command line just tells Windows command interpreter cmd.exe not to echo the command line after preprocessing before execution to console window as by default.
# at beginning of D:\Test\script.exe avoids the output of this command to console executed on each iteration of the loop.
Most often the echo of a command line before execution is turned off at beginning of the batch file with #echo off as it can be seen below.
#echo off
for /R "D:\Test" %%I in (*.bin) do D:\Test\script.exe -i "%%I"
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
echo /?
for /?

How to process 2 FOR loops after each other in batch?

My problem is that two FOR loops are working separately, but don't want to work one after another.
The goal is:
The first loop creates XML files and only when the creation has already been done the second loop starts and counts the size of created XML files and writes it into .txt file.
#echo off
Setlocal EnableDelayedExpansion
for /f %%a in ('dir /b /s C:\Users\NekhayenkoO\test\') do (
echo Verarbeite %%~na
jhove -m PDF-hul -h xml -o C:\Users\NekhayenkoO\outputxml\%%~na.xml %%a
)
for /f %%i in ('dir /b /s C:\Users\NekhayenkoO\outputxml\') do (
echo %%~ni %%~zi >> C:\Users\NekhayenkoO\outputxml\size.txt
)
pause
This question can be answered easily when knowing what jhove is.
So I searched in world wide web for jhove, found very quickly the homepage JHOVE | JSTOR/Harvard Object Validation Environment and downloaded also jhove-1_11.zip from SourceForge project page of JHOVE.
All this was done by me to find out that jhove is a Java application which is executed on Linux and perhaps also on Mac using the shell script jhove and on Windows the batch file jhove.bat for making it easier to use by users.
So Windows command interpreter searches in current directory and next in all directories specified in environment variable PATH for a file matching the file name pattern jhove.* having a file extension listed in environment variable PATHEXT because jhove.bat is specified without file extension and without path in the batch file.
But the execution of a batch file from within a batch file without usage of command CALL results in script execution of current batch file being continued in the other executed batch file without ever returning back to the current batch file.
For that reason Windows command interpreter runs into jhove.bat on first file found in directory C:\Users\NekhayenkoO\test and never comes back.
This behavior can be easily watched by using two simple batch files stored for example in C:\Temp.
Test1.bat:
#echo off
cd /D "%~dp0"
for %%I in (*.bat) do Test2.bat "%%I"
echo %~n0: Leaving %~f0
Test2.bat:
#echo %~n0: Arguments are: %*
#echo %~n0: Leaving %~f0
On running from within a command prompt window C:\Temp\Test1.bat the output is:
Test2: Arguments are: "Test1.bat"
Test2: Leaving C:\Temp\Test2.bat
The processing of Test1.bat was continued on Test2.bat without coming back to Test1.bat.
Now Test1.bat is modified to by inserting command CALL after do.
Test1.bat:
#echo off
cd /D "%~dp0"
for %%I in (*.bat) do call Test2.bat "%%I"
echo Leaving %~f0
The output on running Test1.bat from within command prompt window is now:
Test2: Arguments are: "Test1.bat"
Test2: Leaving C:\Temp\Test2.bat
Test2: Arguments are: "Test2.bat"
Test2: Leaving C:\Temp\Test2.bat
Test1: Leaving C:\Temp\Test1.bat
Batch file Test1.bat calls now batch file Test2.bat and therefore the FOR loop is really executed on all *.bat files found in directory of the two batch files.
Therefore the solution is using command CALL as suggested already by Squashman:
#echo off
setlocal EnableDelayedExpansion
for /f %%a in ('dir /b /s "%USERPROFILE%\test\" 2^>nul') do (
echo Verarbeite %%~na
call jhove.bat -m PDF-hul -h xml -o "%USERPROFILE%\outputxml\%%~na.xml" "%%a"
)
for /f %%i in ('dir /b /s "%USERPROFILE%\outputxml\" 2^>nul') do (
echo %%~ni %%~zi>>"%USERPROFILE%\outputxml\size.txt"
)
pause
endlocal
A reference to environment variable USERPROFILE is used instead of C:\Users\NekhayenkoO.
All file names are enclosed in double quotes in case of any file found in the directory contains a space character or any other special character which requires enclosing in double quotes.
And last 2>nul is added which redirects the error message output to handle STDERR by command DIR on not finding any file to device NUL to suppress it. The redirection operator > must be escaped here with ^ to be interpreted on execution of command DIR and not as wrong placed redirection operator on parsing already the command FOR.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
cd /?
dir /?
echo /?
for /?
And read also the Microsoft article Using command redirection operators.
You need to use the START command with the /WAIT flag when you launch an external application.
I believe it would look something like this:
START /WAIT jhove -m PDF-hul -h xml -o C:\Users\NekhayenkoO\outputxml\%%~na.xml %%a
That should cause the batch file to pause and wait for the external application to finish before proceeding.

How to escape in windows command shell?

I've a file in c:\Program Files directory named tmp.txt
For each line in tmp.txt, I would like to execute a command.
I am trying to use command prompt for loop but it is not able to find the tmp.txt. Note that I've to run this command out of c:\Program Files directory.
Here is how I am trying:
C:\>for /F %i in ("c:\Program Files\tmp.txt") do echo "%i"
the output is:
C:\>echo "c:\Program"
"c:\Program"
which means that for is considering "c:\Program" as parameter and passing it to do
If I put file in c:\, and run for loop as-
C:\>for /F %i in (c:\tmp.txt) do echo "%i"
it works just fine
So my question is- how do I pass full path to for loop so that for consider it as file
Use the usebackq option to for /f:
for /f "usebackq" %i in (...)
This changes the semantics of the various quote characters, as the help states as well:
usebackq - specifies that the new semantics are in force,
where a back quoted string is executed as a
command and a single quoted string is a
literal string command and allows the use of
double quotes to quote file names in
file-set.

Resources