This question already has answers here:
Arrays, linked lists and other data structures in cmd.exe (batch) script
(11 answers)
Closed 2 years ago.
I'm new to bat scripting and I wanted to use an iterative loop in my script (something like this in javascript for example)
for(var i=0;i<n;i++){
//my code here
console.log("my tab ["+i+"] is:"+tab[i];
}
So, basicaly this is my bat script in a file called exctract_excel_info.bat:
#ECHO OFF
::setlocal enabledelayedexpansion
:start
ECHO "Hi it's MIBE in this script i will list all the files with the extention xlsx or xls">log.txt
:start_loop
echo Listing all files in the current directory %cd% >log.txt
ECHO ================== ======================>>log.txt
set "AllowExt= *.xls"
ECHO the variable AllowExt in the first instruction %AllowExt%>>log.txt
set theFileName="NONE"
set /a i = 0
for %%a in (%AllowExt%) do (
set /a i = i + 1
::echo Found the file: "%%a">>log.txt
set theFileName[%i%]=%%a
)
:end_loop
ECHO ============================================>>log.txt
ECHO "The value of i is %i%">>log.txt
ECHO ============================================>>log.txt
for /L %%x in (1,1,%i%) do (
ECHO found the file %theFileName[%%x]%>>log.txt
)
ECHO ============================================>>log.txt
timeout /t 2
ECHO Yo the file %theFileName[0]% will be passed as a parameter
ECHO The array of files %theFileName%>>log.txt
:run_node
node main.js "%theFileName[0]%">>log.txt
timeout /t 2
:end
::PAUSE
I have a problem reading the value of the variable theFileName[i] in line 32
ECHO found the file %theFileName[%%x]%>>log.txt
This is the output (log.txt):
Listing all files in the current directory C:\Users\mibe\my bat
================== ======================
the variable AllowExt in the first instruction *.xls
============================================
"The value of i is 4"
============================================
So my problem is what is the proper way to read the value of the items inside theFileName array?
PS: when I comment the lines 31, 32, and 33:
for /L %%it in (1,1,"%i%") do (
ECHO found the file %theFileName[%%it]%>>log.txt
)
the script executes properly and get the value of %theFileName[0]%
The following is a basic example of what I think you're trying to do, up to a point.
The problem is that because you've not told us what exactly you're intending to do with those similarly named variables, I cannot include the specific methodology for doing so. I have therefore just output the defined variable names along side their value strings.
#SetLocal EnableExtensions DisableDelayedExpansion
#For /F "Delims==" %%G In ('"(Set File[) 2> NUL"') Do #Set "%%G="
#Set "i=0" & For /F Delims^= %%G In (
'"(Set PATHEXT=) & "%__APPDIR__%where.exe" ".:*.xlsx" ".:*.xls" ".:*.csv" 2> NUL | "%__AppDir__%sort.exe""'
) Do #(Set /A i += 1 & SetLocal EnableDelayedExpansion
For %%H In (!i!) Do #EndLocal & Set "File[%%H]=%%~nxG")
#If Defined File[1] For /L %%G In (1,1,%i%) Do #(SetLocal EnableDelayedExpansion
Echo File[%%G]=!File[%%G]! & EndLocal)
#Pause
The code above should define individual variables, each containing the string value of a file in the current directory, which has an extension of either .xlsx, .xls, or .csv.
When I first looked at your question I initially thought that you were intending to pass each file name together as multiple arguments to your node command.
If that is what you're actually intending to do, then I'd assume the following example would suit your purposes better.
#SetLocal EnableExtensions DisableDelayedExpansion
#Set "FileList="&For /F Delims^= %%G In (
'"(Set PATHEXT=) & "%__APPDIR__%where.exe" ".:*.xlsx" ".:*.xls" ".:*.csv" 2> NUL | "%__AppDir__%sort.exe""'
) Do #If Not Defined FileList (Set "FileList="%%~nxG"") Else (SetLocal EnableDelayedExpansion
For /F Delims^=^ EOL^= %%H In ("!FileList!") Do #EndLocal & Set "FileList=%%H "%%~nxG"")
#If Defined FileList Echo %%FileList%%=%FileList%
#Pause
The code above should define a single variable, containing the space separated, and doublequoted, string values, of each file in the current directory, which has an extension of either .xlsx, .xls, or .csv.
Please note that there is a command line length limitation so be aware that your value could become truncated.
In both examples:
The last line, #Pause, is included only to prevent premature closure of the cmd window, should you not be testing this from the CLI. (You can safely remove it if you are).
I assumed you wanted only the filenames without their paths, in the variable values. Should you wish for the full paths, just change %%~nxG in your chosen example code to %%G.
BTW, I used sort.exe against the returned files, because you mentioned array, and IMO an array should be ordered. (If you do not need that functionality, you could remove | "%__AppDir__%sort.exe" from your chosen example code).
Please note that there is a limit to the size of the environment, so be aware that if you have many matching files, they have long filenames, include paths etc. you may reach or exceed that limitation.
Having provided some examples for you, I'm not sure why you could not just iterate your directory, and create an array of those files, directly using .js, (off topic).
Related
Using windows batch file, I am trying to echo a list of file extensions in a folder.
That will then be set as %var% and output to .txt file
#setlocal & #(for %%I in (*.*) do #set /a ext[%%~xI] += 1) & set ext[
from this post Windows command to get list of file extensions
My end game is I would like it to output like so
folder contents:
sample1.jpg
sample2.jpg
sample3.jpg
sample4.mp4
sample5.mp4
sample6.png
sample7.zip
sample8.zip
result:
.jpg, .mp4, .png, .zip
Any help is greatly appreciated, I hope I explained it clear enough
You already have defined a variable for each extension. Just use another for /f loop to concatenate the relevant part (the actual extension) of them:
#echo off
setlocal enabledelayedexpansion
for %%I in (*) do #set /a ext[%%~xI] += 1
for /f "tokens=2 delims=[]" %%a in ('set ext[') do set "var=%%a, !var!"
echo %var:~0,-2%
#setlocal ENABLEDELAYEDEXPANSION&#(for %%I in (*.*) do #set /a ext[%%~xI] += 1&IF DEFINED ext[all] (IF "!ext[all]!" equ "!ext[all]: %%~xI=!" SET "ext[all]=!ext[all]!, %%~xI") ELSE (SET "ext[all]= %%~xI"))&SET "ext[all]=!ext[all]:~1!"&set ext[
Why you want this all on one line, I've no idea.
Since you need to access the changed value of a variable within a loop, you need delayedexpansion. Note that Space.ext is appended each time a new extension is found, so there will be an extra space at the start of the list variable - hence the requirement to remove the first character before displaying the result.
I'm an amateur on the usage of the FOR command. I need a batch file that will run one of 5 file conversion tools based on a file's extension. I want to drop a file onto the batch file icon and have it converted.
Since my list is huge, I can't use nested IF's.
What I've tried so far:
#ECHO OFF
SET cadfile=.dwg .dxf .dwf
SET gsfile=.ps .eps .epi .epsp
SET xxxxxx=.xx .xx and goes on
FOR %%~x1 in (%cadfile%) do (
Do some action
FOR %%~x1 in (%gsfile%) do (
Do some other action
)
)
The %%~x1 variable is used for file extension of file, which dragged and dropped over the batch file.
(edited to make more clear)
FOR %%a in (%cadfile%) do (
if /i "%~x1"=="%%a" some_action "%~1"
)
... and follow the bouncing ball for the rest of the utilities/lists
I think this will work for you. It looks through all your groups of extensions in a single For loop and when the matching extension is found, calls a label where you can do the conversion and any related tasks. You'll need to finish the "groupN" variables and labels.
#echo off
SETLOCAL EnableDelayedExpansion
set file="%1"
set ext=%~x1
:: Set the 5 groups of extensions that have different converters
set group1=.dwg, .dxf, .dwf
set group2=.ps, .eps, .epi, .epsp
For %%A in (1 2 3 4 5) do (
set groupnum=group%%A
call set thisgroup=%%!groupnum!%%
:: Look for extension in this group
echo.!thisgroup!|findstr /i /C:"%ext%" >nul 2>&1
if not errorlevel 1 call :group%%A
:: else go loop next group
)
echo Extension not found in any group &pause &goto end
:group1
echo group1 file to convert is %file%
goto end
:group2
echo group2 file to convert is %file%
goto end
:end
pause
exit
The following method allows you to easily add and modify your list of extensions/applications. Please note that you just need to edit the values placed inside the first FOR command; the rest of the program is the solution you don't need to care of...
#echo off
setlocal EnableDelayedExpansion
rem Define the list of extensions per application:
rem (this is the only part that you must edit)
for %%a in ("cadfile=.dwg .dxf .dwf"
"gsfile=.ps .eps .epi .epsp"
"xxxxxx=.xx .xx1 .xx2") do (
rem The rest of the code is commented just to be clear,
rem but you may omit the reading of this part if you wish
rem Separate application from its extensions
rem and create a vector called "ext" with an element for each pair
for /F "tokens=1,2 delims==" %%b in (%%a) do (
rem For example: %%b=cadfile, %%c=.dwg .dxf .dwf
for %%d in (%%c) do set "ext[%%d]=%%b"
rem For example: set "ext[.dwg]=cadfile", set "ext[.dxf]=cadfile", set "ext[.dwf]=cadfile"
rem In the next line: set "ext[.ps]=gsfile", set "ext[.eps]=gsfile", etc...
)
)
rem Now process the extension of the file given in the parameter:
if defined ext[%~x1] goto !ext[%~x1]!
echo There is no registered conversion tool for %~x1 extension
goto :EOF
:cadfile
echo Execute cadfile on %1 file
rem cadfile %1
goto :EOF
:gsfile
echo Execute gsfile on %1 file
rem gsfile %1
goto :EOF
etc...
If each conversion tool is executed in the same way and don't require additional parameters (just the filename), then you may omit the individual sections and directly execute the conversion tools this way:
if defined ext[%~x1] !ext[%~x1]! %1
For further explanations on array concept, see this post.
I've got some *.Xml files in a directory and its sub-directories. I need to loop through the XML files which have a specific constant at the end of their file name, and then echo/print their names without the constant part nor the extension (.Xml).
For example: these are the file names I have:
FileAAA_Constant.Xml
FileBBB.Xml
FileCCC.Xml
FileDDD_Constant.Xml
And this is the output I need:
FileAAA
FileDDD
I've tried this command:
For /R %%X in (*_Constant.Xml) do echo %%~nX
Which outputs this:
FileAAA_Constant
FileDDD_Constant
As you can see, it has removed the extension only, while I need to remove "_Constant.Xml" as well.
This works if the file names contains only one underscore, as indicated in your example:
for /F "delims=_" %%X in ('dir /S /B *_Constant.Xml') do echo %%X
If the desired file names may contain more than one underscore, use Pokechu22's answer.
Fairly easy if the exact length of the phrase is known; you just need to use the %var:~0,-3% syntax. Since "_Constant" is 9 chars long, you would want %var:~0,-9%, which takes text from the start (0) to 9 chars from the end (-9). Aditionally, delayed variable expansion also must be enabled with setlocal ENABLEDELAYEDEXPANSION for this to be run inside of your For loop.
Here's a full example:
#Echo off
setlocal ENABLEDELAYEDEXPANSION
for /R %%X in (*_Constant.Xml) do (
set FileNameTemp=%%~nX
echo !FileNameTemp:~0,-9!
)
Note that if you have a file named just _Constant.xml, this will produce "ECHO is off." rather than "" (no output). This can be solved by changing echo !FileNameTemp:~0,-9! to echo. !FileNameTemp:~0,-9!, but that puts a space before each output.
Here's the loop alone:
for /R %%X in (*_Constant.Xml) do (
set FileNameTemp=%%~nX
echo !FileNameTemp:~0,-9!
)
I want to define a big array (>400 keys) in batch, but when I execute my script the windows close. I use this setting:
set FILE_LIST=(filename1.xxx [...] filename450.yyy)
Some help? Thx
A Windows Batch file have a limit in the value of each variable to 8192 characters, including the name of the variable and the equal sign. If the value of each "filename#.xxx " have 16 characters, you may store up to 8192/16=512 file names in one variable; to do that, you must use Batch commands. For example:
#echo off
setlocal EnableDelayedExpansion
set "FILE_LIST="
for /L %%i in (1,1,450) do set "FILE_LIST=!FILE_LIST!filename%%i.xxx "
echo FILE_LIST=%FILE_LIST%
Please, note that previous variable is a list, NOT and array. To define an array, use this method:
#echo off
setlocal EnableDelayedExpansion
for /L %%i in (1,1,450) do set "FILE_ARRAY[%%i]=filename%%i.xxx"
echo FILE_ARRAY:
set FILE_ARRAY
There is a limit of 64 MegaBytes for the total space occupied by all variables.
For a detailed description of arrays and other data structures in Batch files, see: Arrays, linked lists and other data structures in cmd.exe Batch script
EDIT: Reply to the comments
The Batch file below assume that there is one file name per line in the .txt file, and that file names does not include exclamation marks:
#echo off
setlocal EnableDelayedExpansion
rem Load the .txt file in FILE_ARRAY elements:
set num=0
for /F "delims=" %%a in (fileList.txt) do (
set /A num+=1
set "FILE_ARRAY[!num!]=%%a"
)
rem Process the FILE_ARRAY elements:
for /L %%i in (1,1,%num%) do echo Processing: %%i- "!FILE_ARRAY[%%i]!"
I finaly use this way:
set FILE_ARRAY[0]=filename1.xxx
set FILE_ARRAY[1]=filename2.yyy
set FILE_ARRAY[2]=filename3.zzz
for /F "tokens=2 delims==" %%i in ('set FILE_ARRAY[') do (
echo %%i
)
Thanks for your answers.
Seems like you might be hitting a batch file limitation (link), The following link describes a WA for this issue as dumping what you want in a var into a file, then reading that file back in when you need that massive var.
Ah batch, and your endless workarounds...
I am creating an MS DOS batch script that needs to list every .bat file in the current directory, but not show autoexec.bat or other utilities or systems .bat files that shouldn't be run by the user.
I currently have DIR "*.bat" /B /P
This lists all .bat files appropriately, but it shows autoexec.bat. How would I exclude that from the list? Also slightly important, how could I chop off the file extensions and show more than the 7-characters DOS limits files to?
Constraints: I am not able to use a DOS version above WinME. That is the version I am using.
Thanks for any help.
EDIT:
There is plenty of information on the internet about doing this, but it is all in the windows command processor, not MS DOS. Please understand that DOS and the Command Prompt are not the same thing.
#echo off
setlocal EnableDelayedExpansion
rem Add more names separated with slashes here:
set exclude=/autoexec/
for %%a in (*.bat) do (
if "!exclude:/%%~Na/=!" equ "%exclude%" (
echo %%~Na
)
)
EDIT: Some explanations added
Batch file processing is slow, so you should use techniques that allows a Batch file to run faster. For example:
Try to use the minimum lines/commands to achieve a certain result. Try to avoid external commands (*.exe files) like find, findstr, fc, etc. specially if they work on small amounts of data; use if command instead.
Use for %%a in (*.bat)... instead of for /F %%a in ('dir /B *.bat').... The second method requires to execute cmd.exe and store its output in a file before for command can process its lines.
Avoid pipes and use redirections instead. A pipe require the execution of two copies of cmd.exe to process the command at each side of the pipe.
A simple way to check if a variable contain a given string is trying to delete the string from the variable: if the result is different then the string exists in the variable: if "!variable:%string%=!" neq "%variable%" echo The string is in the variable.
Previous method may also be used to check if a variable have anyone of a list of values: set list=one two three, if "!list:%variable%=!" neq "%list%" echo The variable have one value from the list. If the values of the list may have spaces, they must be separated by another delimiter.
EDIT: New version added as answer to new comments
The easiest way to pause one page at a time is to use more filter this way:
theBatchFile | more
However, the program must reorder the output in order to show it in columns. The new version below achieve both things, so it does not require more filter; you just need to set the desired number of columns and rows per page.
#echo off
setlocal EnableDelayedExpansion
rem Add more names separated with slashes here:
set exclude=/autoexec/
rem Set the first two next variables as desired:
set /A columns=5, rows=41, wide=(80-columns)/columns, col=0, row=0
rem Create filling spaces to align columns
set spaces=
for /L %%a in (1,1,%wide%) do set spaces= !spaces!
set line=
for %%a in (*.bat) do (
if "!exclude:/%%~Na/=!" equ "%exclude%" (
rem If this column is less than the limit...
set /A col+=1
if !col! lss %columns% (
rem ... add it to current line
set name=%%~Na%spaces%
set "line=!line!!name:~0,%wide%! "
) else (
rem ... show current line and reset it
set name=%%~Na
echo !line!!name:~0,%wide%!
set line=
set /a col=0, row+=1
rem If this row is equal to the limit...
if !row! equ %rows% (
rem ...do a pause and reset row
pause
set row=0
)
)
)
)
rem Show last line, if any
if defined line echo %line%
Antonio
attrib +h autoexec.bat
should hide autoexec.bat and it should thus not appear in the list
DIR "*.bat" /B /P | find /v "autoexec" | for %i in (*.bat) do #echo %~ni
Using for to process each file name individually:
setlocal enabledelayedexpansion
for /f %%i in ('dir "*.bat" /b') do (
set system=0
if "%%i"=="autoexec.bat" set system=1
if "%%i"=="somesystem.bat" set system=1
if !system!==0 echo %%i
)
Another method without variables:
for /f %%i in ('dir "*.bat" /b') do call :test %%i
goto continue
:test
if "%1"=="autoexec.bat" goto :eof
if "%1"=="somesystem.bat" goto :eof
echo %1
goto :eof
:continue
For both, you can add new filenames to exclude from the list.