Parse an environment variable in a Windows BAT script - windows

I'm trying to create a simple application launcher for the software Nuke that puts the path together by evaluating an environment variable. The value of the variable is used in two ways. First it's used as-is. The second time I need to split the variable and use the first half.
The env variable set for the system:
NUKE_VERSION = 10.0v5
The path to the application:
C:\Program Files\Nuke10.0v5\Nuke10.0.exe
The code below works fine on the cmd prompt:
FOR /F "delims=v tokens=1" %i IN ("%NUKE_VERSION%") DO set NUKE_MAJOR=%i
"C:\Program Files\Nuke%NUKE_VERSION%\Nuke%NUKE_MAJOR%.exe"
But when I run a .bat with the code, it returns this error:
NUKE_VERSIONi was unexpected at this time.
Any insights into what is going on? I could just do this in python, but something this simple I shouldn't have to, right? Many thanks in advance.

In a CMD Window a FOR-LOOP uses a single % sign as you have listed in your question.
In a Batch file a FOR-LOOP uses a double %% sign.
FOR /F "delims=v tokens=1" %%i IN ("%NUKE_VERSION%") DO set NUKE_MAJOR=%%i

Related

Variable not being set in Visual Studio Post-build event command line

I have the following script in a Windows batch file that fetches the version of a DLL file and renames an existing file by inserting the version in its file name. This works fine when I put it in a .bat file and run it in Windows. The resultant file name is GCSv1.1.0.7316.zip.
SET WMICommand="WMIC DATAFILE WHERE name='E:\\Projects\\GCS\\bin\\Debug\\Client.dll' get Version /value"
for /f "tokens=2 delims==" %%x in ('%WMICommand%') do (set vers=%%x)
echo %vers%
echo GCS_v%vers%.zip
ren E:\Projects\GCS\InstallerTemplateProject\DeployFolder\GCS_Package.zip GCS_v%vers%.zip
I need to take this same script and put it in a Post-build event command line in Visual Studio to rename the generated file as a last step in the build process. Which is as follows:
SET WMICommand="WMIC DATAFILE WHERE name='$(SolutionDir)GCS\bin\debug\\Client.dll' get Version /value"
for /f "tokens=2 delims==" %%x in ("%WMICommand%") do (set vers=%%x)
ren "$(SolutionDir)GCS\InstallerTemplateProject\DeployFolder\GCS_Package.zip" GCS_v%vers%.zip
But for some reason, the renaming is not happening properly because it is being renamed to GCS_v.zip.
Clearly the %vers% variable is not being set when it is in the Post-build event command line. Why does it work when executing it in a .bat file on Windows, but not when executing in a post-build? What should I do differently to the variable here? The goal is to get the second batch working in visual Studio. The first batch I wrote it just to test it out.
Each of the batchfiles is invoked independently and instantiates its own environment, so the second batch does not acquire the changes made the the first's variables.
Two obvious solutions
Use setx to set the variable into the second batch (with the downside that it will also be included in the environment of any other batch started following the setx) - probably then use setx to clear the variable from the second batch.
Write the variable to a file and read the file back into the second batch.
Ah - my misinterpretation, caused by skimming. I'd assumed the first batch was a pre-build and the second a post-build.
The problem appears to be that VS is not substituting the actual (and no doubt per-project) value of Solutiondir into the batch - which it logically cannot do because there's no guarantee that $(whatever) isn't a string with another interpretation within a random batchfile.
I'd suggest that you try
set>afilename.txt
as the first command within the post-build batch, which will create a file containing the value of most of the environment variables. It may very well be that a variable named something like solutiondir is installed in the environment for the post-build batch - but I can't guarantee that...

windows oneliner to set a command output in an environment variable

As stated here Is it possible to set an environment variable to the output of a command in cmd.exe I always used that
mycommand.exe>%TEMP%\out.txt
set /P FOO=<%TEMP%\out.txt
But that's ugly because it creates a file.
The for method is better but complicated
I wanted something simple a la unix, like:
mycommand.exe|set /P FOO=
No error, but FOO is not set after I run that.
Why is this not working?
Best way I can think of doing this would be to create your own little batch file that silently uses the FOR construct. For instance, you could create a batch named BatchSet.bat, stored somewhere on your path. The batch would contain the following code:
#Echo off
for /f "delims=" %%i in ('%2') do set %1=%%i
Set %1
If you run this with the following command:
BatchSet MyVar whoami
You'll get something like:
MyVar=servername\John
Obviously, the command you run should limit its output to a single line to be stored properly in the environment variable.
For instance, if you run it like this:
BatchSet MyVar Vol
Then you'll only get the first line of the Vol command's output
MyVar= Volume on drive C is labeled MyDisk
But all in all, it's a fairly elegant way of doing what you were looking for.
Note that the last line in the batch is simply there to provide visual output. It can be removed altogether.

Error with bat file token or for loop: not recognized as an internal or external command, operable program or batch file

Need Windows .bat file tech support.
I am having trouble running Win 7 bat files which loop through tokens and variables. The script below works on my wifes PC, and should on all PCs, but does not on mine.
** I am running Windows 7 in VMWare off my Mac.
The file is located at the root of c:
This short little script, and any others like it with tokens gives me errors ( I copied the script below out of the .bat file ):
setLocal ENABLEDELAYEDEXPANSION
set SCHEMA_one= first
set SCHEMA_two= second
set SCHEMA_three= third
#ECHO ON
FOR /F "tokens=2* delims=_=" %%A IN ('set SCHEMA_') DO (
echo "looping through " %%A
)
endLocal
I get the following error:
C:\>FOR /F "tokens=2* delims=_=" %A IN ('set SCHEMA_') DO (echo "looping through " %A )
'set SCHEMA_' is not recognized as an internal or external command, operable program or batch file.
Ideas???
Many thanks in advance. I have been stuck for hours and hours...
You may need to look into for /?, specifically the use of usebackq. This can change the interpretation of you set.
Additionally, I always use enableextensions in my setlocal command as well, to ensure the environment is set up exactly as I need.
The other thing is to do a hex dump of your batch file to make sure that the character between set and SCHEMA is actually a space. If it's a different character, then cmd will probably treat set.SCHEMA (where the . is that funny character) as one command.
EDIT- I DID find the answer.. Complete user error..
At some point, the COMSPEC environment variable path got changed, and so was WRONG!!!! With a typo!!! :/ So lame.
While comparing env variables between my new environment and old, I finally noticed the typo. Man, one character cost me hours and hours. I didn't realize before ( though its obvious now ) that was the path used for the bat command or I would have given it more scruteny. Not sure why it worked at all (???)
================
Well, I am not happy with the fix- because it was a BIG hammer. But I installed a new Windows 7 VM, and now I can run the script.
Earlier In stalled a newer version of the vm- 3.1.4 ( the newest free upgrade from my 3.1.2 ) and that did not solve the problem.
And before that I tried the things suggested above ( Many thanks to the people who posted above! )
Not sure what is screwed up with my windows install, but it's something.. I've compared against other computers, and my environment args all look good. Java is good, etc. Just something about either my user account on that VM, or ...? Not sure. Ran out of time, had to move on. :)
If I figure it out I will post the answer. In the mean time I'll be setting up and hopefully working from this new VM.
cheers!
Dustin

Why is QProcess converting the '=' in my arguments to spaces

I've run into a weird error with a Qt program running on Windows. The program uses QProcess to spawn a child process wit two arguments. The program and arguments passed to the QProcess::start() method are of the form:
"batchfile.bat" "--option1=some_value" "--option2=some_other_value\with_a\path"
For some reason by the time those options get to the batchfile for processing the equals signs have been converted to spaces and it now looks like:
"batchfile.bat" "--option1 some_value" "--option2 some_other_value\with_a\path"
because of this, the processing fails. Any ideas what could be causing the equal signs to be replaced by spaces? I'm using the mingw build of the QT 4.6.3 framework found on the Qt download page.
EDIT:
Here's the actual code. I didn't write it (I'm a complete Qt noob) but I've got to try to get it working. It's part of an automated build system that runs on two versions of RHEL (4 and 5), OS X, and Windows. And it works fine everywhere but Windows.
QProcess sconsProcess;
sconsProcess.setWorkingDirectory(build.getBuildLocation());
sconsProcess.setProcessChannelMode(QProcess::MergedChannels);
qDebug()<<"Starting scons process:"<<build.getSconsLocation()<<QString("--variant=%1-%2").arg(build.getOs()).arg(build.getVariant())<<
QString("--source-release=%1").arg(build.getSettings().getSetting("sourceReleaseLocation", QStringList()<<"BUILDLOCATION"<<"VERSION",
QStringList()<<build.getBuildLocation()<<build.getBuildPackage().getVersion()).toString());
sconsProcess.start(build.getSconsLocation(), QStringList()<<QString("--variant=%1-%2").arg(build.getOs()).arg(build.getVariant())<<
QString("--source-release=%1").arg(build.getSettings().getSetting("sourceReleaseLocation", QStringList()"BUILDLOCATION"<<"VERSION",
QStringList()<<build.getBuildLocation()<<build.getBuildPackage().getVersion()).toString()));
qDebug()<<"Source release build process started";
The actaul values that translates into in Windows (the bit that gets printed out in the first qDebug() print call) is:
DEBUG: Starting scons process: "V:\Glast_Software\Toaster\tools\Python2.5\Scripts\scons-1.3.0.bat" "--variant=Windows-i386-32bit-vc71-Debug" "--source-release=V:\Glast_Software\Toaster\ReleaseManagerBuild\Windows-i386-32bit-vc71\Debug\ScienceTools\LATEST-1-3163\ScienceTools-LATEST-1-3163-source.zip"
However inside the scons-1.3.0.bat (I had it echo all the commands executed) the passed parameters look like:
"--variant Windows-i386-32bit-vc71-Debug" "--source-release V:\Glast_Software\Toaster\ReleaseManagerBuild\Windows-i386-32bit-vc71\Debug\ScienceTools\LATEST-1-3163\ScienceTools-LATEST-1-3163-source.zip"
with the equal signs missing.
EDIT (6/29/10):
I should add that this system is designed to run on a small Windows batch farm using the LSF batch queuing system. It only fails when the process is running as a batch job. When I run this program from the command line on one of the batch machines, it works perfectly and does exactly what it is supposed to do. So maybe it is an environment problem.
There's a good chance that this is because the quotes aren't making it through (they may need to be escaped, see the docs for QProcess::start()).
cmd.exe treats equals signs in command line options that aren't quoted as a separator between arguments similar to a space or tab. Just one of very many bits of oddness in Windows cmd scripting:
C:\test>type c:\util\cmdechoargs.cmd
#echo off
setlocal
set /a i=0
echo args[*]: %*
:loop
if {%1} == {} goto :eof
echo argv[%i%]: %1
set /a i=%i% + 1
shift
goto :loop
C:\test>cmdechoargs testing=123
args[*]: testing=123
argv[0]: testing
argv[1]: 123
C:\test>cmdechoargs "testing=123"
args[*]: "testing=123"
argv[0]: "testing=123"
The best documentation I've come across for how to handle command line arguments in Windows cmd scripts is Tim Hill's "Windows NT Shell Scripting" - get one used for only a penny!
Based on the examples given in your update, I think you might want your options that have equals signs in them to have quotes embedded inside them:
"\"--variant=%1-%2\""
"\"--source-release=%1\""
Edit -- new material
The following script has a routine that will strip the quotes off of an argument passed to a cmd script. The routine returns the 'dequoted' argument in an environment variable named RET using an idiom/technique from Tim Hill's book I mentioned above. I stole some of the dequoting code from an example here: http://ss64.com/nt/syntax-esc.html, but made it a bit more robust to handle empty quotes.
#echo off
setlocal
set /a i=0
echo args[*]: %*
:loop
if {%1} == {} goto :eof
echo.
echo argv[%i%]: %1
call :dequote %1
set dequoted_arg=%RET%
echo argv[%i%] ^(dequoted^): %dequoted_arg%
set /a i=%i% + 1
shift
goto :loop
:dequote
setlocal
SET _string=###%1###
if {%_string%} == {######} goto :dequote_empty
if {%_string%} == {###""###} goto :dequote_empty
SET _string=%_string:"###=%
SET _string=%_string:###"=%
SET _string=%_string:###=%
goto :dequote_done
:dequote_empty
set _string=
:dequote_done
endlocal & (set RET=%_string%) & goto :eof
This kind of thing is why you want to avoid (in my opinion) cmd scripts except for the simplest of tasks. But, I hope this helps you pass unquoted arguments to your scons process through your batch file.
Have you tried escaping the = signs? Also, the paths in your example surely need escaping of the \ character.

Setting a variable from an executable

I am running an executable in a batch file with two parameters;
cmd /k ""executable" "param1" "param2""
This returns a string that I want to launch. I can't figure out how to set this return in a variable and subsequently launch it in IE.
Any ideas?
If the returned string contains a single line you may use FOR /F to set the value of an environment variable. For example:
s1.cmd
echo this is a one line string
s2.cmd
#SETLOCAL
#ECHO OFF
for /f "tokens=*" %%a in ('cmd /c s1.cmd') do set MY_VAR=%%a
echo got: %MY_VAR%
ENDLOCAL
Result
C:\> s2.cmd
got: this is a one line string
C:\>
You can use the following syntax to capture the output of your executable into a variable:
FOR /F "tokens=*" %%i in ('%~dp0YOUR_APP.exe') do SET TOOLOUTPUT=%%i
Source
then you can pass the value on to IE like so:
START "YOUR_WINDOW_NAME" /MAX /D"C:\Program Files\Internet Explorer\" iexplore %TOOLOUTPUT%
I take it that the application code that determines the url is too complicated to be reproduced in a batch file directly, or the source to the executable has been lost. If not I personally would prefer to have the logic visible in the batch file itself.
start %1 %2
Edit: Romulo A. Ceccon posted a much better solution which doesn't involve any file system access and dirty tricks. Left this here for reference (it works with command.com as well if you need 9x compatibility), but please prefer Romulo's solution.
Go through an environment variable you set by using an intermediate helper script you dynamically generate from a template. You will need write permissions somewhere, otherwise it cannot be done (the Windows command shell language is very, very limited.)
Let's call your helper script template helper.tpl with the following contents:
set INTERMEDVAR=
Make sure that helper.tpl has only a single line (no trailing CRLF!) and make sure you don't have any spaces after the equals sign there.
Now, in your main script, capture the output from your command into a temporary file (let's call it my_output_file.tmp):
cmd /k ""executable" "param1" "param2"" > my_output_file.tmp
Then copy the contents of the helper template and the output together into your helper script, let's call it my_helper_script.cmd:
copy /b helper.tpl + my_output_file.tmp my_helper_script.cmd
Then evaluate the helper script in the current context:
call my_helper_script.cmd
Now the INTERMEDVAR variable is set to the first line of the output from "executable" (if it outputs more than one line, you're on your own...) You can now invoke IE:
start iexplore.exe "%INTERMEDVAR%"
And don't forget to clean up the created files:
del /q /f my_output_file.tmp my_helper_script.cmd
This will obviously not work when invoked multiple times in parallel - you'll have to parametrize the temporary file and helper script names using the current cmd.exe's PID (for example) so that they won't overwrite each other's output, but the principle is the same.
However, if you can get a real shell, use that. cmd.exe is extremely cumbersome.

Resources