I've a batch script in which I loop trough all the filenames in a folder and then I invoke a command in order to remove the extension from the filename. I'd like to store at every iteration the filename (without extension) in a variable named result for later reuse.
The RemoveExtension function works fine. However I'm not able to retrieve the result and store it in the _result variable. When I print it, it's always empty. Thanks for your help!
#echo OFF
SETLOCAL EnableDelayedExpansion
set "_result="
for /f "delims=" %%i in ('dir "%~1\*.txt" /b') DO (
echo.filepath: "%~1\%%i"
call :RemoveExtension "%%i"
echo._result: "%_result%" // The "_result" variable is always EMPTY ""
)
goto :eof
:RemoveExtension
SETLOCAL
REM echo "%~1"
set "filename=%~1"
:loop
if "%filename:~-1%" NEQ "." (
set "filename=%filename:~0,-1%
goto :loop
)
set "filename=%filename:~0,-1%"
echo "%filename%"
ENDLOCAL & set "_result=%filename%"
goto :eof
Try this...much simpler way to remove the extension. Because you are changing _result inside a FOR loop, you need to access it using ! instead of %.
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET "_result="
FOR /f "delims=" %%i IN ('dir "%~1\*.txt" /b') DO (
ECHO.filepath: "%~1\%%i"
SET _result=%%~ni
ECHO._result: "!_result!"
)
Related
I wrote this code based on some other examples but just can't get it to work? (it's a .bat file)? The code writes the new file with all the old lines just won't edit the three lines right with the "=" character. Can someone point me in the right direction please.
This is what the INTOUCH.INI file looks like to start:
[InTouch]
AppMode=2
AppName0=test
AppName1=
AppName2=
AppName3=
AppDesc0=New InTouch application
AppDesc1=
AppDesc2=
AppDesc3=
SAOConverted=1
WinFullScreen=1
WinLeft=-4
WinTop=-4
WinWidth=1032
WinHeight=748
UseNewSendKeys=1
DebugScripts=0
UseBigBitmap=1
WindowViewerStartupIconic=0
CloseOnTransfer=0
And this is what is written:
[InTouch]
AppMode=2
AppName0=test
AppName1=
AppName2=
AppName3=
AppDesc0=New InTouch application
AppDesc1=
AppDesc2=
AppDesc3=
SAOConverted=1
1=WinFullScreen=0=1
WinLeft=-4
WinTop=-4
1032=WinWidth=1000=1032
748=WinHeight=700=748
UseNewSendKeys=1
DebugScripts=0
UseBigBitmap=1
WindowViewerStartupIconic=0
CloseOnTransfer=0
This is my .bat file code:
Set "OldString1=WinFullScreen=1"
Set "NewString1=WinFullScreen=0"
Set "OldString2=WinWidth=1032"
Set "NewString2=WinWidth=1000"
Set "OldString3=WinHeight=748"
Set "NewString3=WinHeight=700"
#ECHO OFF &SETLOCAL
cd /d F:\
for %%x in (INTOUCH.INI) do call:process "%%~x"
goto:eof
:process
set "outFile=%~n1_edited%~x1"
(for /f "skip=2 delims=:" %%a in ('find /n /v "" "INTOUCH.INI"') do (
set "ln=%%a"
Setlocal enableDelayedExpansion
set "ln=!ln:*]=!"
if defined ln (
set "ln=!ln:%OldString1%=%NewString1%!"
set "ln=!ln:%OldString2%=%NewString2%!"
set "ln=!ln:%OldString3%=%NewString3%!"
)
echo(!ln!
endlocal
))>"%outFile%"
Exit /b
If you use the file's format to your advantage you can set the values of the new variables at the top of the script and then as you are reading the variable names from the settings file you can see if those variables are defined. If they are defined then output the new value, otherwise output the original value.
The trick to this is the double variable expansion you get when you use the CALL and ECHO commands together. First the for variable is expanded the name of the variable and then in the second phase of expansion the value of the variable is then expanded. That is the reason for the extra sets of percent symbols.
#echo off
Set "WinFullScreen=0"
Set "WinWidth=1000"
Set "WinHeight=700"
REM cd /d F:\
for %%F in (INTOUCH.INI) do set "outFile=%%~nF_edited%%~xF"
REM Read first line of file
set /p line1=<INTOUCH.INI
(echo %line1%
for /f "usebackq skip=1 tokens=1,2 delims==" %%G in ("INTOUCH.INI") do (
if defined %%G (
CALL echo %%G=%%%%G%%
) else (
echo %%G=%%H
)
))>"%outFile%"
Exit /b
#echo off
Set "AppMode=x"
Set "WinFullScreen=0"
Set "WinWidth=1000"
Set "WinHeight=700"
for /f "skip=1 usebackq tokens=1,2 delims==" %%G in ("a.INI") do call :proc "%%G" %%H
exit /b
:proc
set val=%2
for /F "tokens=* eol= " %%S in ("%~1") do set trimmed=%%S
call :getoverrideval %trimmed%
if "%override%" == "" (
echo %~1=%2%
) else (
echo %~1=%override%
)
goto :EOF
:getoverrideval
call set override=%%%1%%
Output:
C:\Users\w16coreeval>cmd /c a.bat
AppMode=x
AppName0=test
AppName1=
AppName2=
AppName3=
AppDesc0=New
AppDesc1=
AppDesc2=
AppDesc3=
SAOConverted=1
WinFullScreen=0
WinLeft=-4
WinTop=-4
WinWidth=1000
WinHeight=700
UseNewSendKeys=1
DebugScripts=0
UseBigBitmap=1
WindowViewerStartupIconic=0
CloseOnTransfer=0
So in Windows Explorer, I have these files sorted like this:
I have this script to remove the brackets and one zero, and in case the trailing number is greater than or equal to 10, to remove two zeroes:
cd C:\folder
setlocal enabledelayedexpansion
SET /A COUNT=0
for %%a in (*.jpg) do (
SET /A COUNT+=1
ECHO !COUNT!
set f=%%a
IF !COUNT! GTR 9 (
set f=!f:^00 (=!
) ELSE (
set f=!f:^0 (=!
)
set f=!f:^)=!
ren "%%a" "!f!"
)
pause
However, once I run the code, I get this result:
So the batch file isn't going through the files "intuitively" like Windows Explorer shows them. Is there any way to change this? Or is there a better way to rename these files altogether?
This uses a different approach:
#echo off
cd C:\folder
setlocal enabledelayedexpansion
SET /A COUNT=0, REMOVE=2
for /F "delims=(" %%a in ('dir /B *.jpg') do (
SET /A COUNT+=1
ECHO !COUNT!
set "f=%%a"
IF !COUNT! EQU 10 SET "REMOVE=3"
for /F %%r in ("!REMOVE!") do set "f=!f:~0,-%%r!"
ren "%%a" "!f!!COUNT!.jpg"
)
pause
Here is a method that does not depend on the sort order used by the file system, preserving the numbers as occurring in the original file names.
For each file name (for instance, test_this_01 SELR_Opening_00000 (1).jpg), the portion after the last under-score _ is retrieved (00000 (1)). Then the parentheses and the space are removed and then the length is trimmed to five characters (00001). This string replaces the original one in the file name finally (test_this_01 SELR_Opening_00001.jpg); the file name must not contain the replaced portion (00000 (1)) multiple times (hence file names like this should not occur: test_this_00000 (1) SELR_Opening_00000 (1).jpg):
#echo off
setlocal DisableDelayedExpansion
rem // Define constants here:
set "LOCATION=."
set "PATTERN=*_* (*).jpg"
set /A "DIGITS=5"
pushd "%LOCATION%" || exit /B 1
for /F "usebackq eol=| delims=" %%F in (`
dir /B /A:-D /O:D /T:C "%PATTERN%"
`) do (
set "FILE=%%F"
setlocal EnableDelayedExpansion
set "LAST="
for %%I in ("!FILE:_=","!") do (
set "LAST=%%~nI" & set "FEXT=%%~xI"
set "FNEW=!FILE:%%~I=!"
)
set "LAST=!LAST:(=!" & set "LAST=!LAST:)=!"
set "LAST=!LAST: =!" & set "LAST=!LAST:~-5!"
ECHO ren "!FILE!" "!FNEW!!LAST!!FEXT!"
endlocal
)
popd
endlocal
exit /B
Adapt the directory location and the file search pattern in the top section of the script as you like.
After having tested, remove the upper-case ECHO command in order to actually rename files.
I'm trying to set the value of a variable that is inside a SETLOCAL, inside a FOR loop and inside an IF statement. However, it never seems to work. I tried using a SET statement at the ENDLOCAL statement, but that doesn't seem to actually set anything. Echoing the variable after that only echos the original set value of 0.
#ECHO off
SET pathsource=K:
SET sourcefile=0
SET counter=0
SETLOCAL enableextensions enabledelayedexpansion
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
FOR /f "tokens=1-3,5*" %%a IN ('dir ^"%pathsource%\*.pptx^" /a-d-h-s /o-d /tw ^| find /i ^".pptx^"') DO (
REM echo !counter!
REM only get the first row by using a counter
IF !counter! EQU 0 (
REM variables are: a-date, b-time, c-am/pm, d&e-filename
ECHO %%a %%b %%c %%d %%e
SET sourcefile=%%d %%e
)
SET /A counter+=1
)
ENDLOCAL & (
SET "sourcefile=%sourcefile%"
)
ECHO %sourcefile%
REM do other stuff with the %sourcefile% variable after this
Because you assign the values within the Set/End Local block, any changes made within this are discarded once on the ENDLOCAL command is reached.
Just move your SETLOCAL and ENDLOCAL commands to the very top and bottom of your script respectively. This would make all assignments made within the entirety of script stick throughout.
Additionally, you don't really need the counter variable. You can just jump out of the loop after the first file is processed:
#ECHO off
REM delayed expansion not needed for the portion shown
SETLOCAL enableextensions
SET pathsource=K:
SET sourcefile=0
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
FOR /f "tokens=1-3,5*" %%a IN ('dir ^"%pathsource%\*.pptx^" /a-d-h-s /o-d /tw ^| find /i ^".pptx^"') DO (
REM variables are: a-date, b-time, c-am/pm, d&e-filename
ECHO %%a %%b %%c %%d %%e
SET sourcefile=%%d %%e
REM We only care about the first row
GOTO EndLoop
)
:EndLoop
ECHO %sourcefile%
REM do other stuff with the %sourcefile% variable after this
ENDLOCAL
One last thing is you can simply your FOR syntax a bit by using native DIR and FOR variable commands:
#ECHO off
REM delayed expansion not needed for the portion shown
SETLOCAL enableextensions
SET pathsource=K:
SET sourcefile=0
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
CD "%pathsource%\"
FOR /f "usebackq tokens=* delims=" %%a IN (`dir "%pathsource%\*.pptx" /a-d-h-s /o-d /tw /b`) DO (
REM Use For loop variables.
REM Print the date/time and file name.
ECHO %%~ta %%~a
SET sourcefile=%%~a
REM We only care about the first row
GOTO EndLoop
)
:EndLoop
ECHO %sourcefile%
REM do other stuff with the %sourcefile% variable after this
ENDLOCAL
If all you want is the name and timestamp of the newest PPTX file, your script is a lot more complicated than it needs to be. Simply scrape dir "path\*.pptx" /b /o:-d then break out of the for /f loop after the first line.
#ECHO off
setlocal
SET "pathsource=K:"
REM Get the newest pptx file in the specified directory. Get the filename and last modified timestamp
pushd "%pathsource%"
FOR /f "delims=" %%a IN ('dir "*.pptx" /b /o:-d') DO (
set "sourcefile=%%~nxa"
set "timestamp=%%~ta"
goto break
)
:break
ECHO %sourcefile%
echo %timestamp%
REM do other stuff with the %sourcefile% variable after this
I made this code
dir /B /S %RepToRead% > %FileName%
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
echo %%a is working fine but echo %z% returns "echo disabled".
I need to set a %z% because I want to split the variable like %z:~7%
Any ideas?
There are two methods to setting and using variables within for loops and parentheses scope.
setlocal enabledelayedexpansion see setlocal /? for help. This only works on XP/2000 or newer versions of Windows.
then use !variable! instead of %variable% inside the loop...
Create a batch function using batch goto labels :Label.
Example:
for /F "tokens=*" %%a in ('type %FileName%') do call :Foo %%a
goto End
:Foo
set z=%1
echo %z%
echo %1
goto :eof
:End
Batch functions are very useful mechanism.
You probably want SETLOCAL ENABLEDELAYEDEXPANSION. See https://devblogs.microsoft.com/oldnewthing/20060823-00/?p=29993 for details.
Basically: Normal %variables% are expanded right aftercmd.exe reads the command. In your case the "command" is the whole
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo %z%
echo %%a
)
loop. At that point z has no value yet, so echo %z% turns into echo. Then the loop is executed and z is set, but its value isn't used anymore.
SETLOCAL ENABLEDELAYEDEXPANSION enables an additional syntax, !variable!. This also expands variables but it only does so right before each (sub-)command is executed.
SETLOCAL ENABLEDELAYEDEXPANSION
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
This gives you the current value of z each time the echo runs.
I struggeld for many hours on this.
This is my loop to register command line vars.
Example : Register.bat /param1:value1 /param2:value2
What is does, is loop all the commandline params,
and that set the variable with the proper name to the value.
After that, you can just use
set value=!param1!
set value2=!param2!
regardless the sequence the params are given. (so called named parameters).
Note the !<>!, instead of the %<>%.
SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%P IN (%*) DO (
call :processParam %%P
)
goto:End
:processParam [%1 - param]
#echo "processparam : %1"
FOR /F "tokens=1,2 delims=:" %%G IN ("%1") DO (
#echo a,b %%G %%H
set nameWithSlash=%%G
set name=!nameWithSlash:~1!
#echo n=!name!
set value=%%H
set !name!=!value!
)
goto :eof
:End
Simple example of batch code using %var%, !var!, and %%.
In this example code, focus here is that we want to capture a start time using the built in variable TIME (using time because it always changes automatically):
Code:
#echo off
setlocal enabledelayedexpansion
SET "SERVICES_LIST=MMS ARSM MMS2"
SET START=%TIME%
SET "LAST_SERVICE="
for %%A in (%SERVICES_LIST%) do (
SET START=!TIME!
CALL :SOME_FUNCTION %%A
SET "LAST_SERVICE=%%A"
ping -n 5 127.0.0.1 > NUL
SET OTHER=!START!
if !OTHER! EQU !START! (
echo !OTHER! is equal to !START! as expected
) ELSE (
echo NOTHING
)
)
ECHO Last service run was %LAST_SERVICE%
:: Function declared like this
:SOME_FUNCTION
echo Running: %1
EXIT /B 0
Comments on code:
Use enabledelayedexpansion
The first three SET lines are typical
uses of the SET command, use this most of the time.
The next line is a for loop, must use %%A for iteration, then %%B if a loop inside it
etc.. You can not use long variable names.
To access a changed variable such as the time variable, you must use !! or set with !! (have enableddelayexpansion enabled).
When looping in for loop each iteration is accessed as the %%A variable.
The code in the for loop is point out the various ways to set a variable. Looking at 'SET OTHER=!START!', if you were to change to SET OTHER=%START% you will see why !! is needed. (hint: you will see NOTHING) output.
In short !! is more likely needed inside of loops, %var% in general, %% always a for loop.
Further reading
Use the following links to determine why in more detail:
Difference between %variable% and !variable! in batch file
Variable usage in batch file
To expand on the answer I came here to get a better understanding so I wrote this that can explain it and helped me too.
It has the setlocal DisableDelayedExpansion in there so you can locally set this as you wish between the setlocal EnableDelayedExpansion and it.
#echo off
title %~nx0
for /f "tokens=*" %%A in ("Some Thing") do (
setlocal EnableDelayedExpansion
set z=%%A
echo !z! Echoing the assigned variable in setlocal scope.
echo %%A Echoing the variable in local scope.
setlocal DisableDelayedExpansion
echo !z! &rem !z! Neither of these now work, which makes sense.
echo %z% &rem ECHO is off. Neither of these now work, which makes sense.
echo %%A Echoing the variable in its local scope, will always work.
)
set list = a1-2019 a3-2018 a4-2017
setlocal enabledelayedexpansion
set backup=
set bb1=
for /d %%d in (%list%) do (
set td=%%d
set x=!td!
set y=!td!
set y=!y:~-4!
if !y! gtr !bb1! (
set bb1=!y!
set backup=!x!
)
)
rem: backup will be 2019
echo %backup%
Try this:
setlocal EnableDelayedExpansion
...
for /F "tokens=*" %%a in ('type %FileName%') do (
set z=%%a
echo !z!
echo %%a
)
You can use a macro if you access a variable outside the scope
#echo off
::Define macro
set "sset=set"
for /l %%a in (1,1,4) do (
::set in loop
%sset% /a "x[%%a]=%%a*%%a"
if %%a equ 4 (
:: set in condition
%sset% "x[%%a]=x Condition"
%sset% "y=y Condition"
)
)
echo x1=%x[1]% x2=%x[2]% x3=%x[3]% x4=%x[4]% y=%y%
:: Bonus. enableDelayedExpansion used to access massive from the loop
setlocal enableDelayedExpansion
echo Echo from the loop
for /l %%a in (1,1,4) do (
::echo in one line - echo|set /p =
echo|set /p "=x%%a=!x[%%a]! "
if %%a equ 4 echo y=%y%
)
pause
I know this isn't what's asked but I benefited from this method, when trying to set a variable within a "loop". Uses an array. Alternative implementation option.
SETLOCAL ENABLEDELAYEDEXPANSION
...
set Services[0]=SERVICE1
set Services[1]=SERVICE2
set Services[2]=SERVICE3
set "i=0"
:ServicesLoop
if defined Services[%i%] (
set SERVICE=!Services[%i%]!
echo CurrentService: !SERVICE!
set /a "i+=1"
GOTO :ServicesLoop
)
The following should work:
setlocal EnableDelayedExpansion
for /F "tokens=*" %%a in ('type %FileName%') do (
set "z=%%a"
echo %z%
echo %%a
)
i am trying to get files from a directory and want to set names of the file to a variable using batch script.
this is my code .but it always setting same value to variable
can any body give solution
echo on
setlocal EnableDelayedExpansion
for /f %%x in ('dir /b C:\backup_dir') do (
SET test=%%~nx
if "%test:~0,6%"=="kdc_db" (set DUMP=%%x)
if "%test:~0,6%"=="kdc_ke" (set KEYS=%%x)
)
echo %DUMP%
echo %KEYS%
here dump and keys variables are always set to same value
You need to use delayed expansion. You have already enabled it, you just need to replace your %'s with !'s
echo on
setlocal EnableDelayedExpansion
for /f %%x in ('dir /b C:\backup_dir') do (
SET test=%%~nx
if "!test:~0,6!"=="kdc_db" (set DUMP=%%x)
if "!test:~0,6!"=="kdc_ke" (set KEYS=%%x)
)
echo %DUMP%
echo %KEYS%