1

I am writing a batch script for the 6th assignment in my class and I have hit a snag when I am pretty much finished. (We usually focus on bash scripting, so I am new to batch)

The script functions as desired the first time running it; but behaves differently when running it for the second time.

Essentially, the script checks the value of an argument if it exists and runs specific code depending on the value. For example, if the argument is "1", then it checks the PATH variable for a directory and creates it if it doesn't exist, and if it does exist - nothing happens and it just continues the script.

The issue occurs when running the script for a second time after the PATH is modified. I receive the output "\Common was not expected at this time".

I had a similar issue when running the script the first time, but managed to fix it by including Quotation marks on both side of the evaluation on the IF statement, but now I am unsure of where to continue with this.

My code is as follows:

@echo on

IF "%1%" == "0" (
    SET "VAR1=%path%"
    echo.%VAR1%|findstr /C:"App0" >nul 2>&1
    if errorlevel 1 SET "PATH=%PATH%%cd%\App0;"
    if not errorlevel 1 echo Found
    goto errorBypass
) ELSE IF "%1%" == "1" (
    SET "VAR2=%path%"
    ECHO %VAR2%
    echo.%VAR2%|findstr /C:"App1" >nul 2>&1
    if errorlevel 1 SET "PATH=%PATH%%cd%\App1;"
    if not errorlevel 1 echo Found
    goto errorBypass
) ELSE IF "%1%" == "" (
    IF "%HUMBER_HOME%" == "" (
        goto Error2
    ) ELSE (
        CALL "HUMBER_HOME\bin\setEnv.bat"
        goto errorBypass
    )
)
echo HERE

:Error2
echo Error2

:errorBypass
call "run.bat"

Also, so I know for future reference - is there an effective way to debug by going line by line? or a command that can output the particular line where the error occurred? I find it somewhat difficult when one error can be caused by multiple issues in different places.

Bryan Douglas
  • 137
  • 1
  • 10

1 Answers1

2

Referencing a batch file argument

Open a command prompt window and run call /?. The output help explains how to reference batch file arguments. %1 references the first argument as passed to the batch file. That can be for example 1 (not quoted argument string), but also "1" (quoted argument string). %~1 references first argument string with surrounding double quotes removed.

It is wrong to add one more % after the argument reference. The syntax %variable% is used to reference the string value of an environment variable. Batch file arguments are referenced only with percent sign and digit without or with a modifier between. There is no more percent sign after the digit. That is also the reason why 1, 2, 3, ... are not possible as names for environment variables.

So not good is IF "%1%" == "0" ( because of this can result on batch file called with "1" as first argument in execution of the command line:

IF ""1"" == "0" (

The much better syntax is IF "%~1" == "0" ( which results in execution of the command line:

IF "1" == "0" (

See my answer on difference between “…” and x“…” in batch for more details on how to evaluate batch file arguments.

Appending a folder path to local PATH

The environment variable PATH holds a comma-separated list of folder paths whereby the list separator is a semicolon instead of a comma.

Therefore a ; at end of PATH means there is one more folder path which is an empty folder path. It is possible to specify an empty folder path in the middle or at end of PATH, but it is bad practice to do so because of PATH should not contain empty folder paths.

For that reason the following command line in your code is not good:

if errorlevel 1 SET "PATH=%PATH%%cd%\App0;"

There is also missing ; in case of PATH does not already end with a semicolon which could be a reason for the error message on second execution of the batch file.

The better code can be seen below on completely revised batch file code.

Referencing current directory

It is possible to reference the current directory with %CD% which can differ from batch file directory referenced with %~dp0. %~dp0 references drive and path of argument 0 which is the batch file itself. The batch file path string referenced with %~dp0 always ends with a backslash. Therefore no additional backslash should be used after %~dp0 on concatenating it with a file/folder name.

The dynamic environment variable CD ends usually with no backslash at end. So in most cases %CD% must be concatenated with an additional \ with a file/folder name. But there is one exception which must be taken into account on using %CD% in a batch file: %CD% expands to a string with \ at end on current directory being the root directory of a drive, for example C:\ or D:\. So it is always necessary on using %CD% to check if the string already ends with a backslash before appending a file/folder name without or with an additional backslash.

Other recommendations

The usage of a command block starting with ( and ending with ) should be avoided on using environment variables defined/modified within a command block and referenced within the command block as this requires the usage of delayed expansion as explained by help output on running set /? in a command prompt window on an IF and a FOR example on which command blocks are used usually. The Windows command processor is designed primary for executing one command line after the other. The usage of command blocks can speed up the execution of a batch file in some cases, but in many cases it is better to avoid them.

See debugging a batch file with a short description how to debug a batch file. A single step execution is not really possible. But cmd.exe shows on which line or command block an error occurred resulting in exiting batch file execution and what is the error.

Revised batch file code

Here is the revised batch file code:

@echo off
goto Main

:AddPath
echo %PATH%;|%SystemRoot%\System32\findstr.exe /I /C:"\%~1;" >nul 2>&1
if not errorlevel 1 echo Found %~1 in PATH& goto :EOF
set "Separator=;"
if "%PATH:~-1%" == ";" set "Separator="
if "%CD:~-1%" == "\" (set "AppPath=%CD%%~1") else set "AppPath=%CD%\%~1"
set "PATH=%PATH%%Separator%%AppPath%"
set "AppPath="
set "Separator="
goto :EOF

:Main
if "%~1" == "0" call :AddPath App0 & goto errorBypass
if "%~1" == "1" call :AddPath App1 & goto errorBypass
if not "%~1" == "" goto RunApp
if "%HUMBER_HOME%" == "" goto Error2
if exist "%HUMBER_HOME%\bin\setEnv.bat" (
    call "%HUMBER_HOME%\bin\setEnv.bat"
    goto errorBypass
)

echo File "setEnv.bat" in subdirectory "bin" in directory
echo defined by environment variable HUMBER_HOME not found.
echo HUMBER_HOME directory: "%HUMBER_HOME%"
echo/
pause
goto :EOF


:RunApp
echo HERE
goto :EOF

:Error2
echo Error2
goto :EOF

:errorBypass
if exist "run.bat" call "run.bat"

There is defined the subroutine AddPath at top of the batch file which is a bit unusual. So the second line with goto Main results in jumping over code of the subroutine on starting the execution of the batch file.

The subroutine AddPath is called with either App0 or App1 on first argument being 0 or "0" or 1 or "1".

The first line in AddPath outputs current value of local environment variable PATH with a semicolon appended and redirects this output to FINDSTR which searches case-insensitive and literally for the first argument string passed to the subroutine after a backslash and ending with a semicolon. The additional \ and ; should avoid a false positive on any folder path in PATH containing by chance also App0 or App1 somewhere in the middle of the folder path. This small enhancement is not 100% fail safe, but should be good enough.

FINDSTR exits with 0 on searched string found in line. In this case just an information message is output and the subroutine is exited which results in batch file execution being continued on main code on which the subroutine was called before. Otherwise the passed application name must be added to local PATH.

See also:

So first the environment variable Separator is defined with ; as value. But if local PATH already ends with a backslash although it should not, the environment variable is deleted immediately. Please note that the command line comparing last character of PATH with ; can fail if PATH ends with ". So this simple version is not 100% fail safe.

Next the current directory path is concatenated with the passed application folder name without or with an additional backslash depending on current directory being root directory of a drive or a subdirectory of any directory.

Then the local PATH is extended with appending the application path according to passed argument without or with an additional semicolon before.

Finally the no longer needed environment variables Separator and AppPath are deleted before exiting the subroutine.

A main mistake in main code as posted in question are the missing percent signs around environment variable HUMBER_HOME on calling batch file setEnv.bat in subdirectory bin of the directory assigned to environment variable HUMBER_HOME. This could be another reason for the error message on second execution of the batch file.

The revised code first checks if each batch file to call really exists before calling it.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • echo /?
  • findstr /?
  • goto /?
  • if /?
  • pause /?
  • set /?
Mofi
  • 46,139
  • 17
  • 80
  • 143
  • Amazing response. THank you for going into such detail, I enjoyed reading all the information and I feel like I have a good grasp on how it works :D. 1+ and accepted answer – Bryan Douglas Dec 11 '18 at 00:32