I'm trying to use a batch file to create another batch file... it's a file I have to use quite often with a few variables changed each time. I'm running into an issue because in the batch I'm trying to create, it is also using echo to write to a .txt file.
Here is the command:
echo echo %date% - %time% >> C:\MOVEit\Logs\FileGrabberLog.txt >> C:\filegrabber_%org%.bat
I want to enter the whole string echo %date% - %time% >> C:\MOVEit\Logs\FileGrabberLog.txt into C:\filegrabber_%org%.bat.
I can put "" around it but then they appear in the batch I'm trying to create.
Anyone know of a way around this?
You escape % with %% and other special characters with ^ so this should work;
echo echo %%date%% - %%time%% ^>^> C:\MOVEit\Logs\FileGrabberLog.txt >> C:\filegrabber_%org%.bat
Or to avoid the carets you can use disappearing quotes
setlocal EnableDelayedExpansion
(
echo !="!echo %%date%% - %%time%% >> C:\MOVEit\Logs\FileGrabberLog.txt
) > C:\filegrabber_%org%.bat
Only the percents have to be doubled then.
It works, as the !="! is parsed in the special character phase, and it is decided, that the rest of the line will be quoted.
And in the delayed phase the !="! will be removed, as the variable with the name =" does not exist (and it can't be created).
The following answer might be beneficial to your question:
This was posted earlier and the answer was given, similar to what was given here:
Ignore Percent Sign in Batch File
Related
Say I have the following batch script:
For ... DO (
SET VAL=%%B
IF defined VAL echo %%A=%%B >> %OUTPUT_FILEPATH%
)
How could I get the echo to output using Unix (just line feed) line endings?
Alternatively, could I write the file as-is then convert it from the batch script afterwards? (some kind of find /r/n and replace with /n? If so, how would I do that?)
I'd like a self-contained solution (i.e. one that doesn't involve downloading extra utilities, and can be done from within the batch script itself [Windows 7]).
The suitable way to perform this conversion is not via a Batch file, but using another programming language, like JScript; this way, the conversion process is fast and reliable. However, you don't need a hundreds lines program in order to achieve a replacement as simple as this one. The two-lines Batch file below do this conversion:
#set #a=0 /* & cscript //nologo //E:JScript "%~F0" < input.txt > output.txt & goto :EOF */
WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/\r\n/g,"\n"));
EDIT: I added a modification to the original code that allows to include more commands in the Batch part in the standard way.
#set #a=0 /*
#echo off
set "OUTPUT_FILEPATH=C:\Path\Of\The\File.txt"
cscript //nologo //E:JScript "%~F0" < "%OUTPUT_FILEPATH%" > output.txt
move /Y output.txt "%OUTPUT_FILEPATH%"
goto :EOF */
WScript.Stdout.Write(WScript.Stdin.ReadAll().replace(/\r\n/g,"\n"));
The first line is a trick that hide the cscript command from the JScript code, so the compilation of this hybrid .BAT file don't issue errors.
In the JScript code: WScript.Stdin.ReadAll() read the whole redirected input file; this may cause problems if the file is huge. The replace method use a regex to identify the text to replace and put in its place the second string; you may read a further description of this topic at this link. The WScript.Stdout.Write just take the output from replace and send it to the screen. Easy! Isn't it? ;-)
If you are OK with using PowerShell, you can produce a Unix newline like this:
PowerShell -Command Write-Host
You can combine this with the SET /P trick to output text without newlines and add newlines manually. For example:
( ECHO | SET /P="Hello World!" & PowerShell -Command Write-Host ) > output.txt
After this, output.txt will contain the text Hello World! with a single 0x0a character appended.
Taken from Macros with parameters appended:
Formatting is tricky, but try
set ^"LF=^
^" Don't remove previous line & rem line feed (newline)
set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"& rem Define newline with line continuation
For ... DO (
SET VAL=%%B
IF defined VAL <nul set/P^=%%A=%%B%\n%>> %OUTPUT_FILEPATH%
)
Or, to avoid the leading space after first line:
<nul set/P^=%%A=%%B%\n%^>> %OUTPUT_FILEPATH%
I'm trying to implement, in a Windows batch file, the logic "if %MyBinaryDir% is not already at the beginning of the system %PATH%, then put it there; if it's already there, do nothing".
I've got this:
#echo %PATH% | findstr /i /b /c:"%MyBinaryDir%;" > nul || set "PATH=%MyBinaryDir%;%PATH%
This has always worked pretty well, until I tried to deploy on someone's machine where for some unearthly reason the %PATH% variable contained an odd number of quote characters.
The problem boils down to this:
#set x="
#echo %x%
#echo %x% | more
The second line prints a single " character. The third line is tripped up by the quote character and fails to pipe the echo output to the second binary (in my case findstr, but in this boiled-down example more) at all. Instead, it literally prints the characters:
" | more
So my questions are:
(Y) How do I safely pipe any arbitrary string into a second binary?
and/or
(X) Is there a way of conditionally-prepending directories to the system path that avoids this mess?
Use delayed expansion to echo any variable content in a safe way.
setlocal EnableDelayedExpansion
set x="
echo !x!
echo !x! | more
echo !path! | more
And to be safe also with empty variables you could use echo(
echo(!path! | more
There is also a question about pretty print/split the path variable
I suggest to clean up PATH as no directory path in this environment variable should contain ever quotes.
Here is an example batch code for your task:
#echo off
rem Uncomment the line below for testing with C:\PythonXX
rem already in PATH with an erroneous double quote in PATH.
rem set "PATH=C:\PythonXX;"%PATH%"
echo Path before clean up:
echo.
echo PATH=%PATH%
set "PATH=%PATH:"=%"
echo.
echo Path after clean up:
echo.
echo PATH=%PATH%
if /I "%PATH:~0,12%" NEQ "C:\PythonXX;" set "PATH=C:\PythonXX;%PATH%"
echo.
echo Path modified:
echo.
echo PATH=%PATH%
echo.
pause
See the answer on set environment variables with spaces and the other answers referenced there why set "variable=value" should be used even if the value to assign to a variable contains 1 or more quotes itself.
What I mean is this:
Echo testing >> new_text_document.txt
Is there a way to give "testing" multiple lines?
Might it work better with variables like:
Set xyz= testing
Echo %testing% >> new_text_document.txt
I don't know. If you can help, it would be apreaciated. Thanks in advance.
There are many ways, it really depends on what you need. One way is to put newline in a variable (note the 2 empty lines are critical):
#echo off
setlocal EnableDelayedExpansion
set NL=^
echo two!NL!lines >> new_text_document.txt
Use one or two > does not matter. since >> appends you can do many lines of echo too
#echo off
:other stuff
echo multiple >> new_text_document.txt
echo lines >> new_text_document.txt
echo. >> new_text_document.txt
echo of text >> new_text_document.txt
that may be clunky so you could do something like this instead
#echo off
:other stuff
(
echo multiple
echo lines
echo.
echo of text
) >> new_text_document.txt
Last but not least you can do something similar to the bash trick but you will have no EOL marker.
#echo off
more +4 %~f0 >> new_text_document.txt
exit /b
your
multi
line
text
In addition to this its possible to for loop the file yourself this way you can have a EOL marker for many different pieces.
2 Questions:
1) Changing scripts based on input
Basically, say I have a file, like search.html that changes based on what you type in.
Aside from doing
set/p string=What would you like to search for?
echo ^<!DOCTYPE html^> >>file.html
echo ^<html^> >>file.html
echo ^<title^>^</title> >>file.html
echo ^<script language="JavaScript""^> >>file.html
echo string = '%site%'
...
Is there another way to do this?
2) Getting things returned from a file?
I have no example for this. I was simply wondering if you could start a file, use wait, and once it has closed get what was in it?
try this:
#echo OFF &setlocal
(
echo ^<!DOCTYPE html^>
echo ^<html^>
echo ^<title^>^</title^>
echo ^<script language="JavaScript""^>
echo string = '%site%'
)>file.html
2) Getting things returned from a file
#echo OFF &setlocal
FOR /f "delims=" %%a IN (file.html) DO (
ECHO(%%a
)
There are many ways to replace text in a file with something else. You can get input and then replace a MARKER (text like that) with the input text. VBS, Powershell, SED, AWK, and batch can do it.
Your second question is a bit short on detail - but FINDSTR etc can read lines from a file.
I am trying to make a simple statistic program which simply counts how many times it's been runned. The problem occurs when I am echo-ing the variable of result of times onto the tracking-file, which are greater than 1. Is it impossible to echo a variable onto a file? What am I missing? :/
if not exist statistic_files (
mkdir statistic_files
)
cd statistic_files
if exist %username%-use_tracking (
FOR /F %%G IN (%username%-use_tracking) DO set /A uses=%%G+1
:: THE PROBLEM IS BELOW
echo %uses% > %username%-use_tracking
cd ..
)
if not exist %username%-use_tracking (
echo 1 > %username%-use_tracking
cd ..
)
Your code is failing when the file already exists because you are setting a variable value and later accessing the value within the same parenthesized code block; %uses% is expanded when the line is parsed, and the entire code block is parsed all at once. So the value you are echoing is the value that existed before the FOR loop is executed - not what you want.
You could solve the problem by enabling delayed expansion using setlocal enableDelayedExpansion, and then using !uses! instead of %uses%. Type help set from a command prompt for more information. Start reading with the paragraph that begins "Finally, support for delayed environment variable expansion has been added..." about halfway through the help.
But there is a simpler way that avoids the need for delayed expansion. You only need to set the value within the IF block. You can access it later outside of the IF block.
There is one other issue that you are currently avoiding, but you should be aware of: 1>filename and >filename do the same thing - they are an instruction to redirect standard output (stdout) to a file. 1 is the id of stdout, and it is the default when a number is not explicitly given. 2>filename redirects standard errror (stderr) to a file. You are avoiding interpretation of the number as the stream id because you have a space between the number and the >. But if you are not careful, you will someday not have a space and be confused as to why the number is not printing. You can avoid the problem by putting the redirection at the front instead of at the end. >filename echo %uses%
So here is a complete working solution.
#echo off
setlocal
if not exist statistic_files mkdir statistic_files
cd statistic_files
set "file=%username%-use_tracking"
set "uses=1"
if exist %file% for /f %%G IN (%file%) do set /A uses=%%G+1
>%file% echo %uses%
Testing your current file (just by removing the block at the top that creates the folder, so that it outputs to my current directory, the two cd .. lines, and adding set username=ken at the top) produces:
The correct content (a single line with 1) the first time.
A file with the single line ECHO is on the second time.
An error message the third time.
You're getting that output because ECHO is on, and the second run is ECHOing that setting the second time the batch file is run.
You need to turn echo off at the top of your batch file. (This doesn't prevent using the echo command; it just prevents your batch file itself from being output.)
This works for me (making the changes I mentioned above):
#echo off
set username=ken
if exist %username%-use_tracking (
FOR /F %%G IN (%username%-use_tracking) DO set /A uses=%%G+1
:: THE PROBLEM IS BELOW
echo %uses% > %username%-use_tracking
)
if not exist %username%-use_tracking (
echo 1 > %username%-use_tracking
)
The results were:
The correct content (a single line with 1) the first time.
A file with the single line 2 the second time.
A file with the single line 3 the third time. (And so forth with more runs.)
If you're wanting to keep every line in the file, change the first echo %uses% > %username%-use_tracking to echo %uses% > %username%-use_tracking (note the double >>) instead. One > means overwrite, two means append to the end of current content.
Your problem is directly related to Delayed variable Expansion. To solve it, insert this line at beginnng of your program:
setlocal EnableDelayedExpansion
and enclose the variable that was modified inside an IF block in exclamation marks instead percents:
echo !uses! > %username%-use_tracking
For further details, seek for "Delayed Expansion" in this forum; there are plenty answers on the topic, like this one:
Set the variable to another variable in the for statement
Use >> instead of > for redirect (append mode).
Use absolute path for log file, instead of relative path, if you are going to change the directory in between.
Ok, I have realised that for command affects variables in a strange way and I have to use delayed expansion. Thanks everyone for stating these. I have managed to make it work properly, exactly how I wanted it, here it is :
#echo off
setlocal EnableDelayedExpansion
if not exist statistic_files (
mkdir statistic_files
cd statistic_files
goto skip
)
cd statistic_files
:skip
set "file=%username%-use_tracking"
set "uses=1"
if not exist %file% (
>%file% echo 1
cd ..
)
if exist %file% (
for /f %%G IN (%file%) do set /A uses=%%G+1
>%file% echo !uses!
cd ..
)
you use single >
ie.: echo "Bla" > file.xyz
single > just overwrites anything in the file
try >>
echo "Blabla" >> file.xyz
double >> appends content at the end of the file