Mysterious Echo Rules in Batch - batch-file

I'm making a batch program that is supposed to be like a simplified(-ish) command prompt for commands that I make. I am wondering, though, about this line of batch coding that I have narrowed down as a fatal error (causes the cmd.exe running the program to close) causing line: echo One(1) application is within this folder. When I attempt to go to that section via inputting the command to run that section it states: application was unexpected at this time followed by immediately closing. I have also tried replacing "application" with "program", to no avail. I was wondering: What are all of the mysterious echo parameters/rules. for instance echo text >> name.extention is possible, but typing echo /? does not give you anything besides #echo on/off and echo text.
I believe it is something to do with the (), as in other languages it is used to call a function with the arguments within the (), but I do not understand why it would be this, as it is not running the "One" function because it is inside an echo, meant only to display it literally. Also, I don't believe batch has functions that can be called like this (I've only seen them in VB, lua, java, and C++ [of the ones I use])
If anyone knows why the program failed at this line, and/or (preferably and) all of the other hidden echo rules, please list them out for us; they really need to be known (I've seen so many questions on this website [and others] specifically about the echo command).

Within a block statement where you have if expression (whatever1) else (whetever2) then you need to escape a closing parentheseis ) with a caret ^ thusly : ^)
The escape tells batch that the ) is NOT the termination of the then/else

Related

DOS v6 era batch echo and ver on same line

What I have:
I have a simple batch file with ASCII characters to make a semi graphical menu for MS DOS, drawing a box and placing text with "#echo off" at the start of my file and each line in editor beginning with "echo". I have not implemented color codes, nothing fancy yet.
Is there a way to display the output of the 'ver' command on the same line as something that is 'echo'd?
Basically, I would like to attempt and achieve and output on a line similar to:
|| <---MS-DOS ver. 6.22---> ||
Where the pipes are ASCII characters (ALT-186) and 'escape' the echo to run 'ver' then return to printing the end character (alt-186)
I remember doing something like it YEARS ago when I was fumbling around on my computer, but unlike riding a bike, I have forgotten many tricks I had at the time.
I have Googled for quite some time, the last few days between work and sleep on my free time, but everything is geared toward the Windows XP or newer command line instead of DOS. I have read many articles on batch scripting and while helpful in relearning other tricks, they are all still geared toward newer CLI. I have read up on escaping and for some reason I am just not getting it. Maybe I have spent too much time trying to figure this out on my own and have burned out? Any help or link to the proper articles will be appreciated. Sample code even better as that is how I learned way back then.
For the case of MSDOS-Version exists a simple solution.
echo #prompt $b$b $l--$V--$g $b$b > temp.bat
%comspec% /c temp.bat
This works, because $V will be translated into the MSDOS-Version, for output of other programs it's more complicated.
Attention: It only works inside a batch program, because variable expansion isn't supported on the command line.
On the command line you could use:
echo #prompt $b$b $V $b$b > temp.bat
C:\command.com /c temp.bat
Output: || <--MS-DOS Version 6.22--> ||
In MS-DOS 6.22 it's tricky to output text without line feed.
In the most cases you should build the complete line before you try to output it.
Or you can try predefined files without a linefeed.
Today I do the echo without line feed this way, ...
echo.|set /P =text without CR/LF
... but I can't say if this would have worked in the early nineties.

cmd batch variable behavior is unintuitive

I'm using windows 10, running batch files through the command prompt window.
I can make things work, but I don't know why it works or why I can't do certain things:
set "file_list=a1 a2"
for %%a in (%file_list%) do (
echo %%a.py
)
This little piece of code works. I can build on it, BUT
Q1: I want to change the variable %%a to %%filename... but that doesn't work! I wondered if maybe filename were reserved, so I tried %%fname .
In this case I get the error:
%fname was unexpected at this time.
I can do a set fromm the command line and use a descriptive variable name, but it doesn't seem to work when looping. (I did it with the %file_list% variable above!) So how come I can only use a single character for a loop variable? Is there some way around that?
Q1a. This makes me think that the loop index variable is a different kind of variable that the ones in set commands. Is that correct? If so, is there a link that clearly and concisely explains the difference?
Q2. I notice the loop index variable is %%a, instead of a or %a or %a% . I never would have guessed this. The web sites I've looked at have just said, do this. But I can't see any explanation of why, except that the first percent is an escape. Okay. That doesn't really explain anything. It just means "this is how you do it." The error message when I use one percent sign is interesting.
set "file_list=a1 a2"
for %a in (%file_list%) do (
echo %a.py
)
"file_list) was unexpected at this time."
So I can vaguely see that maybe something isn't being escaped correctly. Why does that % in the %a need to be escaped, so it becomes %%a ?
A for meta-variable must consist of % (in Command Prompt) or %% (in a batch file) and a single character (case-sensitive letter), like %a or %%a. You cannot define %filename or %%filename.
Loop variables only exist within the respective for loop. Do not confuse such loop variables with normal environment variables, like %TEMP%, for example, which are available globally.
There are these things marked by %-signs:
%-escaping (only applicable for batch files), so %% denotes one literal %-sign;
command line arguments/parameters (only applicable for batch files, obviously), like %1;
immediately expanded environment variables*, like %TEMP%;
for meta-variables, like %a (in Command Prompt) or %%a (in batch files), which are specific to the for command, so they do not exist outside of the related loop context;
%-escaping (1.) happens before expanding for meta-variables (4.), hence actually the for command receives a loop variable like %a.
Then environment variables (3.) are treated differently in Command Prompt and in batch files: the former keeps undefined variables literally, the latter removes them.
The detailed parsing rules can be found in this post, which have been implemented by Microsoft (or IBM?) that way in order to be able to distinguish between the different %-things, so at the end it was their decision, therefore you have to ask them for the exact reason…
*) There is also something like delayed environment variable expansion, but this uses !-signs to mark the variables, like !TEMP!, and this is something that happens after all the %-sign expressions have been parsed.

Why CALL command cannot execute a command enclosed with brackets?

The behavior is the same both in the command prompt and in a .bat file.
#echo off
:: the echo that will never be
call(echo echo echo)
:: its the same with non-cmd internal commands
call ( notepad.exe )
:: and even with a code that should fail
call(gibberish)
:: moreover the execution is successful
call (gibberish)&&echo %errorlevel%
:: though there's some parsing done
call(this will print call help because contains "/?"-/?)
:: and will detect a wrong syntax of some parsed-before-execution commands
call ( if a==)
:: and this too
call (%~)
:: same with labels
call(:label)
call(:no_label)
:label
According to the microsoft documentation:
Using multiple commands and conditional processing symbols - (command1 & command2) Use to group or nest multiple commands.
Here's the CALL help page. - So nothing that indicates that syntax is illegal as long as redirection symbols are not used.
More bugs in CALL parser - here
Enter in command prompt window cmd.exe /? and view the last paragraph on last displayed page which belongs to file completion:
The special characters that require quotes are:
<space>
&()[]{}^=;!'+,`~
So all strings with one of those special characters must be enclosed in double quotes.
As file completion is done by cmd.exe, it can be expected that parsing of a line is done similar for entire command lines as well as for lines in batch files.
It is possible to use for example
call "( Notepad.exe )"
which of course results in an error message as Windows will not find an executable with name ( Notepad.exe ) to call.
I have learned just an hour ago here from an interesting comment by phd443322 that cmd.exe parses lines different than all other applications.
As this question will be read most likely never by a programmer with access to source code of cmd.exe, we will never get the answer why those example commands are interpreted as we can see on executing them.
I think the only commands that interpret parenthesis as delimiters for an expression are FOR, IF and SET /A
Adding new lines may look promising:
Call (
Echo Hello
)
but if you try running just
Call (
what that is actually doing is attempting to run the command (.cmd
If you try creating a batch file called (.cmd, then you can in fact call it with
Call "("
The quotes are required because ( is a 'special' character.
In PowerShell parenthesis can be used in many more places which can be confusing if you switch between CMD and PowerShell
(2+3)
Will return 5 in PowerShell but just gives an error in CMD.

To "Call" or "Not to Call" a batch file?

If from inside a bat file you called another batch file but still had a few remaining operations to complete, how can you make sure that the call to first bat file will after completion or error, will return to the file that called it in the first instance?
Example:
CD:\MyFolder\MyFiles
Mybatfile.bat
Copy afile toHere
or
CD:\MyFolder\MyFiles
CALL Mybatfile.bat
COPY afile toHere
What is the difference between using CALL or START or none of them at all? Would this have any impact on whether it would return for the results of the copy command or not?
As others have said, CALL is the normal way to call another bat file within a .bat and return to the caller.
However, all batch file processing will cease (control will not return to the caller) if the CALLed batch file has a fatal syntax error, or if the CALLed script terminates with EXIT without the /B option.
You can guarantee control will return to the caller (as long as the console window remains open of course) if you execute the 2nd script via the CMD command.
cmd /c "calledFile.bat"
But this has a limitation that the environment variables set by the called batch will not be preserved upon return.
I'm not aware of a good solution to guarantee return in all cases and preserve environment changes.
If you really need to preserve variables while using CMD, then you can have the "called" script write the variable changes to a temp file, and then have the caller read the temp file and re-establish the variables.
call is necessary for .bat or .cmd files, else the control will not return to the caller.
For exe files it isn't required.
Start isn't the same as call, it creates a new cmd.exe instance, so it can run a called batch file asynchronosly
The `CALL' statement was introduced in MS-DOS 3.3
It is used to call other batch files within a batch file, without aborting the execution of the calling batch file, and using the same environment for both batch files.
So in your case the solution is to use CALL
Okay, I actually didn't even really think about the fact that if you call a batch (regardless of the 'type', i.e. '.bat', or '.cmd') that it won't return if you don't use call.
I've been using call myself though for a different reason that I am actually pretty surprised that no one else has brought up. Maybe I missed it. MAYBE I'M THE ONLY ONE IN THE WORLD WHO KNOWS!! :O
Probably not, but I'm going to drop this knowledge off here because it's super useful.
If you use call you can use binary logic operators to decide how to proceed based on the ERRORLEVEL result. In fact, I always was flabbergasted on how && and || existed in DOS and COULDN'T be used this way. Well, that's why.
The easiest way to test this is to create a return.cmd with notepad, or from the command prompt like so:
c:\> type con >return.cmd
You will now notice the cursor goes down to the next line and hangs. Enter:
#exit /B %1
And then hit ENTER, and then CTRL-Z and that file will be created. Good! You may now feel free to try the following two examples:
call return.cmd 0 && echo Huzzah! A Complete Success! (Or cover up...)
call return.cmd 123 || echo Oops! Something happened. You can check ERRORLEVEL if you want the tinest amount of additional information possible.
So what? Well, run them again with the 0 and the 123 swapped and you should see that the messages DON'T print.
Maybe this multi-line example will make more sense. I use this all the time:
call return.cmd 0 && #(
echo Batch says it completed successfully^^!
) || #(
echo Batch completed, but returned a 'falsey' value of sort.
call echo The specific value returned was: %ERRORLEVEL%
)
(Note the 'call' in the || section before the second 'echo'. I believe this is how people got around not having delayed expansion back in the day. If you DO have delayed expansion enabled (via. setlocal EnableDelayedExpansion inside a batch OR launch a command prompt with cmd /v:on then you can just do !ERRORLEVEL!.)
... This is where I have to apologize and say if you have if ERRORLEVEL trauma in your past you should stop reading. I get it. Trust me. I thought about paying someone on fiverr to remotely type this for me, but for completeness sake I'm just going to take one for the team and mention that you can also do the following to check errorlevel:
if ERRORLEVEL 123 #echo QUICK! MOTHERS, COVER YOUR CHILDREN'S EYES! FINGERS ARE BEING UNDONE! :'(
If you've never typed that before then GOOD! You will live longer without having to read up why exactly you aren't getting the results you expect. Cruel is the word you're looking for, not 'quirky'.
The important part that I really want to get across however is that if you try this and DON'T use 'call' it will ALWAYS execute the 'true' branch. Try it for yourself!
If I'm missing something, or you know a better way to do this, please let me know. I love learning stuff like this!
Additional information I mentioned:
I have known for quite some time that you can put redirects BEFORE commands like so:
>nul echo. This won't be displayed!
But I accidentally discovered the other day by being a dumdum that you can apparently also do:
echo A B>file.txt C
And was REALLY surprised to find a file.txt which consisted of "A B C". It appears yo can place them ANYWHERE, even inside the command. I've never seen anyone do this, nor mention it, but I HAVE seen people mention that you can prefix a line with them.
Maybe it's a bug exclusive to Windows 10 or something. If you have another version and wanna try it out and let me know I'd be interested in what you find out.
Stay nerdy!

Stupid Batch File Behavior. Tries to execute comments

I have tried prefixing lines with semicolons, 'REM', etc.. but no matter what when I run my batch file I keep getting "unknown command REM whatever"
"REM test" It is not recognized, and it is windows vista. I simply get "rem" output back to my console.
That's entirely normal behavior. Batch files are simply sequences of commands that are run one after another. So every line will get output to the console as if it were typed there.
H:\>echo rem test > test.cmd
H:\>test
yields the output
H:\>rem test
as if I typed rem test directly to the console.
You can suppress this by either prefixing the line with #:
#rem test
or by including echo off in the batch file:
#echo off
rem test
If I put ":: test" and execute it I get back "Test".
Can't reproduce here.
If I put "; test" it recursively executes itself
A semicolon at the start of the line seemingly gets ignored.
If you're talking about cmd.exe batch files under Windows, you can use:
rem this method or
:: this method.
For bash and a lot of other UNIX-type shells, you use:
# this method.
I'm pretty certain you're not using cmd.exe since that would give you an error like:
'rem' is not recognized as an internal or external command,
operable program or batch file.
rather then:
Unknown command ...
If you are using a UNIX-type shell, the # character is almost certainly what you're after. If you let us know exactly the shell you're using, we can probably help out further.
you probably created an UNICODE file. These files contain 2 bytes header named BOM
which is not shown by any editor but cmd attempts to execute them and fails.
To make sure this is indeed an issue: type any other command at the very beginning
of your file and see it throws the same error - for example #echo test
To fix it, just create a new plain text file and copy content of the original file there.
then remove the original file and replace it by the newly created one.
In my case the problems are line endings. Somehow Maven or the Jenkins pipeline running on a Linux machine changed the line endings from Windows style (CR LF) to Unix style (LF). Changing them back solves the issue for me.

Resources