Windows CMD - set within for loop is not working - batch-file

I want to write batch file which will loop through all directories containing backup directory and remove files older than X days within it.
On computer which I want run my script there's no "forfile" command.
There's no PowerShell, so CMD or VBScripts seems to be only way of accomplishing this task.
Currently I have problem with "set" statement - it seems that when I'm calling %checkpath% I didn't receive expected folder.
rem we will memorize current directory
pushd %cd%
set folder="C:\Documents and Settings\myname\Desktop"
cd %folder%
rem loop only folders with five chars within their names (unfortunately on less also
for /D %%g in (?????) DO (
set checkpath="%cd%\%%g\backup"
if exist %checkpath% (
for %%a in ('%%g\backup\*.*') do (
set FileDate=%%~ta
set FileDate=%%FileDate:~0,10%%
rem here I want to compare file modification data with current date
)
)
popd
pause

You need to use delayed expansion to read a variable that you have set inside a for loop.
Try this instead
setlocal enabledelayedexpansion
rem we will memorize current directory
pushd %cd%
set folder="C:\Documents and Settings\myname\Desktop"
cd %folder%
rem loop only folders with five chars within their names (unfortunately on less also
for /D %%g in (?????) DO (
set checkpath="%cd%\%%g\backup"
if exist !checkpath! (
for %%a in ('%%g\backup\*.*') do (
set FileDate=%%~ta
set FileDate=!%FileDate:~0,10%!
rem here I want to compare file modification data with current date
)
)
popd
pause
Replacing the %'s with !'s on variables you have created will signal it to use delayed expansion instead.

Bali's answer has a slight mistake. The second set filedate is incorrect, otherwise his is fine, but may not work if you do not have delayed expansion enabled. I fixed his mistake and showed you how to ensure delayed expansion is enabled. I have also made some other changes:
::This command will ensure that the delayed expansion, i.e. the "!"s below,
:: will work. Unfortunately, it also means you loose the results of any
:: "set" commands as soon as you execute the "endlocal" below.
setlocal ENABLEDELAYEDEXPANSION
::you might want
::set "folder=%USERPROFILE%\Desktop"
set "folder=C:\Documents and Settings\myname\Desktop"
rem we will memorize current directory
::If you ran this code with the current directory set to a directory on
::a drive other than C:, your previous code would not have worked to
:: change to your desired target directory. this slight change fixes that.
pushd "%folder%"
rem loop only folders with five chars within their names - unfortunately on less also
::Use capitals for your loop variables. The var names are case sensitive, and
::using capitals ensures there is no confusion between the var names and ~modifiers
for /D %%G in ( ????? ) DO (
set checkpath="%CD%\%%G\backup"
if exist !checkpath! (
for %%A in ('%%G\backup\*.*') do (
set FileDate=%%~tA
set FileDate=!FileDate:~0,10!
rem here I want to compare file modification data with current date
)
)
popd
endlocal
pause
But, you don't need the "setlocal" or delayed expansion if you write it like this:
::you might want
::set "folder=%USERPROFILE%\Desktop"
set "folder=C:\Documents and Settings\myname\Desktop"
rem we will memorize current directory
::If you ran this code with the current directory set to a directory on
::a drive other than C:, your previous code would not have worked to
:: change to your desired target directory. this slight change fixes that.
pushd "%folder%"
rem loop only folders with five chars within their names - unfortunately on less also
for /D %%G in ( ????? ) DO (
if exist "%%~G\backup" (
for %%A in ('%%~G\backup\*.*') do (
for /F "usebackq" %%T in ( '%%~tA' ) do (
echo File date is %%T, todays date is %DATE%
)
)
)
popd
pause

Related

Batch Script To Identify Missing Numerical File Name

I have a custom service that automatically generates files every 60 mins into a particular directory with part of the filename incrementing numerically, Eg:
File_00004.job
File_00003.job
File_00002.job
File_00001.job
Currently I have an issue where on occasion a file isn't generated, which results in gaps in the file sequence. This issue then causes a number of issues if not identified ASAP.
I'd like a batch file to identify if I have a gap in the file name sequence.
Tried looking for solutions from existing posts, but haven't found something that fits, so apologies if this has been covered elsewhere.
#ECHO OFF
SETLOCAL enabledelayedexpansion
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files\t w o"
SET "mask=file_??????.job"
SET "lowest="
SET "highest="
FOR /f "delims=" %%a IN (
'dir /b /a-d /on "%sourcedir%\%mask%" '
) DO (
IF NOT DEFINED lowest SET "lowest=%%~na"
SET "highest=%%~na"
)
SET "lowest=%lowest:*_=1%"
SET "highest=%highest:*_=1%"
ECHO checking range %lowest:~1% to %highest:~1%
:: See whether an entry in the range is missing; report&create an empty file if so.
FOR /L %%a IN (%lowest%,1,%highest%) DO SET "name=%%a"&SET "name=file_!name:~1!.job"&IF NOT EXIST "%sourcedir%\!name!" echo !name! missing&(copy nul "%sourcedir%\!name!" >nul)
GOTO :EOF
Alternative structure for the for /L loop:
FOR /L %%a IN (%lowest%,1,%highest%) DO (
SET "name=%%a"
SET "name=file_!name:~1!.job"
IF NOT EXIST "%sourcedir%\!name!" (
echo !name! missing
copy nul "%sourcedir%\!name!" >nul
copy "d:\path to\template.file" "wherever\!name!" >nul
copy "d:\path to\template.file" "anotherplace\!name!" >nul
echo Batch is fun and powerful
copy "d:\path to\template.file" "a third place\!name!" >nul
)
)
The critical point is the positioning of the ( - must be directly after and on the same line as do or else or the logical comparison clause of if and must be matched by a ) (which doesn't need to be on its own line - I find it easier that way, to align indentation.) )s that are not intended to close a block need to be escaped with ^, thus: ^)

How to batch transfer rename?

There are two folders holding equal amounts of files between them.
I'd like to apply the names from one set ; to the other set of files in no particular order. Inherit the name, but retain the extension .
Input files are .bmp Output files are .ini ( has a gear symbol ).
Example :
folder 1- Flowers.bmp ; folder 2- blank.ini
. To this:
folder 1- Flowers.bmp ; folder 2- Flowers.ini
There would be more files , but equal .
The .ini files are all copies . So they may have a generic name and numbered if that matters to know . Those would all receive one name each from the other .bmp files in the other folder.
Normally I have both folders situated on the Desktop .
I make sure both folders have equal number of files between them . That would be a constant .
I'm trying to stream line some menial repetitive daily tasks .
I did search and what I have found does not really help.
#ECHO OFF
SET "vers=%~1"
IF "%vers%" == "" SET /P "vers=Enter Vers: "
FOR %%F IN (file_XX_*.*) DO CALL :process "%%F"
GOTO :EOF
:process
SET "name=%~nx1"
SETLOCAL ENABLEDELAYEDEXPANSION
SET "name=!name:_XX_=_%vers%_!"
RENAME %1 "%name%"
ENDLOCAL
Hoping to find a solution to this finally .
Ok, here is a slightly long version, but it makes sure it gets the content of each folder.
Note You must ensure that the path to both folders are correctly specified in the third and fourth line:
#echo off
setlocal enabledelayedexpansion
set "source=%userprofile%\Desktop\folder 1"
set "destination=%userprofile%\Desktop\folder 2"
set /a num=0
for %%a in ("%source%\*") do (
set /a num+=1
set "fr!num!m=%%~na"
)
set /a oldn=!num!
set /a num=0
for %%b in ("%destination%\*") do (
set /a num+=1
set "to!num!r!=%%~nxb"
set "ext=%%~xb"
)
pushd "%destination%"
for /l %%i in (1,1,!oldn!) do ren "!to%%ir!" "!fr%%im!%ext%"
popd
pause
You can remove pause at the bottom of the script, once you are happy with the output.
This is not the most effective way, but considering your limited batch experience, I guess an understandable approach is better than an optimized one (using array-like variables).
#echo off
setlocal enabledelayedexpansion
(for %%A in ("folder 1\*.bmp") do echo %%~nA)> "%temp%\names.txt"
<"%temp%\names.txt" (
for %%A in ("folder 2\*.ini") do (
set /p "name="
ECHO ren "%%A" "!name!.ini"
))
The first for loop creates a list of the desired names (from Folder 1). The modifier %%~nA returns the name only.
The second for processes each file in Folder 2 and takes a name from the previously generated file for each file. This depends on the fact that the number of files in both folders are the same, else you may get undesired results.
I disabled the actual ren command by just echoing it. Check the output and only when you are satisfied, remove the ECHO.

A script that counts and prints every ocurrence of not any file inside a common subfolder in a specific path

Although I'm really a newbie in this field, I want to accomplish a task in batch scripting: There is a determinate folder of company contracts in a determinate path, each of this folders (approx. 400) has a common folder (2016) where there might be a file indicating there has been an inspection in this year. What i want is to print every company folder that has not any file in the common 2016 folder and a count of the times this happens.
This is what i have (and does not work at all):
set c=0
for %i /d in (*) do
for %j in ($%i\2016\*) do
if (%j==NUL) then (#echo $%i c+=1 echo %c)`
If you just want to know if there is a file in the 2016 directory you can do this:
#echo off
SetLocal EnableDelayedExpansion
set count=0
for %%i /d in (*) do (
REM first unset variable
set files=
for %%j in (%%i\2016\*) do (
REM will set variable each time a file is encountered
set files=present
)
if not DEFINED files (
REM No files in directory 2016
echo %%i
set /a count+=1
echo !count!
)
)
EndLocal
exit /b 0
I don't see why you use $ before each %i. If you execute this code from the command line use one % for the loop variables i and j. But in a batch-script you'll have to use two of them (%%i, %%j).
Another thing, c+=1 won't work except if you use set /a.
I used delayed expansion because each block code ( between (...)) is parsed as one single command (as if it was all on one line with && between the commands inside the block) and you can't just assign a new value to a variable and read that new value in the same command. That's also the reason why I use !count! instead of %count% (which will give the value before the block). If you'd rather not use delayed expansion, remove the SetLocal EnableDelayedExpansion and replace echo !count! with call echo %%count%% (is another way to read a new value in the same command)
Also, be aware that each echo will end its output with a carriage retur and a newline. So each echo will result in a new line of output.

Rename Files using wildcard paths

Recently I started working and my first task is to write a batch file that automatically changes filenames to filename_date with the original file-ending.
For that you should be able to write paths into a textfile (e.g. paths.txt) and when you start the program, it should take any line (=path->file) from there and rename it.
I got it to work on my PC quiet well but as I gave it to testing they asked to make the use of wildcards Z:\Path\*.* possible.
My current code looks as follows:
#echo off
setlocal EnableDelayedExpansion
cd %~dp0
For /F "tokens=*" %%m in (paths.txt) do (
set path=%%~dpm
set name=%%~nxm
pushd "!path!"
dir
For /r !path! %%f in (!name!) do (
set path=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
set "name=!name!_"
set "name=!name!!date:~6,4!"
set "name=!name!!date:~3,2!"
set "name=!name!!date:~0,2!"
set "name=!name!!ending!"
copy "!datsave!" "!name!"
del "!datsave!"
cls
popd
)
)
I know that a lot of it is probably easier and more efficient to do, but this is my first batch project and I am quiet happy except for the wildcard problem.
So an example would be:
C:\Some\Path\*.*
This line would be in paths.txt.
With the splitting
set path=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
I get the following:
path: C:\Some\Path
name: C:\Some\Path
ending: -empty-
datsave: C:\Some\Path
because name is set to the Path at the start of the first FOR-Loop. But that seems to be working if I do not use wildcards.
Now the question: Why does this happen and how do I get rid of it? Or do I just use the wrong type of wildcards?
Again: This is my first time I work with batch, so it might be something simple ;)
Ok, I figured out 2 problems and now it works
set name=%%~nxm evaluates the wildcard. Even if name is *.txt it will return bar.txt.
I replaced that by a basename computation instead: set name=!name:*\=! done enough times (not very subtle but hey batch files forces us to do such things) which preserves the wildcard
The other problem is the for /R loop: after pushd, the argument needs to be . or it won't be scanned.
Last minor one: use rename instead of copy plus delete. It preserves file time and is very fast. Copying then deleting a large file can take a long time.
#echo off
set DEPTH=20
setlocal EnableDelayedExpansion
cd %~dp0
For /F %%m in (paths.txt) do (
set pth=%%~dpm
set z=%%m
set name=!z!
rem brutal basename. We cannot break the inner loop or
rem it would break the upper loop too
for /L %%I in (1,1,%DEPTH%) do set name=!name:*\=!
rem but we can check if it is really a basename
set chkname=!name:*\=!
if not !chkname!==!name! ( echo please increase DEPTH value
pause
exit /B)
rem set name=%%~nxm
pushd "!pth!"
For /r . %%f in (!name!) do (
set pth=%%~dpf
set name=%%~nf
set ending=%%~xf
set datsave=%%~nxf
set "name=!name!_!date:~6,4!!date:~3,2!!date:~0,2!!ending!
echo renaming "!datsave!" to "!name!"
rem ren "!datsave!" "!name!"
popd
)
)
paths.txt contains just a line C:\full\path\to\test\*.txt
my test directory contains 2 text files and 1 other file
output:
renaming "bar.txt" to "bar_20160812.txt"
renaming "foo.txt" to "foo_20160812.txt"
(just uncomment the ren line to get the job done)
Weeeeell First of all thanks again to #Jean-François Fabre and #aschipfl for their patience with me :)
After the hint with the second batch file I had to test a few things as not everything worked as fine, but now everything works great!
Code of the Main file:
#echo off
setlocal EnableDelayedExpansion
cd %~dp0
set DEPTH=20
For /F %%m in (paths.txt) do (
pause
set pth=%%~dpm
REM pushd !pth!
REM set origpth=!cd!
REM popd
set z=%%m
set name=!z!
For /L %%i in (1,1,%DEPTH%) do set
name=!name:*\=!
set chkname=!name:*\=!
if not !chkname!==!name! ( echo depth to small
pause
exit /B)
rem set name=%%~nxm
pushd "!pth!"
For /r . %%f in (!name!) do (
pushd %~dp0
call renamefiles.bat %%f REM "!origpth!"
popd
)
)
And the code of the sub-file:
#echo off
REM set pth=%~dp1
REM set origpth=%2
REM set origpth=%origpath:"=%\
REM If !pth!==%origpth% (
set path=%~dp1
set name=%~n1
set ending=%~x1
set datsave=%~nx1
pushd !path!
set "name=!name!_!date:~6,4!!date:~3,2!!date:~0,2!!ending!"
pause
echo renaming "!datsave!" to "!name!"
rem "!datsave!" "!name!"
cls
popd
REM )
EDIT: After testing around a bit I figured, that subfolders are included as well! I put extra code to both codes marked with REM and two extra spaces. Take out those REM's and the programm will not longer include subfolders when renaming :)

next, previous parallel folder

I am trying to make a relative shortcut using a batchfile and a program to turn the batch in an exe program with an icon.
I need a 'shortcut' to open the next alfabetic parallel folder in an explorer window and one to open the previous. Ideally I would even like it to to close the explorer window used to doubleclick it.
I have so far:
#echo off
echo %cd%
for %%a in ("%cd%") do set folder=%%~na
pushd ..
echo %folder%
echo .
dir /A:D /B
pause
the %folder% has the name of the folder (not path!) from where the batch is executed.
the line: dir /A:D /B gives you an output of multiple lines giving you all the parallel folders (because I go up a level using pushd ..). I really need to find a way to search for the %fodler% value and pick the line above, or below it.
I tried something using for /f but it is not that usefull when processing multiple lines instead of one single string.
anny ideas?
Even though it is not quite clear to me what exactly you want to achieve, the following snippet should do what you want. Simply add it to your script:
setlocal enabledelayedexpansion
for /f %%a in ('dir /b /ad /on') do (
#rem shift the current value through the variables
set previous=!current!
set current=!next!
set next=%%a
#rem check if the "current" value is the right one
if "!current!"=="%folder%" goto :found
)
#rem if we get here the loop has finished without %current% having the expected value
#rem but we need to check if it was the last folder in the directory
if "%next%"=="%folder%" (
set previous=%current%
set current=%next%
set next=
goto :found
)
endlocal
#rem exit here if no match is found (should never happen)
goto :eof
#rem variables should have valid values
:found
echo %previous%
echo %current%
echo %next%
Explanation:
The 3 variables previous, current and next are use like a shift register. In each loop iteration the current directory value is shifted by one place through the variables
At the end of the shifting the current variable is tested for the desired folder
If the loop ends before the condition is true, this means the last folder is the correct one, hence that trailing shifts.
Hope that helps...

Resources