I am using a for loop, and within it, I echo a variable, that updates as the loop goes on. For the format of my code, I have to append everytime it loops. Is there a way for it to overwrite the first time, then on the rest of the for loops, it appends? Here is my code:
#echo off
set file=file
cd %cd%
SETLOCAL ENABLEDELAYEDEXPANSION
for /d %%b in (*) do dir /ad /on /s /b "%%b" >> get_dirs.txt
for /F "tokens=*" %%A in (get_dirs.txt) do (
echo ^<a href="%file%:///%%A"^>%%A^</a^>^<br^> >>index.html
)
pause
So everytime I run this, even when I delete the output file, the html document gets bigger and bigger by duplicating the text inside it so I have the same thing up to over 100 times.
If you want some more backstory of the code head over to my other question that developed some of the code, Command Variable Outputs.
You could use a variable to track whether it is the first iteration or not, and then use an IF/ELSE statement with the same command in each branch, except one uses overwrite, and the other append. But that is needlessly complex.
The simplest thing to do is to delete any existing file before the start of the loop, and then use append in all cases. The append redirection will automatically create the file if it does not yet exist.
#echo off
setlocal
set file=file
del get_dirs.txt
for /d %%b in (*) do dir /ad /on /s /b "%%b" >>get_dirs.txt
del index.html
for /f "delims=" %%A in (get_dirs.txt) do (
echo ^<a href="%file%:///%%A"^>%%A^</a^>^<br^> >>index.html
)
pause
Unless you need "get_dirs.txt" later on, I don't see any need to create that file. you can use FOR /F to directly process the result of the DIR command.
Your code opens the output file, positions the file pointer to the end, and closes the output file once for each iteration. This slows the program down. It is much more efficient (faster) to put parentheses around the entire FOR statement and then use a single redirection. This avoids the need for append mode, so no DEL command is needed.
#echo off
setlocal
set file=file
>index.html (
for /d %%b in (*) do for /f "delims=" %%A in (
'dir /ad /on /s /b "%%b"'
) do echo ^<a href="%file%:///%%A"^>%%A^</a^>^<br^>
)
pause
Here are some additional notes regarding changes I made to your code that are unrelated to your question:
cd %cd% does absolutely nothing useful (change directory to the current directory). So I removed that line.
You enable delayed expansion, but never use it (all your expansion uses %var% instead of !var!). I don't see any need for delayed expansion, and enabled delayed expansion can corrupt FOR variables if they contain !. So I disabled delayed expansion.
I put the SETLOCAL before you define your file variable. No sense in cluttering up your command session environment variable space after your script runs.
Your use of "tokens=" will probably work in real world situations. But technically it is not what you want. It will strip leading spaces from the values. Instead you want to disable delims by using "delims=".
Related
I've been wrestling trying to get the syntax right on this batch file and I cannot figure out why some things aren't working.
The variable i is not getting incremented each time I do it.
Concatenation on strc doesn't seem to concatenate.
Here is my code:
set i=0
set "strc=concat:"
for %%f in (*.mp4) do (
set /a i+=1
set "str=intermediate%i%.ts"
set strc="%strc% %str%|"
ffmpeg -i "%%f" -c copy -bsf:v h264_mp4toannexb -f mpegts "%str%"
)
set strc="%strc:-1%"
ffmpeg -i "%strc%" -c copy -bsf:a aac_adtstoasc Output.mp4
You are not the first, who fell into the famous "delayed expansion trap" (and you won't be the last).
You need delayed expansion if you want to use a variable, that you changed in the same block (a block is a series of commands within parentheses (and )).
Delayed variables are referenced with !var! instead of %var%.
Reason is the way, cmd parses the code. A complete line or block is parsed at once, replacing normal variables with their value at parse time. Delayed variables are evaluated at runtime.
Two simple batch files to demonstrate:
setlocal EnableDelayedExpansion
set "var=hello"
if 1==1 (
set "var=world"
echo %var% !var!
)
setlocal EnableDelayedExpansion
for /L %%i in (1,1,5) do (
echo %random% !random!
)
Note: A line is also treated as a block:
set "var=old"
set "var=new" & echo %var%
With delayed expansion:
setlocal EnableDelayedExpansion
set "var=old"
set "var=new" & echo !var!
Delayed expansion is per default turned off at the command prompt. If you really need it, you can do:
cmd /V:ON /C "set "var=hello" & echo !var!"
Also there is a way to do the same without delayed expansion (but call costs some time, so it's slower, but if for some reason you can't / don't want to use delayed expansion, it's an alternative):
setlocal DISabledelayedexpansion
for /L %%i in (1 1 5) do (
call echo %random% %%random%%
)
Both methods can also be used to display array-like variables:
(This is often asked like "variable which contains another variable" or "nested variables")
Here is a collection for using such array-like variables in different situations:
With delayed expansion:
setlocal ENableDelayedExpansion
set "num=4"
set "var[%num%]=HELLO"
echo plain delayed: !var[%num%]!
for /L %%i in (4 1 4) do (
echo for delayed: !var[%%i]!
set a=%%i
call echo for delayed with variable: %%var[!a!]%%
)
without delayed expansion:
setlocal DISableDelayedExpansion
set "num=4"
set "var[%num%]=HELLO"
call echo plain called: %%var[%num%]%%
for /L %%i in (4 1 4) do (
call echo FOR called: %%var[%%i]%%
set a=%%i
call echo FOR called with variable: %%var[%a%]%%
)
Note: setlocal has no effect outside of batchfiles, so delayedexpansion works only:
In batch files
When the cmd was started with delayed expansion enabled (cmd /V:ON) (by default, the cmd runs with delayed expansion disabled)
(Follow the links, when you are interested in the technical background or even the advanced technical stuff)
I'm very new to .bat files and have excitedly created some to copy, move and rename documents.
Despite searching, I'm getting stuck with a more complex command, largely because the document I'm trying to modify is pipeline delimited rather than 'normal' csv...
My question: Can I, and if I can - how do I take an existing pipeline delimited csv that always has the same number of columns and add a column onto the end with todays date (DD/MM/YYYY) in it for every row?
$ awk -F, 'NF>1{$0 = "\"YYYY-MM-DD\"" FS $0}1' file
sed 'N;s/","/","YYYY-MM-DD HH:MM:SS","/5' file
I cant seem to get anything to even modify the document at the moment :-(
Your batch attempt isn't that bad:
#echo off
for /f "delims=" %%a in ('type "file.csv"') do (
>>"fileout.csv" echo.%%a|%time%
)
Just a few adjustments:
#echo off
(for /f "delims=" %%a in (file.csv) do (
echo(%%a^|%time%
))>"fileout.csv"
for /f is able to process the contents of a file directly (no need for type, although it works fine)
Redirecting (>>) inside the loop is slow, because the file will be opened, and closed every time you write to it (although it works). It's much faster to only once open/close the file (especially with large files).
echo. is not secure (although it works fine in most circumstances), best option is echo(.
The pipe, | is a special character and in this case needs escaping with the caret, ^.
Note: for /f skips empty lines, so they will not be in the new file. Same with lines, that start with ; (default EOL)
Edit for "adding |Date to the Header":
#echo off
<file.csv set /p header=
>fileout.csv echo %header%^|DATE
(for /f "skip=1 delims=" %%a in (file.csv) do (
echo(%%a^|%date%
))>>"fileout.csv"
<file.csv set /p header= is a way to read just one (the first) line of a file to a variable. Write it back with the new data appended (or leave it unchanged - your comment isn't quite clear about that). Use skip=1 to skip the first line with further processing.
Don't forget to change ))>"fileout.csv" to ))>>"fileout.csv".
Im trying to build a batch file.
I have 30 csv files in a folder
My intention is
Get each file name (Example is 097_021216_192332.csv)
Extract the first 3 digits
Compare it with a lookup table ( lookup1.bat) against which i have marked another string
EG: lookup1.bat
#echo 107=B_05-
#echo 097=B_06-
#echo 149=B_07-
#echo 109=B_08-
#echo 101=B_09-
#echo 105=B_10-
#echo 098=B_11-
So here i will get "B_06-"
Modify the file name with this "B_06-" prefix and rename the file
Here is my code , i have only basic ideas about looping and im struggling a lot.Thanks for any help.
#echo on
setlocal enabledelayedexpansion
for %%a in ("*.csv") do (
set FileName=%%~na
goto:stepa
goto:eof
)
:stepa
for /f "tokens=1 delims=_" %%a in ("%FileName%") do (
set A=%%a
echo %A%
)
#SET MN=%A%
#FOR /F "tokens=1,2 delims==" %%i IN ('lookup1.bat') DO #IF %%i EQU %MN% SET MW=%%j
#ECHO The board number corresponding to %MN% is %MW%.
set "str=%MW%%FileName%"
echo "%str%"
Ren "!FileName!" "!str!"
:eof
You have a series of problems with the structure of your program. If you want to call a subroutine from inside a for command the right way is using call, not goto, and the goto :eof command must be placed after the for ends. However, this code is simple enough so it don't requires a subroutine.
The table lookup method is more efficient (and also simpler, IMHO) if you use an array to load the table just once, and then directly access its elements via an index.
#echo off
setlocal EnableDelayedExpansion
rem Create the lookup file with lookup1.bat file,
rem this program create lines like this one:
rem 097=B_06-
call lookup1.bat > lookup1.txt
rem Load the lookup table from the file,
rem this method create _array elements_ like this one:
rem set "table[097]=B_06-"
for /F "tokens=1,2 delims==" %%a in (lookup1.txt) do set "table[%%a]=%%b"
rem Process the files in current folder,
rem file name format is this:
rem ###_restOfFileName.csv
for /F "tokens=1* delims=_" %%a in ('dir /A:-D /B *.csv') do (
#ECHO The board number corresponding to %%a is !table[%%a]!.
ren "%%a_%%b" "!table[%%a]!%%b"
)
You may test if the board number is not defined for a file via an additional if not defined table[%%a] ... command placed inside the for.
You may directly create the lookup1.txt file with your text editor; just eliminate all these #echo parts from lookup1.bat and change the extension.
You may review a detailed explanation on array use in Batch files at this answer.
Basically, I was working on some code, dealing with creating a text file, then later reading the text file line by line, and turning each line into a variable, which is then echoed within other text to make the variable fit into html code, which will later be used in a website. Here is my code:
#echo off
set file=file
cd %cd%
for /d %%b in (*) do dir /ad /on /s /b "%%b" >> get_dirs.txt
for /F "tokens=*" %%A in (get_dirs.txt) do (
echo %%A > tmpfile.txt
set /p t= < tmpfile.txt
pause
echo ^<a href="%file%:///%t%"^>%t%^</a^>
)
pause
Ignore the pauses they were being used for previous debugging. My main problem is that my command prompt is giving me an output of
which is want I want accept my variable %t% is not being echoed with the rest of html code. I can't seem to figure out what is wrong with it. Thank you.
Standard delayed expansion problem. Search SO for hundreds of articles about setlocal delayedexpansion
Your code is written better
for /F "tokens=*" %%A in (get_dirs.txt) do (
echo ^<a href="%file%:///%%A"^>%%A^</a^>
)
If, for reasons you don't reveal, you actually want the variable in t then
for /F "tokens=*" %%A in (get_dirs.txt) do (
set "t=%%A"
echo ^<a href="%file%:///%%A"^>%%A^</a^>
)
will do that, but you'd syill need to either use delayedexpansion or a subroutine to use the dynamic value assigned to t.
I have read this thread, which helped, but doesn't answer my specific question. I'm hoping someone can help.
I am trying to export a CSV file with TWO COLUMNS of data. Column 1 is the actual filename (with extension), and Column 2 would be the immediate Folder Name (without any path info) of the file location. Question I have is, is that possible? Next question is, how can I export this as a CSV file with two columns of information?
This is a good starting point except this only has the filename (doesn't have the second column that shows the immediate folder name), and this doesn't seem to return to the next line for each filename. Instead this is simply separating with commas and not returning to new lines.
Can you advise if this is possible and offer some ideas?
#echo off
<nul (
for /f "eol=: delims=" %%F in ('dir /b /o:n') do set /p =""%%F","
) >fileList.csv
Thanks everyone!
If by the "Immediate folder name" you mean the name of the containing directory but without the path to that directory, then:
#ECHO OFF
SETLOCAL
PUSHD "%~1"
FOR /f "delims=" %%i IN ("%cd%") DO SET directory=%%~nxi
(
FOR /f "delims=" %%i IN ('dir /b /a-d /on') DO (
SETLOCAL enabledelayedexpansion
ECHO "%%i","!directory!"
endlocal
)
)>filelist.csv
POPD
The pathname of the directory required should be supplied as the first parameter, quoted if necessary.
Essentially, change to the directory in question, find and save the name of the leaf directory, then execute a directory scan returning the filenames. Quote both and output with a comma between. The inner setlocal is to allow at least some silly directory names.
edit 20130422-1421Z
#ECHO OFF
SETLOCAL
PUSHD "%~1"
FOR /f "delims=" %%i IN ("%cd%") DO SET directory=%%~nxi
(
FOR /f "delims=" %%i IN ('dir /b /a-d /on') DO (
SET fdate=%%~ti
SETLOCAL enabledelayedexpansion
ECHO "%%i","!directory!","!fdate:~0,10!"
endlocal
)
)>filelist.csv
POPD
Edited to show date as third element. Quotes retained - remove at will.
If date AND TIME are required, remove the SET fdate line and replace the "!fdate:~0,10!" with "%%~ti
Date and time format - to be certain, need to know the format you are using.
If you're doing a recursive directory search, filename with extension only can be obtained within your for /f loop from %%F by using %%~nxF. That's easy.
The trickier part is scraping the last folder from the path (%%~pF). There's actually an easy trick to that as well though. Use another for loop to get %%~nxI of %%~dpF. Yes, the filename.ext of a full path is the trailing directory.
This only works if the directory does not end in a trailing backslash, though. Since the result of %%~dpF does end in a trailing backslash, you can work around it simply by adding a single dot to the end. So instead of "c:\users\me\Desktop\" you get the ~nx of "c:\users\me\Desktop\." with a trailing dot.
Enough explanation. Here's how it's done.
#echo off
for /f "delims=" %%F in ('dir /s /b /o:n') do (
for %%I in ("%%~dpF.") do echo "%%~nxF","%%~nxI"
) >filelist.csv