Use DelayedExpansion in nested FOR loops - windows

My objective is to list the local hard disks on PCs and search for specific file on each of them. If found, output the path of the file to a text file that would be named "PCNAME.txt". My expectation is as below:
set filename="abc.exe"
set uncpath="%userprofile%\desktop\"
set fullpath=%uncpath%\%computername%.txt
for /f %%a in ('wmic logicaldisk where "description='Local Fixed Disk'" get Caption ^| find ":"') do (
set letter=%%a
cd %letter%\
for /R %%f in (%filename%) do #IF EXIST %%f #echo File Location^(s^) Identified: >> %fullpath% & goto :search
:search
for /R %%f in (%filename%) do #IF EXIST %%f set filenew=%%f & call :trim
echo. >> %fullpath%
exit /b
:trim
set filenew=%filenew:"=%
#echo %filenew% >> %fullpath%
)
The above script works fine when the first FOR loop is removed and current working directory is changed to C:\ drive. But I would want it to run on all local hard drives. After a lot of googling, I guess the approach might be to use DelayedExpansion. I also understand that the goto inside the FOR loop is canceling the loop. But I am not able to fix the script completely.
I would also like to know if it is possible to eliminate the "trim" label altogether and instead double quotes be removed directly under "search" label using DelayedExpansion or whatever. Any help will be appreciated.

Just don't ever use multi-line code blocks in your batch scripts.
#for ... do #call :SubroutineName %%G
#exit /b 0
:Subroutine
rem Whatever.
Then do your work in the subroutine without having to worry about all the gotcha's involved in multi-line code-blocks. You'll find that such code is easier to debug with echo on as well.

Here's one method using WMIC and Dir:
#Echo Off
Set "filename=abc.exe"
Set "uncpath=%userprofile%\desktop"
Set "fullpath=%uncpath%\%computername%.txt"
Echo File Location(s) Identified:>"%fullpath%"
Set "_="
(For /F "Skip=1" %%A In (
'WMIC LogicalDisk Where "DriveType='3'" Get DeviceID 2^>Nul') Do (
For /F "Delims=" %%B In ('Dir /B/S/A-D-L "%%A\%filename%" 2^>Nul'
) Do Set "_=T" & Echo %%B))>>"%fullpath%"
If Not Defined _ Del "%fullpath%"
If you just want the locations without the file name itself, change %%B on the line 11 to %%~dpB.

Related

looped batch file, naming subfolders with a title,date, and time

(TLDR see last paragraph)
Below code I have cobbled together with information from other threads in this forum and others. I am very new to this code/language so I don't have a clue how to optimize this.
Here is what I am trying to achieve with this code.
script outputs to a .log instead of command window
:top
script searches for a .dat file in "M:\AnalysisDrop\"
if a .dat file is detected (
script automatically creates a subfolder "M:\AnalysisDrop\title_date_time"
file is moved into the folder
file is opened with the program C:\Analysis\Analysis.exe
)
wait 5 seconds
goto top (repeat forever)
this script will continuously repeat for what I hope to be a very long time. Will I run into issues with too many "set" commands? I ran into some issue with doing "SETLOCAL EnableDelayedExpansion" and SETLOCAL DisableDelayedExpansion
#Echo off
echo Script 1 Initiated
#echo on
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetime=%%I
set datetime1=%datetime:~0,8%_%datetime:~8,6%
SETLOCAL EnableDelayedExpansion
SET LOGFILE=Script1_LOG_%datetime1%.log
call :top >> %LOGFILE%
exit /b 0
:top
for %%f in (*.dat) do (
set datetime1=%datetime:~0,8%_%datetime:~8,6%
set foldername=%%~nf_%datetime1%
md "!foldername!"
move "M:\AnalysisDrop\%%~nf*.dat*" "M:\AnalysisDrop\%%~nf_%datetime1%\"
pushd M:\AnalysisDrop\%%~nf_%datetime1%\
C:\Analysis\Analysis.exe '%%~nf.dat'
popd
)
timeout /t 5 /nobreak
goto top
exit /b 0
I honestly dont need the "for" loop but i'm not sure how to do otherwise (tried to replace "for" with "if", no success. Also, the "datetime1" variable wont update, so if I try to run multiple .dat files of the same name, it writes over the previous one in the same subfolder, which I can live with but prefer not to have.
So to clarify my questions:
1. How do i do this without the "for" loop? I understand this may be causing my issue of the datetime variable not updating.
2. I used the "goto" command to continuously loop this script, should I use something different?
Answer to my question, with help from #compo. I wasn't aware that I needed the for \f... statement in the loop as well as the set datetime2. I needed to change %datetime...% to !datetime...! throughout the for loop. See below for fixed code.
In response to #Gerhard Barnard, %time% and %date% is not in a format that I can name directories with unfortunately.
#Echo off
echo Script 2 Initiated
#echo on
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetime=%%I
set datetime2=%datetime:~0,8%_%datetime:~8,6%
SETLOCAL EnableDelayedExpansion
SET LOGFILE=Script2_LOG_%datetime2%.log
call :top >> %LOGFILE%
:top
for %%f in (*.dat) do (
for /f "tokens=2 delims==" %%I in ('wmic os get localdatetime /format:list') do set datetime=%%I
set datetime2=!datetime:~0,8!_!datetime:~8,6!
set foldername=%%~nf_!datetime2!
md "!foldername!"
move "M:\AnalysisDrop\%%~nf*.dat*" "M:\AnalysisDrop\!foldername!\"
pushd M:\AnalysisDrop\!foldername!\
C:\Analysis\Analysis.exe '%%~nf.dat'
popd
)
timeout /t 5 /nobreak
goto top
exit /b 0
I hope this, based upon your original code, helps you with structuring your script:
#Echo Off
Rem Enabling delayed expansion
SetLocal EnableDelayedExpansion
Rem Setting current directory
If /I Not "%CD%"=="M:\AnalysisDrop" (CD /D "M:\AnalysisDrop" 2>Nul||Exit/B)
Rem Setting date and time variable for log file name
Call :GetLDT
Rem Call Main section outputting to log file
Call :Main>>"Script1_LOG_%LDT%.log"
Rem Exit script
Exit/B
Rem Main section
:Main
Rem Loop over each matching file in the current directory
For %%A In (*.dat) Do (
Rem Setting date and time variable for destination directory name
Call :GetLDT
Rem Create directory if it does not exist
If Not Exist "%%~nA_!LDT!\" MD "%%~nA_!LDT!"
Rem Move file to directory
Move "%%~A" "%%~nA_!LDT!"
Rem Run analysis against file in destination
Start "" /D "%%~nA_!LDT!" /W "C:\Analysis\Analysis.exe" '%%~A'
)
Rem Create a delay
Timeout 5 /NoBreak>Nul
Rem Re-run Main section in loop
GoTo Main
Rem GetLDT section
:GetLDT
Rem Setting date and time string
For /F "EOL=L" %%A In ('WMIC OS GET LocalDateTime') Do For %%B In (%%~nA
) Do Set "LDT=%%B"
Rem Formatting string as required
Set "LDT=%LDT:~,8%_%LDT:~-6%"
Rem Return to point after Call
GoTo :EOF
Your command using '%%~A' looks wrong with single quotes, but I'll leave that to you to decide.

How to compare file names in directory with file names in a text file?

I try to improve an overview of my search and find memories for batch files in Windows XP command prompt environment.
In order to my previous sentence I am not happy with my search possibilities and have to post a question.
I try to compare the names of some text files and have written words in a text file that are by reading the same. With such a start environment I wrote following batch script to get an echo output.
The aim is
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%b in ('dir "C:\A Folder"') do set var=%%~nb & echo !var!
rem The output is the name of the files without extension. Now my question:
rem Is it possible to compare the above file names with some input
rem from a text file, for example like:
for /f "delims=" %%b in ('dir "C:\A Folder"') do set var=%%~nb & for /f %%a in (Textfile.txt) do (if !var!==%%a echo good else echo search)
rem That returns no output. I would like to know if there are possibilities
rem to do that? And if it is possible, how to revise this batch file?
endlocal disabledelayedexpansion
pause
Have a nice day, wishes
Stefan
This should work with Latin characters - some foreign characters may not work:
#echo off
for /f "delims=" %%b in ('dir /b /a-d "C:\A Folder\*.*" ') do find /i "%%~nb" < "textfile.txt" >nul && (echo "%%~nb" found) || (echo "%%~nb" not found)
pause
proper formatting you code increases readabilty:
for /f "delims=" %%b in ('dir /b /a-d "C:\A Folder"') do (
for /f %%a in (textfile.txt) do (
if "%%~nb"=="%%a" ( echo good ) else ( echo search )
)
)
I added a /b to the dircommand (show name only, no date/time/attributes) and a /a-d to exclude directorynames.
You don't need to use a variable (!var!) here (but you can, it works fine).

Windows batch operation for adding pdf page dimensions to filename?

I need to include page size information of many single-paged pdf's into their filenames. E.g. "150x250mm.pdf". I found no renamer apps able to do it. I suspect this could be done using a batch file and pdfinfo.exe (from xpdf suite), but I have no idea how to make use of it.. Could you give me some hints?
Yes, you can convert from postscript points to MM. In this case, the script is in the top level folder containing the PDF's to be renamed. It does go into subfolders. If you don't want or need that, remove the /s from the dir command on the 5th line. Change the paths as needed.
#echo off
setlocal enabledelayedexpansion
set "pdfi=U:\Scripts\Utilities\xpdf\pdfinfo.exe"
for /f "delims=" %%a in ('dir /b /s *.pdf') do (
for /f "tokens=3,5 delims= " %%b in (
'%pdfi% -meta "%%a"^|find /i "Page size:"') do (
set pts=%%b %%c
for %%d in (!pts!) do (
call :Eval %%d*.352777778 mm
set "mm1=!mm1!x!mm!"
)
ren "%%~dpfnxa" "!mm1:~1!.pdf"
set mm1=
)
)
exit /b
:Eval <in> <out>
setlocal
if exist eval.vbs del eval.vbs
>eval.vbs echo wsh.echo formatnumber(eval("%1"),0)
for /f "delims=" %%a in (
'cscript //nologo eval.vbs'
) do endlocal & set %~2=%%a
del eval.vbs

Recursive directory processing in a BAT file with a twist

OK, I apologize ahead of time for a) using an old, crappy technology (BAT files) and b) asking what seems to be a redundant question. I'm limited in the technology I'm allowed to use in this particular case and after looking at dozens of posts on the subject I can't find anything I can adapt to what I need.
I have a directory structure that looks like this:
A
B
C
D
etc...
XYZ
more folders
My BAT file is located outside this files system. I need to inspect it starting at level "C" and need to find the "XYZ" directory. The folders between C and XYZ can have variable names depending on the environment in which the files were created. I need to end up with a string that consists of the directory names from C through XYZ (i.e. "C\D\E\F....\XYZ") that I can put into a variable so when my BAT file is completed I can reference the variable and run another command.
I've looked at posts using FIND and FOR but I can't seem to figure out how to a) limit the string to the starting directory (for example when I combine FOR with DIR I get "A\B\C...") and how to stop when I get to "XYZ"...
Any help is greatly appreciated.
This should work in most situations:
#echo off
setlocal enableDelayedExpansion
set "root=c:\a\b\c"
set "target=xyz"
for %%R in ("%root%") do for /f "delims=" %%F in (
'dir /b /s /ad "%root%\%target%"'
) do (
set "fullPath=%%F"
set "relpath=!fullPath:%%~dpR=!"
)
echo !relpath!
It can fail if any of your paths contain ! or =. There are solutions for this, but the code is significantly more complicated.
EDIT
Actually, there is a relatively simple solution using FORFILES that should work in all situations. (Assuming your version of Windows has FORFILES)
#echo off
setlocal disableDelayedExpansion
set "root=c:\a\b\c"
set "target=xyz"
for /f "delims=" %%F in (
'forfiles /p "%root%" /m "%target%" /s /c "cmd /c if #isdir==TRUE echo #relpath"'
) do set "relpath=%%~F"
for %%R in ("%root%") do set "relpath=%%~nxR%relpath:~1%"
echo %relpath%
The only restriction is the code has to change slightly if your result contains poison characters like &. In that case you need to add quotes to the final ECHO statement, or else enable delayed expansion at the end and use echo !relpath!
For a) question:
FOR /F "TOKENS=*" %%d IN ('DIR A\B\C\XYZ /S /AD /B') DO SET variable=%%d
For a) and b) question:
FOR /D /R "A\B\C" %%d IN (*.*) DO IF /I "%%~nxd"=="XYZ" (SET variable=%%d& GOTO :EOF)
but this will exit batch script, so you need:
... your batch code
CALL :GET_XYZ
... your batch code
GOTO :EOF
:GET_XYZ
FOR /D /R "A\B\C" %%d IN (*.*) DO IF /I "%%~nxd"=="XYZ" (SET variable=%%d& GOTO :EOF)
ECHO XYZ not found!
GOTO :EOF

Amending text file strings and using the result to perform an xcopy

I have a windows boot pen that runs a batch file when it starts up, all it needs to do is copy a large list of files specified in a text file from the machine to the boot pen.
I made a test run on my PC before making the boot pen and thought this should work
#echo off
set DRIVE=c
for /F "tokens=*" %%a in (e:\test\files.txt) do call :amendDirectoryAndCopy %%a
pause
:amendDirectoryAndCopy
set DEST=%~1
set DEST=%DEST:~1%
echo set DEST=%DRIVE%%DEST%
echo xcopy %~1 %DEST%
all it should do is for each file, remove the first character of the string, add "c" to the beginning which gives the destination directory, then perform an xcopy. I find the output confusing as "#echo set "DEST=%DRIVE%%DEST%" outputs what I would expect, the correct directory on C: such as
c:\test\folder\file.txt
but the xcopy outputs
xcopy e:\test\folder\file.txt :\test\folder\file.txt
the drive letter is missing on the destination.
I believe SetLocal EnableDelayedExpansion is needed along with its counterpart ! replacement of % in variable expansion to get your code to work.
I am away from my Windows machine right now, so I cannot test the syntax, but off the top of my head, something like this should work:
SetLocal EnableDelayedExpansion
#echo off
set drive=c
set ready_to_move=0
set file_list=e:\test\files.txt
if "!ready_to_move!" == "1" (
set echo=
) else (
set echo=echo
)
for /F "eol=; tokens=1 delims=" %%f in ('type "!file_list!"') do (
set source=%%~f
for /f "tokens=* delims= " %%s in ("!source!") do set source=%%s
set destination=!drive!!source:~1!
!echo! xcopy "!source!" "!destination!"
)
Does this work for you?

Resources