I know you can create a batch file from a batch file but this code is not working the "/a" does not make it to the new batch file
the code:
set/a points=500
#echo set/a points=%points% >>scoreboard.bat
the result:
set points=500
That's because variables are being expanded before the echo prints anything. To echo some special chars literally, you have to escape them.
Most of those special chars (<>|&) are escaped with a caret: echo ^>.
The percent sign instead is escaped by another percent sign: echo %%.
To write literally set /a points=%points%, write:
#echo set /a points=%%points%% >>scoreboard.bat
Tip: if you want to write several lines, put them in a command block and redirect it once instead of every single line:
(
echo #echo off
echo REM new bat file
echo set /a points=%%points%%
) >scoreboard.bat
Related
I've set up an app for a couple of friends and me in batch with a auto-updating system, but I need to add a line of code in the auto-updating system. I decided to completely rewrite the file, it takes a long time to add 'echo' in from to every line and, '>>text.txt' at the end of every line and added '^' when needed, so I was wondering if there was an easier way of writing lot's of code to a file in batch.
Example:
#echo off
rem I need a way to do the following without adding 'echo' and '>>text.txt'
echo echo Checking for updates... >text.txt
echo echo 1.4 ^>^>new.txt >>text.txt
echo ping localhost -n 2 ^>nul >>text.txt
rem and so on and so on.
Or if there is a way to simply add a new line of code in a specific place in the file, that would also help!
Thanks in advance!
The following is how you can more easily and efficiently do what your current code does, by removing all of those individual write processes.
#( Echo #Echo Checking for updates...
Echo #(Echo 1.4^)^>^>"new.txt"
Echo #(%__AppDir__%ping.exe LocalHost -n 2^)^>NUL
)>"text.txt"
There are other possibilities, but at this time, based on the lack of information in your question, I'm reluctant to expand further at this time.
If I understand correctly, then you could do the following:
in the batch file, prepend each line of text that you want to output with :::: (this constitutes an invalid label that is going to be ignored);
then use the following code:
rem // Redirect to file:
> "text.txt" (
rem // Find all lines that begin with `::::` and loop over them:
for /F "delims=" %%T in ('findstr "^::::" "%~f0"') do (
rem // Store currently iterated line:
set "TEXT=%%T"
rem // Toggle delayed expansion to avoid loss of `!`:
setlocal EnableDelayedExpansion
rem // Remove `::::` prefix and output remaining line:
echo(!TEXT:*::::=!
endlocal
)
)
replace set "TEXT=%%T" by call set "TEXT=%%T" if you want to enable percent expansion within the returned text (so it could, for example, contain %~nx0, which would then be expanded to the file name of the script).
I am using this technique a lot (without the output redirection) for help systems in my batch files (/?).
Your asked
I need a way to do the following without adding echo and >>text.txt
The script takes advantage of the line continuation character, the caret ^.
The first character after the caret ^ is always escaped, so do linefeed characters:
#echo off
SETLOCAL EnableDelayedExpansion
call :init
>text.txt (
echo #echo off%NL%
Checking for updates...%NL%
>^>new.txt echo 1.4%NL%
>NUL ping localhost -n 2
)
ENDLOCAL
exit /b
:init
( set LF=^
%= 0X0D FORM FEED =%
)
::0X0A Carriage Return
FOR /F %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
::Create newline/line continuation character
set ^"NL=^^^%LF%%LF%^%LF%%LF%^^" %= Unix-Style Endings \n =%
::set ^"NL=%CR%^^^%LF%%LF%^%LF%%LF%^^" %= Windows-Style Endings \r\n =%
exit /b
The variable %LF% is a escaped linefeed, and %NL% is a escaped %LF% plus a escaped caret ^ for line continuation.
The code
>^>new.txt echo 1.4%NL%
>NUL ping localhost -n 2
might seem strange. Why isn't the first caret ^ escaped?
Because %NL% already escaped it.
Sources:
Explain how Windows batch newline variable hack works
https://stackoverflow.com/a/5642300/12861751
https://www.dostips.com/forum/viewtopic.php?t=6369
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.
Not quite sure how to title my issue that I'm running into (tried as best I could), but what I'm having issues with is when I'm trying to read in file names and then use them in a backup script I've wrote. I had originally tested it on files without &'s in the name (didn't remember that I had any with them in it, and didn't realize that there would be an issue until now).
Here is part of the code that is being used upto the call in the below example:
:backup2
if %DEBUG%=="t" echo Begin backup part 2
for %%? in ("!FILEREADIN!") do (
SET "FILENAME=%%~n?%%~x?"
SET "BACKUPFQP=%%~f?"
SET "BACKUPLAST=%%~t?"
call :getlength FILELENGTH "!FILENAME!"
Anyway the part that I'm running into the issue with when I'm working with the file name in my code to get the length of the file name (used in a separate section of script).
:getlength
SETLOCAL enabledelayedexpansion
if %DEBUG%=="t" echo %2 parsed... %%2 delayed... !%2!
SET "LENGTH=!%1!"
SET "STRING=%2"
REM need to correct the string for the "" that get added from passing in %2
REM Issue arises with this part below when working on a file name with a &
SET "STRING=!STRING:~1,-1!"
:getlengthwhile
if %DEBUG%=="t" echo Length !LENGTH!
if %DEBUG%=="t" echo String left: !STRING!
SET /a "LENGTH+=1"
REM Issue here too when working with file names with &'s
SET "STRING=%STRING:~1%"
if %DEBUG%=="t" echo Length now !LENGTH!
if %DEBUG%=="t" echo String now left: !STRING!
if %DEBUG%=="t" pause
if not ["%STRING%"]==[""] (
if %DEBUG%=="t" echo Continuing source length calculation
Goto :getlengthwhile
) else (
if %DEBUG%=="t" echo Length calculated)
ENDLOCAL & SET TEMPNUM=%LENGTH%
SET "%~1=%TEMPNUM%"
if %DEBUG%=="t" echo !%1!
if %DEBUG%=="t" pause
goto :eof
I know that there is escaping &'s to normally not have the error of the batch script trying to use the stuff right of the & as a command, but when reading in a file with one (or more) in its name how do I get it to work properly?
Here's an example of a file name that I'm having issue with and what happens when I'm running my script:
File "E:\Projects\.\Abilities&Events.docx"
Press any key to continue . . .
Begin backup part 2
"Abilities&Events.docx" parsed... %2 delayed...
'Events.docx""' is not recognized as an internal or external command, operable program or batch file.
As said, you should use more quotes and more delayed expansion.
Btw. Your code add the length to the variable in %1, perhaps this was intended, else you should change SET "LENGTH=!%1!" to set LENGTH=0
:getlength
SETLOCAL Enabledelayedexpansion
SET "LENGTH=!%1!"
SET "STRING=%~2"
:getlengthwhile
if defined STRING (
set /a LENGTH+=1
set "string=!string:~0,-1!"
goto :getlengthwhile
)
echo !LENGTH!
(
ENDLOCAL
SET "%~1=%LENGTH%"
)
goto :eof
Another problem of your code is this line
call :getlength FILELENGTH "!FILENAME!"
It fails with filenames containing ^, as they are doubled by the CALL.
So it's better to use
call :getlength FILELENGTH FILENAME
...
:getlength
SETLOCAL Enabledelayedexpansion
SET "LENGTH=!%1!"
SET "STRING=!%2!"
For much faster strlen functions you could look at SO: How do you get the string length in a batch file?
I found an answer to my problem. Thanks all for the help.
My solution was after finding out that I can replace parts of a variable (which I can use to delete parts of them even) from this site: http://ss64.com/nt/syntax-replace.html
I didn't know that replacing could be done (I knew that getting a part of a variable was possible (site for reference: http://ss64.com/nt/syntax-substring.html"))
I ended up ditching my getlength function (since it was no longer needed). and ended up with something like this:
:backup2
if %DEBUG%=="t" echo Begin backup part 2
for %%? in ("!FILEREADIN!") do (
SET "FILENAME=%%~n?%%~x?"
SET "BACKUPFQP=%%~f?"
SET "BACKUPLAST=%%~t?"
if %DEBUG%=="t" echo Filename before "!FILENAME!"
SET FILETESTPART=!FILEREADIN!
if %DEBUG%=="t" echo !FILETESTPART!
SET "FILETESTPART=!FILETESTPART:%SOURCE%=!"
if %DEBUG%=="t" echo Filename after "!FILETESTPART!"
SET "FILETESTPART=!FILETESTPART:~3!"
if %DEBUG%=="t" echo Filename after "!FILETESTPART!"
I have a batch file that allows the user to enter a file. It enters and stores the file path correctly. But when I go to enter the file and do anything with it, (in my case just set line1 as the first line and print it), It can't find the filename because it has a space in there.
Here is the code.
#ECHO OFF
SET /p "infile=enter file"
Echo you're file is %infile%
setlocal enableextensions enabledelayedexpansion
FOR /f "usebackqdelims=" %%a IN ("%infile%") do (
set line1= %1
goto :endfor
)
:endfor
echo %line1%
pause
endlocal
And here is what happens.
When trying to find the file, The system thinks that files name ends at "my" instead of the full "my text.txt" How could I deal with the space in the filename?
Your problem is not the space in the name of the file. Your problem is that you are typing the name of the file with quotes, quotes that are stored in the variable, and then placing aditional quotes in the for command.
SET /p "infile=enter file"
set "infile=%infile:"=%"
Echo you're file is %infile%
Ensure there are no aditional quotes in the filename and this error will not happen (or don't use quotes when typing the name of the file)
In addition to MC_ND's answer:
In the original code this line should be set to %%a and not %1 (without the space too):
set line1= %1
I have a DATA file, which holds database-connection info in the following format (with | as delimiter):
DatabaseServerIp1|UserName1|Password1|DatabaseName1
DatabaseServerIp2|UserName2|Password2|DatabaseName2
DatabaseServerIp3|UserName3|Password3|DatabaseName3
And I have a batchfile which reads the contents of this file and uses the contents to execute a script on each of the databases in this file. This script works great in most cases, but runs into issues when one of the variables contains a special character like % or #.
I read online that I'm supposed to use "SETLOCAL EnableDelayedExpansion" and surround the variables with exclamation marks, but I can't get this to work. Some other posts also mentionned using quotes instead of exclamation marks, but I couldn't get this to work either.
I would like to avoid changing the contents of the DATA file (I don't want to add escape characters inside the DATA file).
The script in which I'd like to use this is:
rem ... more code here ...
FOR /F "eol== delims=| tokens=1,2,3,4" %%i IN (%DATABASEDATAFILE%) DO CALL :_ACTION %%i %%j %%k %%l
GOTO :_END
:_ACTION
IF "%1"=="" GOTO :EOF
SET IPSERVER=%1
SET USERNAME=%2
SET PASSWORD=%3
SET DATABASE=%4
sqlcmd -S%IPSERVER% -U%USERNAME% -P%PASSWORD% -d%DATABASE% -i%SCRIPTFILE% -o%RESULTFILE%
GOTO EOF
:_END
rem ... more code here ...
:EOF
How do I make this code handle special characters correctly?
Example: %-character in the password field.
As you might have guessed, I'm not the original creator of this batch file or the data file.
The most of your suggestions (delayed expansion, quotes) are correct, but the order is important.
setlocal DisableDelayedExpansion
FOR /F "eol== delims=| tokens=1,2,3,4" %%i IN (%DATABASEDATAFILE%) DO (
SET "IPSERVER=%%i
SET "USERNAME=%%j"
SET "PASSWORD=%%k"
SET "DATABASE=%%l"
CALL :ACTION
)
goto :EOF
:ACTION
setlocal EnableDelayedExpansion
sqlcmd -S!IPSERVER! -U!USERNAME! -P!PASSWORD! -d!DATABASE! -i!SCRIPTFILE! -o!RESULTFILE!
endlocal
goto :EOF
#ECHO OFF
SETLOCAL
FOR /f "tokens=1-4delims=|" %%a IN (%DATABASEDATAFILE%) DO ECHO sqlcmd -S%%a -U%%b -P%%c -d%%d -i%SCRIPTFILE% -o%RESULTFILE%
GOTO :EOF
should do the trick. I've just ECHOed the SQL line produced - after verification, you'd need to change ECHO sqlcmd to sqlcmd to execute the sqlcmd.