I was having an issue with a script so I went to ss64.com like I usually do to see what gems I might find to help me.
In looking at the page for the CALL command I came across this line:
Redirection with & | <> also does not work as expected.
However, that page and just about anywhere else I've looked does not explain how it works unexpectedly. I know that the | can do some unexpected things in general but I don't know about the others.
What is this unexpected function? Does it depend on how you use the CALL command (calling a label vs script)?
I suppose Rob think of things like this:
call :func | more
exit /b
:func
echo line1
echo line2
exit /b
Or something like
setlocal EnableDelayedExpansion
set var=Line2
( echo Line1
echo !var! ) | more
For this the explanation can be found at(you mentioned this)
SO:Why does delayed expansion fail when inside a piped block of code?
But redirection in the first step works with CALL as expected (and the other characters)
call echo Hello > output.txt
But if you try to use any of the special characters in the second expansion of the CALL command, the complete command will not be executed.
About the special effects of CALL for the batch parser is described at
SO:How does the CMD.EXE parse scripts? (especially phase 6)
set "myCmd=echo 1 & echo 2"
call %%myCmd%%
The double %% have the effect that the first expansion results in %myCmd% and the real expansion of the content will be done not until the second run of the parser, so the & have to be interpreted in the call context.
And also this will result in nothing (because of the parenthesis)
set "myCmd=echo Line1"
call (%%myCmd%%)
But this will obviously call a batch file named 1.bat
echo echo Hello > 1.bat
set "myCmd=echo Line1 && echo Line2"
call (%%myCmd%%)
echo End
This was discussed at dostips:CALL me, or better avoid call
Currently I suppose, CALL works somehow with tokens, but can't handle them in the correct way.
Related
I need to put this string >>> (it's just some handy copypaste) to clipboard.
Since > is a special cmd-character, I'm using ^ before it to mean all special characters literally.
So far, my Batch code looks like this
(&& pause here is used to see debug messages):
echo ^>^>^> && pause
echo ^>^>^>>"%~dp0foo.txt" && pause
echo foo|clip && pause
echo ^>^>^>|clip && pause
1st line works perfectly (not affecting clipboard though).
2nd line works perfectly (not affecting clipboard either though).
3rd line works perfectly (not using the symbols I need though).
4th line returns >> was unexpected at this time error.
Obviously, I need some syntax tips.
It's a bit tricky, because the pipe creates two new cmd instances (like #aschipf mentioned).
You could use a variable and delayed expansion
set "var=>>>"
cmd /v:on /c "echo(!var!"| clip
Or you can use FOR-variable expansion
set "var=>>>"
( FOR %%X in ("%%var%%") DO #(echo(%%~X^) ) | clip
Okay, I figured an almost decent workaround:
echo ^>^>^>>"%~dp0foo.txt"
type "%~dp0foo.txt"|clip
del "%~dp0foo.txt"
puts >>> into foo.txt right next to your Batch
(it also accounts for spaces in path to the file via ").
returns >>> as a content from foo.txt and puts it into clipboard.
deletes foo.txt right away.
Still hoping to meet a proper-syntax-based solution.
i was toying around with cmd a bit and wanted to write a little application which involves a simple feature to read a counter from a txt file, then work with it and at the end raise the counter by one.
set /p x=<file.txt
...
set /a y=x+1
echo %y%>file.txt
Problem is it always returns "ECHO ist eingeschaltet (ON)." which translates to ECHO is turned on (ON) for some reason. Could somebody please explain where it comes from and how to fix it? I dont need anything fancy. I just want it to work and know where my mistake is.
At first, I want to show you how your echo command line should look like:
> "file.txt" echo(%y%
Here is your original line of code again:
echo %y%>file.txt
The reason for the unexpected output ECHO is on./ECHO is off. is because the echo command does not receive anything to echo (type echo /? and read the help text to learn what on/off means). Supposing y carries the value 2, the line expands to:
echo 2>file.txt
The number 2 here is not taken to be echoed here, it is consumed by the redirection instead; according to the article Redirection, 2> constitutes a redirection operator, telling to redirect the stream with the handle 2 (STDERR) to the given file. Such a handle can reach from 0 to 9.
There are some options to overcome that problem:
inserting a SPACE in between the echoed text and the redirection operator:
echo %y% >file.txt
the disadvantage is that the SPACE becomes part of the echoed text;
placing parentheses around the echo command:
(echo %y%)>file.txt
placing the redirection part at the beginning of the command line:
>file.txt echo %y%
I prefer the last option as this is the most general and secure solution. In addition, there is still room for improvement:
quote the file path/name to avoid trouble in case it contains white-spaces or other special characters;
use the odd syntax echo( to be able to output everything, even an empty string or literal strings like on, off and /?;
> "file.txt" echo(%y%
Hint:
To see what is actually going on, do not run a batch file by double-clicking on its icon; open a command prompt window and type its (quoted) path, so the window will remain open, showing any command echoes and error messages. In addition, for debugging a batch file, do not put #echo off on top (or comment it out by preceding rem, or use #echo on) in order to see command echoes.
Echo on means that everything that is executed in the batch is also shown in the console. So you see the command and on the following line the result.
You can turn this off with the echo off command or by preceding a # sign before the command you want to hide.
so
::turns of the echo for the remainder of the batch or untill put back on
::the command itself is not shwn because off the #
#echo off
set /p x=<file.txt
...
::the following won't be shown regardless the setting of echo
#set /a y = x+1
echo %y% > file.txt
EDIT after first comment
because your command echo %y%>file.txt doesn't work, you need a space before the > symbol, now you get the result of echo which gives you the current setting of echo
here a working sample, I put everything in one variable for sake of simplicity.
echo off
set /p x =< file.txt
set /a x += 1
echo %x% > file.txt
So I wrote a batch that has some code to check how many times it has been run by reading a textfile and then writing back into that textfile the new, increased number.
#ECHO OFF
for /f "delims=" %%x in (TimesRun.txt) do set Build=%%x
set Build=%Build%+1
#echo Build value : %Build%
echo %Build%>>TimesRun.txt
Pause
That does append the textfile allright, but it adds "1+1" to it. Silly me! I forgot to use the /a switch to enable arithmetic operations! But when I change the code accordingly...
#ECHO OFF
for /f "delims=" %%x in (TimesRun.txt) do set Build=%%x
set /a Build=%Build%+1
#echo Build value : %Build%
echo %Build%>>TimesRun.txt
Pause
... something funny happens: Instead of appending my file, ECHO is off. gets written on the console. Now, I know that this usually happens when ECHO is used without text or with an empty variable. I have added the first #echo Build value : %Build% specifically to see whether the variable Build is empty or not; it is not, and the calculation was carried out correctly.
I already figured out that
>>TimesRun.txt (echo %Build%)
does bring the desired result. I still do not understand why
echo %Build%>>TimesRun.txt
does not, however. What am I missing?
You are unintentionally specifying a redirection handle.
Redirection allows you to specify a certain handle that defines what is to be redirected:
0 = STDIN (keyboard input)
1 = STDOUT (text output)
2 = STDERR (error text output)
3 ~ 9 = undefined
For the input redirection operator <, handle 0 is used by default; for the output redirection operators > and >>, the default handle is 1.
You can explicitly specify a handle by putting a single numeric figure in front of the redirection operator; for instance 2> defines to redirect the error text output.
In your echo command line you are doing exactly this unintentionally, when %Build% is a single numberic digit, like 1 for example:
echo 1>>TimesRun.txt
To avoid that, you have the following options:
To reverse the statement so that the redirection definition comes first:
>>TimesRun.txt echo %Build%
This is the most general and secure way of doing redirections.
To enclose the redirected command in parentheses:
(echo %Build%)>>TimesRun.txt
This also works safely.
To put a SPACE in front of the redirection operator:
echo %Build% >>TimesRun.txt
This works too, but the additional SPACE is included in the output of echo.
See also this great post: cmd.exe redirection operators order and position.
Batch file redirection can be customized to specify where you're outputting to.
command 1>file.txt redirects the output of STDOUT to file.txt
command 2>file.txt redirects the output of STDERR to file.txt
Your build value was 1, so you inadvertently told CMD to send the output of echo to TimesRun.txt - when you run echo by itself, it prints it's status (ON or OFF).
You also could have said echo %Build% >>TimesRun.txt and the space would prevent the value of Build from being treated as a redirection command.
The Microsoft article Using command redirection operators explains the 3 standard handles and how to redirect them to another handle, command, device, file or console application.
Redirection of output written to handle 1 - STDOUT - to a file should be done with just
using > ... create file if not already existing or overwrite existing file, or
using >> ... create file if not already existing or append to existing file.
The redirection operators are usually appended at end of a command line. But this is problematic in case of using command ECHO and the string output to STDOUT ends with 1 to 9.
One of several solutions is to specify in this case the redirection at beginning of the command line:
#for /F "delims=" %%x in (TimesRun.txt) do #set Build=%%x
#set /A Build+=1
#echo Build value : %Build%
>>TimesRun.txt echo %Build%
Executing this small batch file without #echo off at top from within a command prompt window shows what Windows command processor executes after preprocessing each line with text file TimesRun.txt containing currently the value 0 or does not exist at all.
echo 1 1>>TimesRun.txt
It can be seen that Windows command interpreter moved the redirection to end of line with inserting a space and 1 left to >>.
With above batch code the line with >> really executed after preprocessing is:
echo 2 1>>TimesRun.txt
Specifying the redirection at end with 1>>, i.e. use in the batch file
echo %Build%1>>TimesRun.txt
is also no good idea as this would result on first run in executing the line:
echo 11 1>>TimesRun.txt
So 11 is written into the file instead of 1. This wrong output could be avoided by inserting a space before >> or 1>>, i.e. use one of those two:
echo %Build% >>TimesRun.txt
echo %Build% 1>>TimesRun.txt
But then the space after %Build% is also written into the file as really executed is:
echo 1 1>>TimesRun.txt
The trailing space would be no problem here, but should be nevertheless avoided.
Note: On using arithmetic operations, i.e. set /A ... any string not being a number or operator is automatically interpreted as variable name and the current value of this variable is used on evaluating the arithmetic expression. Therefore after set /A with environment variable names consisting only of word characters and starting with an alphabetic character as usually used for environment variables no %...% or !...! must be used inside the arithmetic expression. This is explained in help of command SET output into console window on running set /? within a command prompt window.
In one of my scripts, I need to use variables that contain parenthesis inside IF statements, but either the string is missing a closing parenthesis or the script exits prematurely with * was unexpected at this time (not actually an asterisk), depending on the scenario.
Example
#echo off
SET path=%programFiles(x86)%
echo Perfect output: %path%
IF NOT "%path%" == "" (
REM Variable is defined
echo Broken output: %path%
)
pause >nul
Output
Perfect output: C:\Program Files (x86)
Broken output: C:\Program Files (x86
I think/know that this is because it thinks the closing parenthesis in C:\Program Files (x86) is the end of the IF statement and it exits before the echo is complete.
Is there a simple way to cirumvent this? Preferably without resorting to
single-line IF statements, as I need to run more than one line of code within them,
copious amounts of GOTOs, as it's not practical,
SETLOCAL EnableDelayedExpansion and using !path! instead of %path%, as I recall reading somewhere that that method doesn't work consistently across OSs.
If not, I'll happily accept the most reliable solution offered, whatever it is.
(The scenario isn't up for debate. This is just a refined, concentrated example of the problem. The structure needs to be like this, as it is in my actual script, for reasons I won't go into. It's besides the point and it'll just confuse things and distract from the actual issue.)
First off - you should never use the PATH variable for your own use. It is a reserved environment variable. Using it for your own purposes can break your scripts.
The simplest solution really is to use delayed expansion. As long as your platform uses CMD.EXE then you have access to delayed expansion.
But there is a relatively easy way to make it work without delayed expansion. You can use disappearing quotes. The quote exists at parse time as the name of a FOR variable while the command is parsed. It expands to nothing before execution time.
#echo off
SET mypath=%programFiles(x86)%
echo Perfect output: %mypath%
IF NOT "%mypath%" == "" (
REM Variable is defined
for %%^" in ("") do echo fixed output: %%~"%mypath%%%~"
)
pause >nul
EDIT - When to use delayed expansion: Response to comment
I generally only use delayed expansion when it is needed (or more precisely, when it is advantageous). That being said, I usually find it advantageous in some portion of my batch code.
Major Advantages
Inside a code block in order to see changes to a variable within the block
When dereferencing the name of a variable. If a variable name is passed in as a parameter, the value of the variable can be gotten via delayed expansion: echo !%1!
When using variables as arguments to search and replace or substring operations: echo !var:%search%=%replace%!, echo !var:%start%,%len%!.
Whenever I need to expand the value and not worry about special characters within it needing escaping or quoting: set "var=A&B" & echo !var!
There are other methods to do the above (except the last), but delayed expansion is the easiest, most efficient (fastest to execute), and most reliable option.
Major Disadvantage
Any FOR variable that contains ! in its value will be corrupted when it is expanded if delayed expansion is enabled. I frequently toggle delayed expansion on and off within a FOR loop to get around the problem.
It is not good for executing a "macro" (executing code contained within a variable value) because many important phases of command parsing take place prior to the delayed expansion. So many batch features are unavailable to "macros" that are executed via delayed expansion.
my suggestion is :
if (condition_TRUE) goto goodbye_parenthesis_BEGIN
goto goodbye_parenthesis_END ----- line when previous condition is FALSE ----
:goodbye_parenthesis_BEGIN ----- line when previous condition is TRUE ----
...
variable treatment
...
:goodbye_parenthesis_END
The ) from the resolved variable in your echo statement is prematurely closing the IF block.
Ordinarily, you could fix that by escaping the ) with ^), but you can't modify the environment variable to resolve to C:\Program Files (x86^).
You can prevent this issue by surrounding the variable with quotes.
As a simpler example:
> SET bad=a)b
> IF 1 == 1 ( ECHO %bad% )
b was unexpected at this time.
> IF 1 == 1 ( ECHO "%bad%" )
"a)b"
As others already pointed out, the unescaped and unquoted closing parenthesis ) unintentionally ends the parenthesised if block.
Besides escaping, quotation, delayed expansion and "disappearing quotes", there are the following further options:
Use a for meta-variable on the quoted value and remove the quotes by the ~-modifier:
#echo off
set "PATH=%ProgramFiles(x86)%"
echo Perfect output: %PATH%
if not "%PATH%" == "" (
rem Variable is defined
for %%P in ("%PATH%") do echo Unbroken output: %%~P
)
pause > nul
Use the call command to initiate another variable expansion phase, together with doubled (escaped) %-symbols:
#echo off
set "PATH=%ProgramFiles(x86)%"
echo Perfect output: %PATH%
if not "%PATH%" == "" (
rem Variable is defined
call echo Unbroken output: %%PATH%%
)
pause > nul
Do escaping by sub-string substitution, which happens before ^-escaping is detected:
#echo off
set "PATH=%ProgramFiles(x86)%"
echo Perfect output: %PATH%
if not "%PATH%" == "" (
rem Variable is defined
echo Unbroken output: %PATH:)=^)%
)
pause > nul
Forgive me if I'm reading this wrong, but isn't the "NOT" causing control to enter the bracketed if and run the broken output?
what about:
#echo off
echo Perfect output: %programFiles(x86)%
IF NOT "%programFiles(x86^)%" == "" (
REM Variable is defined
echo Broken output: %programFiles(x86)%
)
pause >nul
?
I have a batch-file program that gets called from another batch-file so I can redirect error output to a file. When you run the commmand START /B "C:\Some\Script" 2>"C:\Some\Log.log" Is the redirection recognized as an argument?
Just a question with no real usefulness (at least that I can tell), just asking out of curiousity.
No, it doesn't. Try using this as your test script to verify:
set argC=0
for %%x in (%*) do Set /A argC+=1
echo %argC%
In that file, argC is the number of arguments. (Source: Wikibooks, via this answer.)
Ben is correct - redirection is not counted as an argument. The redirection is handled before the script is called.
As an alternative to counting the args as a test, you can simply look at the args that have been passed in to your script
echo %*
Another option when you use START or CMD /C is to echo %CMDCMDLINE% to see the exact line that was used to launch CMD (including the parameters). It is really handy sometimes to help diagnose parsing, escape and expansion issues. This is also handy when the instantiation of CMD is implicit such as with FOR /F %%A IN ('command') or command1|command2 (each side of the pipe gets its own CMD session).
I'm curious why you are using START /B script 2>logFile instead of CALL script 2>logFile? They both should work, but I think the 2nd option is simpler and more efficient.