I am trying to collect first file from the directory then process the file. But at the second time when the running and processing the batch file I am unable to store the values in the variable for the file name
Below is the sample code:
for /R C:\abcde_efghij\ab_abcabca %%i IN (*.*) DO (
set filename=%%i
set newname=%filename:~14%
set transname=%filename:~25%
goto tests
)
:tests
echo %filename%
echo %newname%
echo %transname%
I am sure we have to use something called SETLOCAL but I am unable to make it in the above code.
Any Help!
You should avoid percent expansion inside of blocks, also FOR blocks, as the expansion only occours only once when the block is parsed.
for /R C:\abcde_efghij\ab_abcabca %%i IN (*.*) DO (
set filename=%%i
goto :tests # Get only the first file
)
exit /b
:tests
set newname=%filename:~14%
set transname=%filename:~25%
echo %filename%
echo %newname%
echo %transname%
exit /b
As #Stephan noted, you could also use delayed expansion inside blocks.
setlocal EnableDelayedExpansion
for /R C:\abcde_efghij\ab_abcabca %%i IN (*.*) DO (
set filename=%%i
set newname=!filename:~14!
set transname=!filename:~25!
goto :tests # Get only the first file
)
Related
I made this script that finds all directories and echoes the directorie's name to a .txt file. The script is working but it ends up echoing only %A without any value. My script is below!
set /a count=0
setlocal EnableDelayedExtensions
FOR /D %%A in ("*") DO (call :sub)
endlocal
pause
exit
:sub
(echo [DIR] %%A)>>%count%.txt
set /a count+=1
The output in the .txt files is [DIR] %A.
Any idea how to fixe this? Thanks -David
First remark, you are using an invalid option for setlocal but that is probably just a typo.
The problem is that you are try to use a for-parameter where it cannot be used.
The rule is "A for-parameter can only be used within the command or command block () of a for loop"
Your subroutine is not within the command block of a for loop, but you can start a dummy for loop in the subroutine which will give you access to all available for-parameters.
set /a count=0
setlocal EnableDelayedExpansion
FOR /D %%A in ("*") DO (call :sub)
endlocal
pause
exit/b
:sub
For %%. in (.) do (echo [DIR] %%A)>>%count%.txt
set /a count+=1
You need to pass the parameter to the subroutine.
From https://www.informit.com/articles/article.aspx?p=1154761&seqNum=11 :
for %%f in (*.dat) do call :onefile %%f
exit /b
:onefile
echo Processing file %1...
echo ... commands go here ...
exit /b
As you've already enabled delayed expansion, there's no need to use a Call to a label, just do it within the loop.
#Echo Off
SetLocal EnableExtensions EnableDelayedExpansion
Set "count=0"
For /D %%G In (*) Do (
Set /A count += 1
(Echo [DIR] %%G) 1>"!count!.txt"
)
On this version, I've started at 1 instead of 0 for the first text file name, if you really want to start at 0, change line 3 to Set "count=-1"
I have a folder structure, which is like for example C:\Temp\ and there are a lot of folder and file, and within each folder there are a "callme.bat". I would like to create a so called main.bat which is one after another call the callme files within the main' window. But there is a problem, within the callme files are some echo which contains "!" mark what make a problem for me.
I realized the problem with the setlocal-endlocal combo, because the batch scrip wants to interpret the message within the "!" marks, so I must use endlocal, but if I did I not able to run the callme bats.
callme.bat
#echo off
echo !!! hidden message !!! not hidden message
pause
main.bat variant 1
#echo off
setlocal enabledelayedexpansion
set PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
set CURR_DIR=%PATH%\%%x
set ACTUAL_BATCH=!CURR_DIR!\callme.bat
echo !ACTUAL_BATCH!
call !ACTUAL_BATCH!
pause
)
pause
exit
main.bat variant 2
#echo off
set PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
setlocal enabledelayedexpansion
set CURR_DIR=%PATH%\%%x
set ACTUAL_BATCH=!CURR_DIR!\callme.bat
echo !ACTUAL_BATCH!
ENDLOCAL & SET VAR=!ACTUAL_BATCH!
echo %VAR%
pause
)
pause
exit
main.bat variant 3
#echo off
set PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
setlocal enabledelayedexpansion
set CURR_DIR=%PATH%\%%x
set ACTUAL_BATCH=!CURR_DIR!\callme.bat
echo !ACTUAL_BATCH!
REM source: https://stackoverflow.com/questions/3262287/make-an-environment-variable-survive-endlocal
for /f "delims=" %%A in (""!ACTUAL_BATCH!"") do endlocal & set "VAR=%%~A"
echo %VAR%
call %VAR%
pause
)
pause
exit
So I don't know what to do. Anyone has an idea?
variant 1's output:
C:\Temp\1\callme.bat
not hidden message
C:\Temp\2\callme.bat
not hidden message
variant 2-3's output:
C:\Temp\1\callme.bat
ECHO is off.
C:\Temp\2\callme.bat
ECHO is off.
TL;DR
ENDLOCAL&set "varname=%sourcevarname%"
probably, where varname is the variablename to set and sourcevarname is the variable whose value is to be assigned to varname - and they CAN be the same name, even if the statement appears logically null - it's exporting the variable from within the setlocal/endlocal block.
Key point: MUST be on one physical line and may be repeated if necessary (ie
ENDLOCAL&set "varname=%sourcevarname%"&set "varname2=%sourcevarname2%"
So
ENDLOCAL&set "fred=%fred%"&set "bill=%george%"
is perfectly valid, to set the value of fred outside the setlocal/endlocal bracket to its final value inside and of billoutside to the final value of george inside.
Some points about your code:
Never use PATH as a variable name, as it destroys the PATH variable for searching executable files.
Use the extended SET syntax set "varname=content" to avoid problems with trainling spaces.
You only need to disable the delayed expansion mode by using setlocal DisableDelayedExpansion
#echo off
setlocal EnableDelayedExpansion
set MY_PATH=C:\Temp
for /F %%x in ('dir /B/A:D %PATH%') do (
set "CURR_DIR=%MY_PATH%\%%x"
set "ACTUAL_BATCH=!CURR_DIR!\callme.bat"
call :execute ACTUAL_BATCH
pause
)
pause
exit /b
:execute ACTUAL_BATCH
set "batFile=!%~1!"
echo Calling !batFile!
setlocal DisableDelayedExpansion
call %batFile%
endlocal
exit /b
I am trying to write a batch script that does the following:
When a folder is drag-and-dropped onto the batch script, it processes every file in that folder.
I am running into a problem with a certain filenames that contain exclamation marks. e.g.:
!.txt or !!!.txt
For now, I am simply trying to rename the file to demonstrate the issue:
#echo off
SetLocal EnableDelayedExpansion
set folder=%~1
set count=0
for /r "%folder%" %%G in (*) do (
set fullpath=%%G
set fileExtension=%%~xG
call :processFile
)
goto end
:processFile
echo "fullpath = %fullpath%"
echo "fileExtension = %fileExtension%"
rename "%fullpath%" "temporary_filename_500%fileExtension%"
set /a count+=1
echo.
goto :eof
:end
echo "%count% files processed."
pause
It gives me the error "The system cannot find the file specified." However, it works if I change the filename to something simple like "test.webm" How can I make the script more robust?
I don't see that you are using somewhere delayed expansion. So, either disable it with setlocal DisableDelayedExpansion in the start of your batch file or just remove it by removing line setlocal EnableDelayedExpansion.
However, if you want to keep it, do:
#echo off
SetLocal EnableDelayedExpansion
rem Code above (^^) if exists.
Setlocal DisableDelayedExpansion
set "folder=%~1"
set "count=0"
for /R "%folder%" %%G in (*) do (
set "fullpath=%%~fG"
set "fileExtension=%%~xG"
call :processFile
)
goto end
:processFile
echo "fullpath = %fullpath%"
echo "fileExtension = %fileExtension%"
ren "%fullpath%" "temporary_filename_500%fileExtension%"
set /a "count+=1"
echo/
goto :eof
:end
echo "%count% files processed."
pause
setlocal EnableDelayedExpansion
rem Your code below with active delayed expansion:
Note that:
You should always quote the variable name and the value in the set command like set "var=value" and in set /a like set /a "var+=1", etc.; see set /? for more information.
To find the full path of a file/folder for sure in a for loop, use the f modifier, like %%~fG.
Mentioned by Mofi here: don't use echo.; use echo/ for better practice.
See also the Phase 5 (Delayed Expansion) of this answer about how batch files are interpreted.
I'm trying to fill X files with chunks of content from a .txt file.
The thing is the .txt (source) file is small and I want my code to loop over it's content chunk by chunk and start again when no more chunks are avaliable until all files have been filled.
This is part of the (still on development) code. I've managed to teach my Batch.bat to recognized the chunks but I don't know how to check if I am at EOF to go back again to the begining.
Current code doesn't work since it has been simplified for the question (delayed expansion not present)
SET /A J=1
FOR /F "skip=%J% tokens=*" %%A IN (%~1%) DO (
CALL :CSV "%%A" %J%
)
EXIT /B
:CSV
SETLOCAL
IF NOT "%~1"=="---" (
ECHO %~1 >> Chistaco.txt
)
ENDLOCAL && SET /A J=%2+1
Code posted here is just a guide of my target, don't miss the question.
Thank you !
EDIT: Simplified Code Module
#ECHO OFF
SETLOCAL
REM Ask for parameters
SET /P filler=Enter the name of the file to use as filler (file.ext):
SET /P seed=Enter the seed of the files to fill:
CLS && ECHO %~1%
REM The variable "J" counts the lines to jump in "file.ext" (and btw, it cannot be 0 as the batch parses fails to read it)
REM This is where the EOF check is needed: "J" must be referenced outside the main loop so the script can loop inside "file.ext" untill all "seed???" files have been filled
SET /A J=1
FOR /R "%path%" %%X IN (%seed%*) DO ECHO %%X && CALL :FillerChomper "%filler%" "%%X"
ENDLOCAL
EXIT /B
:FillerChomper
SETLOCAL
FOR /F "skip=%J% tokens=*" %%A IN (%~1%) DO (
CALL :CSV "%%A" J %2%
IF "%%A"=="---" EXIT /B
)
EXIT /B
:CSV
SETLOCAL
IF NOT %1=="---" ECHO %~1 >> %~3%
ENDLOCAL && SET /A %2=%J%+1
EXIT /B
REM All functions must have a "EXIT /B" command or batch will just keep executing codelines until :EOF
Thank you guys for your help!
i am writing a batch script monotonic file renamer. basically, it makes the titles of all the files 1 2 3 4 .... and so on. i have since expanded it to be able to handle files of different types (txt, doc, flv, etc) but not everything is working out.
my main concern is i have broken the delayed expansion calls i was making before. now using !var1! is never expanded, or never recognized as a variable.
here is a verbosely commented version of my script
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET tempfile=temp.txt
SET exttemp=exttemp.txt
if [%1] == [] goto usage
::make sure your dont overwrite something useful
if EXIST %tempfile% (
ECHO Temp file already exists, are you sure you want to delete?
del /P %tempfile%
)
if EXIST %exttemp% (
ECHO EXT Temp file already exists, are you sure you want to delete?
del /P %exttemp%
)
::initialize
SET /a counter=0
SET type=
SET /a ender=%1
::write filenames to tempfile
DIR /B /ON > %tempfile%
::read lines one by one
for /f "usebackq delims=" %%a in (%tempfile%) do (
REM make sure we do not rename any of the working files
if NOT "%%a"=="renamer.bat" (
if NOT "%%a"=="temp.txt" (
if NOT "%%a"=="exttostr.bat" (
SET /a counter+=1
REM get file extension
exttostr %%a > %exttemp%
SET /P type= < %exttemp%
REM housekeeping
del /F %exttemp%
REM rename
ren %%a !counter!.!type!
ECHO Renamed "%%a" to "!counter!.!type!"
)))
REM exit when we have run enough
if "!counter!"=="!ender!" goto exit
)
goto exit
:usage
echo Usage: renamer NUMFILES
:exit
::final housekeeping
DEL temp.txt
the idea is i drop my two files, renamer.bat(this file) and exttostr.bat(helper to get the file extension) into the folder and run it, it will rename files sorted alphabetically from 1 to how ever many files i specify.
when i run the code, it never uses the variables marked for delayed expansion appropriately, always leaving them as "!varname!", so it renames the first file "!counter!.!type!" and throws errors for the rest because there is already a file in the directory with that name.
this brings me to a secondary issue. sorting the dir list alphabetically results in a poor handling of numbered files. for example the list:
"1 7 15 75 120"
is sorted:
"1 120 15 7 75"
i have not been able to find a way around this yet, only that it is indeed the intended result of the dir sort. the only workaround i have is padding numbers with enough zeroes in the front.
thanks in advance for any insight!
everything is sorted but the second problem. i think i have not spoken well. i have this issue when i take IN the directory file names, not when writing out. so they already need to be padded. i has hoping there was some other way to read the directory and have it be sorted appropriately.
the most promising thing i have found is here: http://www.dostips.com/DtCodeBatchFiles.php#Batch.SortTextWithNumbers
#ECHO OFF
if "%~1"=="/?" (
echo.Sorts text by handling first number in line as number not text
echo.
echo.%~n0 [n]
echo.
echo. n Specifies the character number, n, to
echo. begin each comparison. 3 indicates that
echo. each comparison should begin at the 3rd
echo. character in each line. Lines with fewer
echo. than n characters collate before other lines.
echo. By default comparisons start at the first
echo. character in each line.
echo.
echo.Description:
echo. 'abc10def3' is bigger than 'abc9def4' because
echo. first number in first string is 10
echo. first number in second string is 9
echo. whereas normal text compare returns
echo. 'abc10def3' smaller than 'abc9def4'
echo.
echo.Example:
echo. To sort a directory pipe the output of the dir
echo. command into %~n0 like this:
echo. dir /b^|%~n0
echo.
echo.Source: http://www.dostips.com
goto:EOF
)
if "%~1" NEQ "~" (
for /f "tokens=1,* delims=," %%a in ('"%~f0 ~ %*|sort"') do echo.%%b
goto:EOF
)
SETLOCAL ENABLEDELAYEDEXPANSION
set /a n=%~2+0
for /f "tokens=1,* delims=]" %%A in ('"find /n /v """') do (
set f=,%%B
(
set f0=!f:~0,%n%!
set f0=!f0:~1!
rem call call set f=,%%%%f:*%%f0%%=%%%%
set f=,!f:~%n%!
)
for /f "delims=1234567890" %%b in ("!f!") do (
set f1=%%b
set f1=!f1:~1!
call set f=0%%f:*%%b=%%
)
for /f "delims=abcdefghijklmnopqrstuwwxyzABCDEFGHIJKLMNOPQRSTUWWXYZ~`##$*_-+=:;',.?/\ " %%b in ("!f!") do (
set f2=00000000000000000000%%b
set f2=!f2:~-20!
call set f=%%f:*%%b=%%
)
echo.!f1!!f2!!f!,%%B
rem echo.-!f0!*!f1!*!f2!*!f!*%%a>&2
)
this code can sort the filenames with one number in them (i.e. video100.mov is fine, video100video10.mov would break it)
the issue i have is i think adding a call to this helper fn will break it again, so i will be trying to include this in my modified renamer.bat now. any help is appreciated.
Probably the batch for extracting the extension reset the local environment.
But, you don't need it. You may extract the extension with the ~x option. Something similar to this ....
:monotonicrename
set /a counter = 0
for %%a in (%1\*.*) do (
if exist %%~fa (
set /a counter += 1
echo ren %%~fa !counter!%%~xa
)
)
goto :eof
to include leading zeroes in the counter, so that the directory sorts correctly, replace the previous rename command with three lines
set zcounter=0000!counter!
set zcounter=!zcounter:~-4!
echo ren %%~fa !counter!%%~xa
So putting all pieces together, add the monotonicrename function you just created in the batch file that can be as simpler as...
#echo off
setlocal enabledelayedexpansion
call :monotonicrename %1
goto :eof
:monotonicrename
set /a counter = 0
for %%a in (%1\*.*) do (
if exist %%~fa (
set /a counter += 1
set zcounter=0000!counter!
set zcounter=!zcounter:~-4!
echo ren %%~fa !zcounter!%%~xa
)
)
goto :eof
I didn't experience any issues with delayed expansion, everything worked fine for me (except, of course, for the fact that I didn't have the exttostr.bat helper script.)
Anyway, there are several things that could be improved about your script:
You don't need to store the result of DIR into a file to read it afterwards. You can read the output directly in the FOR loop.
You don't need the helper batch script. The extension can be extracted from %%a by using the ~x modifier with the loop variable: %%~xa. You can read more about modifiers by issuing HELP FOR from the command prompt.
The renamer batch file's own name can be referenced in the script as %0. You can apply the ~n modifier where you only need to use the name without the extension. The combined modifier of ~nx will give you the name with the extension.
So, here's how your script might look like with the above issues addressed:
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
IF [%1] == [] GOTO usage
::initialize
SET /A counter=0
SET type=
SET /A ender=%1
::read lines one by one
FOR /F "usebackq delims=" %%a IN (`DIR /B /ON`) DO (
REM make sure we do not rename any of the working files
IF NOT "%%~a"=="%~nx0" (
SET /A counter+=1
RENAME "%%~a" "!counter!%%~xa"
ECHO Renamed "%%~a" to "!counter!%%~xa"
)
REM exit when we have run enough
IF "!counter!"=="!ender!" GOTO :EOF
)
GOTO :EOF
:usage
ECHO Usage: %~n0 NUMFILES
As for your secondary issue, it can be easily resolved like this:
Use something like 100000 as counter's initial value. (Use however many 0s you like, but possibly no more than nine.) Add the same value to ender as well.
When renaming files, instead of !counter! use the expression that removes the first character (the 1): !counter:~1! (in fact, this is not about removal, but about extracting a substring starting from the offset of 1, learn more about it with the HELP SET command).
Here's the modified version of the above script:
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
IF [%1] == [] GOTO usage
::initialize
SET /A counter=1000
SET type=
SET /A ender=%1
SET /A ender+=counter
::read lines one by one
FOR /F "usebackq delims=" %%a IN (`DIR /B /ON`) DO (
REM make sure we do not rename any of the working files
IF NOT "%%~a"=="%~nx0" (
SET /A counter+=1
RENAME "%%~a" "!counter:~1!%%~xa"
ECHO Renamed "%%~a" to "!counter:~1!%%~xa"
)
REM exit when we have run enough
IF "!counter!"=="!ender!" GOTO :EOF
)
GOTO :EOF
:usage
ECHO Usage: renamer NUMFILES
You can also see that I made some other enhancements, like making sure the file name is enclosed in double quotes, and using GOTO :EOF instead of GOTO exit (:EOF is a special pre-defined label that points at the end of the batch script so you don't need to define your own).