1

What I'm doing

I'm trying to simulate exception handling in batch. I know theres a forum post on DosTips that goes extensively into this topic and even provides a script that does some of what you would expect. Since this is a work project I can't use their solution so I tried my own hand at this.

How I'm trying to do it

I've tried to implement what I'll call the "poor man's try catch" which is really just a FOR loop that runs batch commands line by line and if the errorlevel is above 0, the line is caught by the loop in an IF from the DO clause. For example, I tried writing this in batch:

@echo OFF
SET /A "LineCount=1"
FOR /F "tokens=* delims=" %%A IN ('SET "VarATest^=something" ^^ SET "VarBTest^=somethingelse" ^^ SE "VarCTest^=syntaxerror"') DO (SET /A "LineCount=1+!LineCount!" & IF !ERRORLEVEL! EQU 1 (echo errorlevel was: "!ERRORLEVEL!" and line was "!LineCount!") ELSE (echo line worked))
pause 

Basically what the above is supposed to do is, initialize a LineCount variable, run the three batch commands in the IN clause (seperated by the ^ line enders) and for each line, increment the LineCount and check to see if the line caused an errorlevel of anything higher than 1 (!ERRORLEVEL! EQU 1 has that odd behavior). The third line has an obvious syntax error, the SET is spelled SE which should cause an errorlevel higher than 0. The point is demonstration, this could be refined but right now its simply for proof of concept.

the problem

Unfortunately when I ran this code it didn't run anything as expected, instead all I get is the pause output but no errors. I don't beleive its running the IN clause line by line as I expected so my question is this:

A. Is it possible to run batch commands line by line in the IN clause of a FOR loop to simulate a try catch? Obviously this means using delayed expansion and escape characters more frequently, but it'd be cool to have this rudimentary functionality.

B. If it is possible then what is the proper way to make the input of the IN clause be raw text but also multiline at the same time. For example, the IN clause right now is:

(SET "VarATest^=something" ^^ SET "VarBTest^=somethingelse" ^^ SE "VarCTest^=syntaxerror")

which I want to be functionally similar to the regular batch code of:

SET "VarATest=something"
SET "VarBTest=somethingelse"
SE "VarCTest=syntaxerror"

Where the third line obviously causes an error. I don't want to use another file if possible so no tempfiles that hold the try block code. Would I use the ^ line ender? I tried ^^ thinking I needed to escape the first line ender. I also tried newlines where the ^^ were but that didn't work either. I tried modifying delims to try and split the commands that way, maybe run them each that way but still I get no output.

EDIT:

edited out noise, tried answer provided which was extremely helpful but unfortunately did not yeild the solution however after tinkering I came up with this:

@echo OFF && setlocal enableextensions enabledelayedexpansion 

SET lf=^&echo/^^



SET /A "LineCount=0"
FOR /F "tokens=*" %%A IN ('call!lf!SET "VarATest^=something"!lf!SET "VarBTest^=somethingelse"!lf!SET "VarCTest^=syntaxerror"!lf!') DO (SET /A "LineCount=1+!LineCount!" && echo ER: "!ERRORLEVEL!" or "%ERRORLEVEL%" A: "%%A" && IF !ERRORLEVEL! NEQ 0 (echo errorlevel was: "!ERRORLEVEL!" and line was "!LineCount!") ELSE (echo line worked = !LineCount!))
pause >nul 
echo/!LineCount! && pause >nul 

now this is mostly the answer provided with a few caveats. Adding in the "tokens=*" part actually gets the full SET line. The answer provided the linefeed mechanism. With experimentation I observed that a call!lf! was needed BEFORE the first command in the "string". I have absolutely no idea why but it doesn't work any other way because the FOR loop seems to want to skip the first argument. I got a printout of each command line in the string through %%A. fantastic! The only problem though, is that errors are not caught when they should be. ERRORLEVEL does not seem to change when I change the last command string from

SET "VarCTest^=syntaxerror"

to:

ST "VarCTest^=syntaxerror"

or any similar variations that would cause an error to be thrown. The issue is the SET commands are never actually ran. Running an echo of them after the FOR loop reveals this. I'm going to try and get them running

Community
  • 1
  • 1
Redacted
  • 565
  • 3
  • 19
  • 4
    Sorry. I just didn't read your question. Is _too long_... **`:(`** But answering the topic title: _"Batch exception handling using a FOR loop with multiline IN running the commands to be TRYed. Is it possible?"_. The short answer is: No... – Aacini Dec 06 '18 at 15:30

3 Answers3

1

If I understood well your question:

enter image description here

@echo off & setlocal EnableDelayedExpansion & color 0a
for /f %%a in ('forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo 0x1B"') do set "Esc=%%a"
SET lf=^&echo/^^

SET /A "LineCount=1"
FOR /F %%A IN ('SET /p "VarATest=something"^<nul!lf!SET /p "VarBTest=somethingelse"^<nul!lf!SET /p "VarCTest=syntaxerror"^<nul!lf!') DO (
    SET /A "LineCount=1+!LineCount!"
    IF "!ERRORLEVEL!" == "1" (
        echo errorlevel was: "!ERRORLEVEL!" and line was "!LineCount!"
    ) ELSE (
        echo line worked = !LineCount!
    )
    echo/!Esc![4m!Esc![31mError on "!LineCount!" Error code !ERRORLEVEL!!Esc![0m 
)

pause >nul 
echo/!LineCount! & pause >nul 

Note: After SET lf=^&echo/^^, the 2 line below are needed to do the "fake break line".

double-beep
  • 3,889
  • 12
  • 24
  • 35
It Wasn't Me
  • 2,145
  • 3
  • 15
  • 22
  • 1
    Unfortunately this didn't solve the problem, but it was very very helpful in getting closer to the solution. One small question: In your code, you changed my SET commands into SET /p commands. What does that really do? From what I know it sets the command to a prompt for user input and then redirects it to null which makes the prompt not display. I've made an edit to my code based on your answer. – Redacted Dec 06 '18 at 17:36
  • Well, set **/p** + ** – It Wasn't Me Dec 06 '18 at 17:47
  • 2
    @Redacted: `set /p var=prompt – Stephan Dec 17 '18 at 17:51
1

So I finally got the code to this point. It took a ton of experimentation, and a little trouble with escaping double quotes and resetting the errorlevel, but the code I got to work was this:

@echo OFF && setlocal enableextensions enabledelayedexpansion 

SET lf=^&echo/^^



SET /A "LineCount=0"
FOR /F "tokens=* delims=" %%A IN ('call!lf!ST ""VarATest^=something""!lf!SET ""VarBTest^=somethingelse""!lf!SET ""VarCTest^=syntaxerror""!lf!') DO (SET /A "LineCount=1+!LineCount!" && call :RunMyCommandSubroutine "%%A")
echo A: "!VarATest!"
echo A: "%VarATest%"
echo B: "!VarBTest!"
echo B: "%VarBTest%"
echo C: "!VarCTest!"
echo C: "%VarCTest%"
pause >nul
goto:EOF

:catchsubroutine
echo [43;97mError on "!LineCount!" Error code !ERRORLEVEL![0m 
goto:EOF

:RunMyCommandSubroutine
SET "StringImUsing=%~1"
SET "StringImUsing=%StringImUsing:""="%"
%StringImUsing% >nul 2>&1
IF !ERRORLEVEL! NEQ 0 (echo [43;97mLine was %StringImUsing%[0m && call :catchsubroutine) ELSE (echo line worked = !LineCount!)
(call )
goto:EOF

how I did it

So to test it, you can create a typo in the SET command strings in the IN portion of the for loop and see that that specific "line" is marked in Yellow as an error (e.g. try making SET into ST). You're all probably wondering what the (call ) is for in the RunMyCommand subroutine. Its for resetting the ERRORLEVEL to 0 but not permanently. I was falling for the mistake of SETing the errorlevel myself (using SET /A "ERRORLEVEL=0"), which creates a copy and doesn't use the system variable errorlevel, which I observed as it remained 0 after I 'reset it' even when an error occurred. So after fixing that, and escaping the double quotes around the SET argument with "", I also needed to make a subroutine because putting the check for errorlevel in the DO of the IF caused the check to be one line too late, because when the FOR loop gets an error in the DO clause, it seems to stop everything else and move on to the next argument. I also figured out that you can just use the command string itself to 'run' the command (e.g. if I have the command in %%A just type %%A on a seperate line and the command will run normally). So we have a proper check, a proper errorlevel reset, a proper way to run our multiline input strings, and I even put in some ANSI codes to make the UI look nice.

Future reference

All in all pretty cool! I have no idea if this can work with more complicated examples like nested IFs FORs or other weird commands, but It should check for any error that causes the errorlevel to be anything but zero. I also have a catch subroutine that allows for cleanup almost immediately after the erroring line is ran. This allows for some cleanup operations. So hey! It turns out it was (sorta) possible!

Still big thanks to kapputz. His answer wasn't the solution but it helped ALOT with so many different things I needed to understand.

Redacted
  • 565
  • 3
  • 19
  • ,, This was splendid!! and i´ll spend some time to understand all!! – It Wasn't Me Dec 06 '18 at 20:51
  • one question: what system you run this code working? – It Wasn't Me Dec 06 '18 at 20:54
  • I ran it on a win10 VM 64 bit. Do you need the specific windows version? Its just a standard text file converted to a batch by file save as so it isn't anything special. I do know that it has to be Win 10 for the ANSI to work, previous versions of Windows didn't have native ANSI support – Redacted Dec 06 '18 at 21:19
  • I presume win 10 because have ascii/ansi code support, wanted is confirm this info! Thx! – It Wasn't Me Dec 06 '18 at 21:22
  • This part "echo [43;97mError on "!LineCount!" Error code !ERRORLEVEL![0m " won't work on my system, by add forfiles trick to set escape character to a var, yEp! see new picture! – It Wasn't Me Dec 08 '18 at 15:22
1

This is not really an answer to this question, but it is too large to fit in a comment...

  • To run several commands in a Batch file you must separate them by & character (or its variants). This is a very basic Batch-file point.
  • The ^ character is not a "line ender" (where did you read that?). It is used to escape the next character. When the next character is the end of the current line, then the current line "continues" into the next one (with certain restrictions).
  • The behavior of for /F command is run all commands placed in IN clause first and, after the last command had ended, start to iterate the commands placed in the for-body using the lines generated in IN part.
  • The method that for /F uses to execute the commands in IN clause is via a child cmd.exe process. There is no way to directly pass any errorlevel value from this cmd.exe to the commands placed in the for-body, excepting via a text file.
  • The child cmd.exe is executed in the command-line context, NOT in the Batch-file context, so a special management is required in order to modify the behaviour of the commands placed in the IN part.
  • All this info is already published in many Batch-file related sites. Anyone could search for it and find it, if he/she have interest...

EDIT: I added code that supposedly answers the question.

I really don't understand what is the purpose of this topic and I don't understand many (almost all) of the descriptions you use to explain what you are doing... In despite of your extensive explanations (with a lot of non-technicall words and distracting "chapter titles"), you had not clearly described what you want as final result and the way you want go get such a result. For this reason I can only guess... So I guess you are looking for something similar to this:

@echo off
setlocal EnableDelayedExpansion

SET /A "LineCount=0"
FOR %%A IN ( "SET ""VarATest=something"""
             "ST  ""VarBTest=syntaxerror"""
             "SET ""VarCTest=somethingelse""" ) DO (
   SET /A "LineCount+=1"
   SET "StringImUsing=%%~A"
   SET "StringImUsing=!StringImUsing:""="!"
   !StringImUsing! >nul 2>&1

   IF !ERRORLEVEL! NEQ 0 (
      echo Line was !StringImUsing!
      echo Error on "!LineCount!" Error code !ERRORLEVEL!
      VER >nul
   ) ELSE (
      echo line worked = !LineCount!
   )

)

echo A: "%VarATest%"
echo B: "%VarBTest%"
echo C: "%VarCTest%"

VER is the command used to reset ERRORLEVEL to zero as described below Table 4 at this answer. It is a much simpler and standard command that the strange (call ) trick...

Aacini
  • 59,374
  • 12
  • 63
  • 94
  • Sorry, when I said line ender I should have been more specific: https://stackoverflow.com/questions/69068/long-commands-split-over-multiple-lines-in-windows-vista-batch-bat-file is what I was talking about. As for the other points, I knew about the & character but that wasn't relevant to my problem, the child process thing is interesting and the FOR loop behavior of the IN clause is something I already knew. I was able to get the errorlevel of the commands by running the command string in a subroutine instead of directly in the FOR loop. I appreciate the info! – Redacted Dec 06 '18 at 20:00
  • **1-** The linked post refers to _"Long command split over multiple lines"_, NOT to include "multiple commands in the same line", that is practically the opposite thing. **2-** If you knew about `&` to separate several commands, why you don't use it? It is much simpler than your strange `!lf!` trick. **3-** If you knew about the `for` behavior you should know that there is no way to get the errorlevel of a command in `IN` clause from _any command_ placed in the for-body (no matters if it is placed in a subroutine)... **-->** See the edit in my answer – Aacini Dec 06 '18 at 23:31
  • **1**- thats my bad. I should've been more clear. If I did say "multiple commands in the same line" I meant "Long command split over multiple lines" but I must've just mistyped it because I was in the moment. **2**- I didn't know you could so that Inside the IN clause but I use it plenty outside the IN clause. Also, I didn't think that using & would split the commands into one command per line. **3**- I don't know how to respond to this, because I did manage to get the errorlevel of the command in my solution. A typo in the SET command causes error code 9009 for me... **-continued** – Redacted Dec 07 '18 at 13:33
  • ...and a SET command with no arguments causes errorcode 1. This seems to me like it is using the errorlevel of the command. **4-** the difference between (call ) and VER is largely a matter of preference as this can be solved by comments. I assume most likely the person reading the code won't know either method off the top of their head and thats what comments are for. This issue is minor anyways, this was just an experiment. Coincidentally, your method of using Strings instead of commands in the `IN` clause occured to me just last night in my sleep. Good programmers think alike! – Redacted Dec 07 '18 at 13:37