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

(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.

Related

How to create folder name without extension of file type using batch script and move in created sub directory?

Help me sir i try more than 100 times but it not exactly works for me.
Currently getting results in wrong way:-
Given below batch script create folder from this file (7101-1- kutana.pdf) name with .pdf extension but not exactly what i need.
Results i need in right way a/q to given below steps
For example
Step 1:-I want to remove 1st five character from file name (7101-1- kutana) and also i want to remove extension from folder name.
Sourcedir=e:\automovesystem\temp
A. source pdf file name :- 7101-1- kutana.pdf
Step 2:-
Create directory under destdir name of 1-(space)kutana and award
B. destdir=e:\automovesystem\prpl
Created directory e:\automovesystem\prpl\1- kutana\award
Step3.check if already folder name available according to step 1 file name.
Step 4: - move same file in same folder which is created in step 2 under sub directory.
#echo off setlocal enableextensions disabledelayedexpansion
Set "sourcedir=e:\automovesystem\temp"
Set "destdir=e:\automovesystem\prpl"
For /f "eol=| tokens=1* delims=-" %%a in ('dir /b /a-d-h "%sourcedir%\*-*" 2^>nul') do (
Md "%destdir%\%%b\award" 2>nul
Move /y "%sourcedir%\%%a-%%b" "%destdir%\%%b\award" )
Endlocal
Additional to your own answer, I probably would have done it a litte more like this:
#Echo Off
SetLocal EnableExtensions
Set "SRC=E:\automovesystem\TEMP"
Set "DST=E:\automovesystem\PRPL"
If Not Exist "%SRC%\" (Exit /B) Else If Not Exist "%DST%\" Exit /B
For /F "Delims=" %%G In ('%SystemRoot%\System32\where.exe "%SRC%":"????-?*-?*.*"
2^>NUL') Do For /F "Tokens=1,* Delims=-" %%H In ("%%~nG"
) Do %SystemRoot%\System32\Robocopy.exe "%%~dpG." "%DST%\%%I\NOTICE" "%%~nxG"^
/Mov 1>NUL 2>&1
All help with this goes to #XionicFire he reworked the whole of this code I'm just posting it for others to use/benefit:
This code comes from a mix of several different people #PrahladDixit & #Compo, the code was not working we couldn't tell why after a LOT of search seems it suffered the usual... "a pro wrote this so no one understands what he did" syndrome, where us worms cannot figure it out, we remade it and organized it so normal human "worms" can understand what its doing and made it a little friendlier so its easier to mod and change as needed, we hope this helps others, works great in windows 11
Code
#ECHO OFF
ECHO This file will create Individual folders inside the current folder using the following Parameters and sort all *.JPG files in current directory accordingly
REM To change target or source directory simply type it in the variable field
SET "SRC=%~dp0"
SET "SRC=%SRC:~0,-1%"
SET "DST=%~dp0"
SET "DST=%DST:~0,-1%"
ECHO.
REM For Diagnostics & Troubleshooting Purposes
ECHO Source: %SRC%
ECHO Destination: %DST%
ECHO Characters: 19
ECHO Where: %SystemRoot%\System32\where.exe "%SRC%":*.jpg
ECHO.
ECHO To Cancel this operation press CTRL-C
PAUSE
SetLocal EnableDelayedExpansion
If Not Exist "%SRC%\" (Exit/B) Else If Not Exist "%DST%\" Exit/B
For /F "Delims=" %%A In ('%SystemRoot%\System32\where.exe "%SRC%":*.jpg') Do (
SET "FNX=%%~nA"
REM Replace 19 for the number of characters from the start of the filename you wish to use as Base for folder creation
SET "FNX=!FNX:~,19!
REM For Diagnostics & Troubleshooting Purposes
REM ECHO !DST!\!FNX!\
If Not Exist "!DST!\!FNX!\" (MD "!DST!\!FNX!" 2>NUL
If ErrorLevel 1 ECHO Unable to create directory !DST!\!FNX!)
If Exist "!DST!\!FNX!\" (MOVE /Y "%%A" "!DST!\!FNX!">NUL 2>&1
If ErrorLevel 1 ECHO Unable to move %%A to !DST!\!FNX!)
)
ECHO ************************ DONE ************************
PAUSE
REM Use in the case of running multiple scripts in a row
REM EXIT/B
I had less knowledge of batch scripting that's why not sleep from yesterday continue
working on to make AutoMoveFilesystem but also i am great thankful to provide stack overflow platform to create this code. i can't able to do without this plateform.
#Echo Off
Set "SRC=E:\automovesystem\TEMP"
Set "DST=E:\automovesystem\PRPL"
SetLocal EnableDelayedExpansion
If Not Exist "%SRC%\" (Exit/B) Else If Not Exist "%DST%\" Exit/B
For /F "Delims=" %%A In ('Where "%SRC%":?????????*.*') Do (Set "FNX=%%~nA"
If Not Exist "%DST%\!FNX:~5,50!\NOTICE" (MD "%DST%\!FNX:~5,50!\NOTICE" 2>Nul
If ErrorLevel 1 Echo Unable to create directory %DST%\!FNX:~5,50!\NOTICE)
If Exist "%DST%\!FNX:~5,50!\NOTICE" (Move /Y "%%A" "%DST%\!FNX:~5,50!\NOTICE">Nul 2>&1
If ErrorLevel 1 Echo Unable to move %%A to %DST%\!FNX:~5,50!\NOTICE))
Timeout -1
Exit/B

Use DelayedExpansion in nested FOR loops

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.

Why a loop in batch file prints a message only at the last execution?

i want to make a batch file, This Batch file must look out, into a folder with the name "Draft" and for every sub-folder will be make a search for a .txt file "list.txt" and when finds this .txt file, Then will be execute a copy from the folder "Draft" to the folder "Ready". I have written a small script but i have some issues.
#echo off
:loop
for /d %%i in ('dir "C:\Users\ntosis\Desktop\Draft" /ad /o:d /s /b') do (
SET a=%%i
echo %a%
)
echo Folder is empty or does not exist
timeout /t 15
goto loop
The problem in this small part of the script is that, the variable "a" cannot save the name of the folder, if i change the echo %a% to echo Hello World then the script prints only one time the message and not as long as the loop runs.
Any ideas?
I'm not sure if there are more bugs in the code but one problem is the missing of delayed expansion. Here is the fix:
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:loop
for /f %%i in ('dir "C:\Users\ntosis\Desktop\Draft" /ad /o:d /s /b') do (
SET a=%%i
echo !a!
)
echo Folder is empty or does not exist
timeout /t 15
goto loop
You have to add SETLOCAL ENABLEDELAYEDEXPANSION at the beginning of your script and replace %a% with !a!. You always to do this when changing a variable inside a for loop (and also in many other cases). For an explanation check http://ss64.com/nt/delayedexpansion.html
EDIT: replaced for /d ... with for /f ...

Reading a file with for statment - Batch

I have this code:
#echo off
for /f "delims=" %%a in ("%~dp0\files\text\lead_r.txt") do set /p key= < lead_r.txt
ECHO %key%
PAUSE
What I want it to do is to open a specific text file, read it and then write what's in it to %key%. It works when lead_r.txt and the batch file are in the same folder but it fails when I move lead.txt to \files\text. There are no files with spaces in \files\text so i don't know where the problem is.
Thanks
Hard to understand your goal, but try next code snippet:
#echo off >Nul
setlocal EnableDelayedExpansion
for /f "delims=" %%a in ("%~dp0\files\text\lead_r.txt") do (
rem the same file in next line
rem set /p key=< lead_r.txt
set /p key= < %%a
ECHO !key!
PAUSE
)
endlocal
Here mind EnableDelayedExpansion keyword of setlocal command = Expand variables at execution time rather than at parse time, if used !key! syntax instead of %key% one.

Batch File to list folders and allow user selection

I have searched and searched to find a solution to (what feels like) a unique problem. Many
answers here have been quite helpful and have gotten me a long way, but the last bit has me stumped.
My batch file needs to open an executable in a folder with a variable name
This folder may be in one of two directories
This is what I originally had and it works
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET DIR1="%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
SET DIR2=C:\Application\SRW\Profiles
IF NOT EXIST %DIR1% (
GOTO PROFILE_2
) ELSE (
GOTO PROFILE_1
)
:PROFILE_1
for /f "delims=" %%A in ('dir %DIR1% /ad /b') do (
set foldername=%%~nxA
)
SET DIR1=%DIR1:"=%
SET DIR=%DIR1%\%foldername%\app.exe
GOTO START_APP
:PROFILE_2
for /f "delims=" %%A in ('dir %DIR2% /ad /b') do (
set foldername=%%~nxA
)
SET DIR=%DIR2%\%foldername%\app.exe
GOTO START_APP
:START_APP
START "" /b "%DIR%"
:EOF
ENDLOCAL
EXIT
Then I was thrown a curveball when I discovered that some users may have multiple profiles in the variable profile folder and they need to be able to select which one to use for that given task. Now I have a variable profile folder and either one or multiple variable profiles within that folder.
I have found code to list the folder names and display them in the command window for selection.
#echo off
cls
setlocal EnableDelayedExpansion
set /a count=0
for /d %%d in (*) do (
set /a count+=1
#echo !count!. %%d
)
setlocal DisableDelayedExpansion
set /P selection="select folder number:"
I also found this routine which allows the user to select a file from a list then it is supposed to translate that file name into a variable.
Batch Script Programming -- How to allow a user to select a file by number from a list of files in a folder?
Unfortunately, I cannot get the example in the link to work as is and I have no idea how to make it work with folder names as the folder name example and the file name example are close but not close enough for me to understand what to do. And even if it somehow does manage to work, how then can I make such a routine work within the original code posted above?
In addition, I really don't want the user to be forced to make a folder selection if there is only one folder. If only one folder exists, it should be placed into the folder name variable automatically and nothing displays to the user at all.
Is what I'm trying to do even possible at all?
Any help is most appreciated.
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
::
:: Set count-of-targets and application name
::
SET /a targets=0
SET appname=app.exe
SET "dots=..."
::
:: clear all "$" variables
::
FOR /f "delims==" %%i IN ('set $ 2^>nul') DO SET "%%i="
::
:: look for app.exe in (1) userprofile... (2) \application\srw...
::
SET dir1="%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
SET dir2="C:\Application\SRW\Profiles"
:: My testing - my directory names
SET dir1="c:\sourcedir"
SET dir2="C:\destdir\test dir"
FOR %%s IN (%dir1% %dir2%) DO (
FOR /f "delims=" %%i IN ('dir /s /b "%%~s\%appname%"') DO ( CALL :process "%%~dpi"
)
)
::
:: Now have TARGETS=#hits; $_n=full pathname; $n=directoryname
::
IF %targets%==1 SET mychoice=1&GOTO chosen
::
:: repeat a menu
::
:again
CLS
FOR /l %%i IN (1,1,%targets%) DO (
IF %%i lss 10 (ECHO(%%i%dots%%dots:~0,1%!$%%i!) ELSE (ECHO(%%i%dots%!$%%i!)
)
SET mychoice=
SET /p mychoice="Please choose 1..%targets% : "
IF NOT DEFINED $_%mychoice% GOTO again
:chosen
CALL SET application="%%$_%mychoice%%%%appname%"
ECHO START "" /b %application%
GOTO :EOF
::
:: Parameter is quoted-full-pathname
::
:process
SET /a targets+=1
SET $_%targets%=%~1
SET $=%~1
FOR %%n IN ("%$:~0,-1%") DO SET $%targets%=%%~nxn
GOTO :eof
From what you've said, this should work.
First steps are to set up. Set the number of targets found and the real application name and clear out any variablenames that start "$".
Next step is to specify the directories to use. I simply copied yours, then overrode those settings with settings to suit my machine - you'd need to delete those overriding lines, of course. Note that the names should be set quoted...
Next, scan from each of the directories for the application. Each time an instance is found, call :process passing the full -terminated pathname.
:process increments the count of targets found and sets the variable $_n to the full pathname passed (dequoted) It then sets $ to the dequoted-full-pathname and uses a syntax-trick to have FOR find the application's immediate directory. %$:~0,-1% is the full pathname minus the last character - the \ so the results looks like a filename. That "filename" is then assigned to $n
Once the directory-scans are finished, %targets% will contain the er, number of targets. If that's 1, then we have all we need - we can only select target number 1, so that's chosen for us.
Otherwise, clear the screen and display a number of lines - %targets% to be precise. The format isn...immediatedirname` and an extra dot is added if the number of targets is less than 10 so that a long list will line up nicely.
Then ask for a line number.
At this point, the only $_ variables that exist are $_1..$_%targets% so if $_%mychoice% exists, it must be a valid choice. If it's not defined, then repeat the question...
CALLing SET application="%%$_%mychoice%%%%appname%" first parses the command, then parses it again. After the first parse, the line becomes (if mychoice=3) SET application="%$_3%app.exe" so on the second occasion,application` is properly set to the full fulename of the application - and quoted.
There wasn't much point in my starting the app, since it doesn't exist, so I just echoed it.
Given the extended requirement:
You's probably be starting this from a shortcut. Suppose you run that shortcut minimised and insert after the SETLOCAL
SET "poweruser=%1"
Add, after
IF %targets%==1 SET mychoice=1&GOTO chosen
the line
IF NOT DEFINED poweruser START "%username% - poweruser" "%~dpnx0" poweruser&GOTO :EOF
And change the
GOTO :EOF
just prior to the label :process to
EXIT
So that if it wasn't run in poweruser mode (therefore poweruser is NOT defined) AND %targets% is NOT 1 (I'm presuming it won't be 0) Then this user hasn't been set as a poweruser in the shortcut, but does have more than one target, so the job is restarted in poweruser mode (ie. maximised.)
Note that it doesn't actually matter what the string is after the "~dpnx0" - so long as it's a string. I just used poweruser What it does is tell the batch that it's being run in a normal window, not minimised.
For powerusrs, you could if you like set the shortcut to normal window or maximised AND put a parameter in the command line after the batchname, which will marginally quicken the procedure. Leaving it without parameters and minimised would suit ALL users as it will auto-adjust if more than 1 target is found.
I have tried this. It worked for me.
I answered your question in two steps. In the first one I translated your original code into a more readable one. Here it is:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET "DIR1=%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
SET DIR2=C:\Application\SRW\Profiles
IF NOT EXIST "%DIR1%" (
for /f "delims=" %%A in ('dir %DIR2% /ad /b') do (
SET DIR=%DIR2%\%%~nxA\app.exe
)
) ELSE (
for /f "delims=" %%A in ('dir %DIR1% /ad /b') do (
SET DIR=%DIR1%\%%~nxA\app.exe
)
)
START "" /b "%DIR%"
ENDLOCAL
EXIT
You should check first that previous code is equivalent to your original one.
In the second step I added the profile selection to the previous code:
#ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET "DIR1=%USERPROFILE%\AppData\Local\Application Workspace\Profiles"
SET DIR2=C:\Application\SRW\Profiles
IF NOT EXIST "%DIR1%" (
for /f "delims=" %%A in ('dir %DIR2% /ad /b') do (
SET DIR=%DIR2%\%%~nxA\app.exe
)
) ELSE (
call :SelectProfile
)
START "" /b "%DIR%"
ENDLOCAL
EXIT
:SelectProfile
rem Get a list of folders in User Profile dir
set count=0
for /f "delims=" %%A in ('dir %DIR1% /ad /b') do (
set /A count+=1
SET DIR[!count!]=%DIR1%\%%~nxA
)
rem Initialize the selected profile
set select=1
if %count% equ 1 goto endSelection
rem Show the profiles menu
cls
echo Available profiles:
echo/
for /L %%i in (1,1,%count%) do echo %%i- !DIR[%%i]!
echo/
rem Let's the user to select the profile
:select
set select=1
set /P "select=Enter number of desired profile [first one]: "
if not defined DIR[!select!] goto select
:endSelection
SET DIR=!DIR[%select%]!\app.exe
exit /B
Please note that previous code fail if the user enter spaces in the answer. This detail may be fixed, if needed.

Resources