Returning string from external function batch - windows

I have two batch files. One is the main (caller) file and one is a function.
The function takes one parameter, does some stuff and then i wanna return the string to the caller (main.bat). %ERRORLEVEL% is not an option, as it can only return integers.
main.bat:
call function.bat hello
function.bat:
REM some code.......
[HERE THE CODE TO RETURN A STRING TO CALLER FILE]
Seems like a basic operation, so there must be a way. Also, i prefer not making files with the output and reading it in the main.bat, because i want to make it work as a easy-to-use function.

The two batch files are executed both by same Windows command processor process and share therefore all environment variables.
Main.bat:
#echo off
set "MyVariable="
call Function.bat hello
echo MyVariable=%MyVariable%
Function.bat:
#echo off
rem Some code ...
set "MyVariable=%~1"
It could be that Function.bat uses for some reason command SETLOCAL. In this case all environment variables defined as well as all modifications made on environment variables after the command SETLOCAL are lost after corresponding ENDLOCAL. This command is implicitly called by Windows command processor on exiting execution of a batch file for each SETLOCAL not yet completed with explicit execution of corresponding ENDLOCAL. Read this answer for details about the commands SETLOCAL and ENDLOCAL.
It is necessary to explicitly set an environment variable on same command line as the command ENDLOCAL with using immediate environment variable expansion to pass value of an environment variable from current environment variables list to previous environment variables list.
Function.bat:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Some code ...
set "MyVariable=%~1"
endlocal & set "MyVariable=%MyVariable%"
The last command line is preprocessed first by Windows command interpreter to
endlocal & set "MyVariable=hello"
So the command line after preprocessing does not contain anymore any variable reference. The command ENDLOCAL restores previous environment which results in deletion of environment variable MyVariable. But the second command SET specified on same command line sets this variable once again with the value hello in restored environment.
See also Single line with multiple commands using Windows batch file.

Related

Unable to append to environment variable in batch file

I'm trying to append four directories to %pythonpath%.
The directories are:
C:\src\tensorflow\models\research
C:\src\tensorflow\models\research\object_detection
..\utils
..\mail
When user is User42, %pythonpath% is always set to:
;C:\src\tensorflow\models\research\object_detection;..\utils;..\mail
Why is the first path ignored/overwritten?
#echo off
if "%username%"=="User42" (
set pythonpath=%pythonpath%;C:\src\tensorflow\models\research
set pythonpath=%pythonpath%;C:\src\tensorflow\models\research\object_detection
) else (
:: Other path
)
:: This is common to all users
set pythonpath=%pythonpath%;..\utils;..\mail
echo %pythonpath%
Windows command processor replaces all environment variable references with syntax %VariableName% in a command block starting with ( and ending with matching ) during parsing phase of the next command line to execute on which a command block begins. In this case this means all %pythonpath% in both branches of the IF condition are substituted already by the current value of environment variable pythonpath before the IF condition is executed at all. This behavior can be seen by running the batch file without #echo off from within a command prompt window as in this case Windows command processor outputs the command lines after being parsed before execution.
The solution is using delayed expansion as also explained by help of command SET output on running in a command prompt window set /? on an IF and a FOR example, or avoiding the definition or modification of an environment variable and referencing it once again in same command block.
Here is a solution which works without usage of delayed expansion:
#echo off
set "Separator="
if defined pythonpath if not "%pythonpath:~-1%" == ";" set "Separator=;"
if /I "%username%" == "User42" (
set "pythonpath=%pythonpath%%Separator%C:\src\tensorflow\models\research;C:\src\tensorflow\models\research\object_detection"
) else (
rem Other path is added here to environment variable pythonpath.
)
rem This is common to all users. Variable pythonpath is defined definitely now.
for %%I in ("%CD%") do set "ParentPath=%%~dpI"
set "pythonpath=%pythonpath%;%ParentPath%utils;%ParentPath%mail"
echo %pythonpath%
It additionally makes sure there is not ;; in value of pythonpath in case of there is already a semicolon at end. And it makes sure pythonpath is not defined with a ; at beginning if this environment variable does not exist at all before first IF condition.
Further there are no relative paths added to pythonpath because of determining absolute path for ..\utils and ..\mails before appending them to pythonpath.
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.
echo /?
if /?
rem /?
set /?
BTW: Invalid labels :: as comments should not be used in command blocks. That can result in undefined behavior on execution. It is safer to use command REM for comments.
%pythonpath% used twice between parentheses will be evaluated on read before execution begins.
This is why %pythonpath% has the same value on the 2nd set.
You can use call set to force evaluation on the variable with doubled %s.
This has a similar effect to enabledelayedexpansion seen in setlocal /?
and if /?.
#echo off
if "%username%"=="User42" (
set "pythonpath=%pythonpath%;C:\src\tensorflow\models\research"
call set "pythonpath=%%pythonpath%%;C:\src\tensorflow\models\research\object_detection"
) else (
:: Other path
)
:: This is common to all users
set "pythonpath=%pythonpath%;..\utils;..\mail"
echo %pythonpath%

Windows prompt content variable as variable name

It is possible in windows command line refer to a content variable like a pointer?
Example:
SET envTEST=FOO
SET envPROD=BAR
SET CURR=envTEST
SET data=%%CURR%%
I want to data contains FOO but it contains %envTEST%
Thanks
You need multiple rounds of expansion. See How does the Windows Command Interpreter (CMD.EXE) parse scripts? for an explanation as to why the following work:
From the command line:
call set "data=%%CURR%%"
Or if delayed expansion has been enabled by cmd /v:on, then:
set "data=!%CURR%!"
From within a batch script:
call set "data=%%%CURR%%%
or
setlocal enableDelayedExpansion
...
set "data=!%CUR%!
If CURR is set and expanded within the same code block (typically done within IF or FOR loop), then:
setlocal enableDelayedExpansion
...
(
set "CURR=envTEST"
for %%V in (!CURR!) do set "data=!%%V!"
)
The CALL technique is quite slow, so I try to avoid it. It is not an issue for a single use, but it can become a major problem (slow performance) if used within a tight loop.

Windows Batch setting PATH through loop

I'm trying to create a drive with a suite of programs that can be implemented through the command prompt.
My issue is setting the PATH environment variable to include paths to the program suite. Since there are a couple directories for these suites (and I'm constantly adding more), I am trying to get it so I can have a .ini file be parsed through a simple Batch script and returned. From this I want to take the path parsed from the .ini file and append it to my current PATH variable.
This is the current script I'm trying to get fixed.
FOR /F "delims=" %%i IN ('call %cd:~0,3%\initTermPort\parse.ini.bat %cd:~0,3%\initTermPort\config.ini Alternate _path') DO (
SET PATH="%PATH%;%cd:~0,3%%%i"
)
The call to parse.ini.bat is getting the next path in config.ini on every run.
For example:
If I substitute SET PATH="%PATH%;%cd:~0,3%%%i" with ECHO %%i I get the following output:
E:\> (ECHO ~\bin)
~\bin
E:\>(ECHO ~\library\bin)
~\library\bin
But for some reason SET PATH="%PATH%;%cd:~0,3%%%i" is not setting the PATH at all.
Any help is appreciated.
The problem is that when the for code block (the code enclosed in parenthesis) was parsed, all the read operations to variables were replaced with the value in the variable before starting to execute, and in each iteration what is used is this initial value, and not the value inside the variable during the execution.
If you change a variable inside a block of code and need to access the changed value inside the same block of code, you need to enable delayed expansion and change the syntax to access the variables to use !varName! instead of %varName%. This indicates to the parser that this read operation must be delayed.
setlocal enableextensions enabledelayedexpansion
....
FOR /F "delims=" %%i IN (
'call %cd:~0,3%\initTermPort\parse.ini.bat %cd:~0,3%\initTermPort\config.ini Alternate _path'
) DO (
SET "PATH=!PATH!;%cd:~0,3%%%i"
)

Concatenate file paths to an environment variable in batch script

I have made a bat script that should copy the list of folders to a variable but I don't get anything in the variable. In other words, when I echo the variable after my for loop I get the expected output, but in the shell outside after executing the script, I don't see anything set in my variable. How can I get all the variables to copy correctly?
I am using Windows 7.
Batch FIle (script.bat):
#echo off
setlocal enabledelayedexpansion enableextensions
for /r /D %%x in (*) do (
SET PATH_VALUE=%%x;!PATH_VALUE!
)
echo %PATH_VALUE%
Output of windows cmd utility
C:\test> script.bat
C:\test\1;C:\test\2
C:\test> echo %PATH_VALUE%
%PATH_VALUE%
How do I get the %PATH_VALUE% as an environment variable? I found a similar question here but it doesn't quite answer my case.
That is because of your SETLOCAL command that you use to enable delayed expansion. Yes it provides the delayed expansion you need, but it also localizes environment changes. As soon as your batch script ends, there is an implicit ENDLOCAL, and the old environment is restored.
You can pass the value across the ENDLOCAL barrier by adding the following to the end of your script:
endlocal&set "PATH_VALUE=%PATH_VALUE%"
or you could write it like:
(
endlocal
set "PATH_VALUE=%PATH_VALUE%"
)
Both of the above work because the blocks of code are expanded and parsed prior to the ENDLOCAL executing, but the SET statement with the expanded value is executed after the ENDLOCAL.

How to persistently add directories to PATH when using SETLOCAL? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Make an environment variable survive ENDLOCAL
How to keep the value of a variable outside a Windows batch script which uses “delayed expansion local” mode?
I have a batch file that goes something like this:
REM I need to use SETLOCAL so as not to pollute the environment
REM with any variables used to implement the logic in this script
SETLOCAL
SET I_DONT_WANT_THIS_VARIABLE_TO_LEAK_OUTSIDE=c:\
REM BUT, there is this one change to the environment I want to make sticky:
PATH %PATH%;%I_DONT_WANT_THIS_VARIABLE_TO_LEAK_OUTSIDE%
The script uses SETLOCAL to isolate the environment from whatever the batch file wants to do, but this causes a problem: at the end I do want to modify the caller's environment in some very specific manner, but SETLOCAL prevents this (the change is undone as soon as the batch file exits). I also cannot call ENDLOCAL before setting the path because that restores the environment to its original state, wiping out the value of the variable so I cannot append its value to the path.
Is there a way to set the path while ignoring the SETLOCAL in effect? Alternatively, is there any trick to explicitly preserve the value of a variable from being wiped out when calling ENDLOCAL?
I am aware that it's possible to use a temp file as a vessel that can bypass the ENDLOCAL barrier, but a more elegant solution would be welcome.
Add this to the end of your script (or just before you return):
endlocal & #set path=%path%
The reason that this works is that the cmd parser performs environment variable expansion to the entire line before executing any of it. So by the time endlocal executes, the line that cmd is processing looks like:
endlocal & #set path=c:\whatever\the;c:\new\path;c:\is
Then endlocal throws away all the local changes, but the ones made to the path have been temporarily preserved in the set command that's about to be executed.

Resources