Parenthesis in Windows cmd-script variable values not allowed? - windows

Why gives the following Windows 7 .cmd command script:
set SUN_JAVA_HOME=C:\Program Files (x86)\Java\jdk1.6.0_17
if 3==3 (
set JAVA_HOME=%SUN_JAVA_HOME%
)
echo ready
The following error message instead of printing "ready"
\Java\jdk1.6.0_17 was unexpected at this time.
The error message disapears, if I remove the "(x86)" in the path name.

on the command prompt, enter the following commands
C:
CD\
dir /ogen /x
This will show you the 8 character name for Program Files (x86)
Use that name (probably "Progra~2")

The problem is the parentheses grouping after the if 3==3 part.
While parsing the set JAVA_HOME=%SUN_JAVA_HOME% command, the interpreter immediately replaces the %SUN_JAVA_HOME% variable and that causes an early match of the closing parenthesis in (386).
This can be avoided if you enable delayed expansion and replace %SUN_JAVA_HOME% with !SUN_JAVA_HOME!:
setlocal enabledelayedexpansion
set SUN_JAVA_HOME=C:\Program Files (x86)\Java\jdk1.6.0_17
if 3==3 (
set JAVA_HOME=!SUN_JAVA_HOME!
)
echo ready

you have to enclose the set command by double quotes
replace
set SUN_JAVA_HOME=C:\Program Files (x86)\Java\jdk1.6.0_17
by
set SUN_JAVA_HOME="C:\Program Files (x86)\Java\jdk1.6.0_17"
because there's a space in the path

I've written about this a while ago (slightly outdated by now).
As an alternative, if you need grouping the commands, then use a subroutine:
if 3==3 call :foo
...
goto :eof
:foo
...
goto :eof

Previous answer is ok. I just want clarify it with simple example. It's about detecting Program Files directory for 32-bit application on x86 and x64 systems. There similar problem with "(x86)".
IF DEFINED ProgramFiles(x86) (GOTO x64) ELSE (GOTO x86)
:x64
SET AppDir=%ProgramFiles(x86)%\SomeFolder
GOTO next
:x86
SET AppDir=%ProgramFiles%\SomeFolder
:next
ECHO %AppDir%

Related

How do I format Tesseract-OCR language settings within a .bat file's "for" command

I'm new to Windows cmd and .bat, and to Tesseract. But thanks to this list I've managed a couple of successes.
My first success was this cmd-window line:
tesseract.exe -l eng+lat+ita D:\TIFs\Convivio.tiff D:\TIFs\Convivio
My next success was the .bat file:
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set _SourcePath=D:\temp\TIFs\*.tif
Set _OutputPath=D:\temp\TIFs\
Set _Tesseract="D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in (%_SourcePath%) Do Echo Converting "%%A"...... &"D:\temp\Tesseract-OCR\tesseract.exe" "%%A" "%_OutputPath%%%~nA"
PAUSE
:End
Set "_SourcePath="
Set "_OutputPath="
Set "_Tesseract="
The problem now is how to include in the .bat file that"-l eng+lat+ita" bit from the cmd-window line.
I got the idea that this is possible from an explanation of the "For" command, which states that "do command" can be followed by "CommandLineOptions" (i.e., "-l eng+lat+ita").
Any help would be much appreciated... 'cause I've been banging my head on this one for hours now...
UPDATE: Found an alternative, but still would like an answer to my question.
I didn't know that "FOR" commands could be run from cmd. So, I pasted the folllowing line in the cmd window:
for %i in (*.tif) do "D:\temp\Tesseract-OCR\tesseract.exe" -l eng+lat+ita "%i" "D:\temp\%~ni"
And, it worked!
As I say, though, how to do this with a .bat file?
#ECHO OFF
SETLOCAL
:Start
#Echo off
ECHO.
ECHO This is a batch file
ECHO.
PAUSE
BREAK=ON
Set "_SourcePath=D:\temp\TIFs\*.tif"
Set "_OutputPath=D:\temp\TIFs"
Set "_Tesseract=D:\temp\Tesseract-OCR\tesseract.exe"
:Convert
For %%A in ("%_SourcePath%") Do Echo Converting "%%A"...... &"%_Tesseract%" -l eng+lat+ita "%%A" "%_OutputPath%\%%~nA"
PAUSE
:End
rem Set "_SourcePath="
rem Set "_OutputPath="
rem Set "_Tesseract="
GOTO :EOF
Since I don't have the tesseract utility, I used another. The above worked for me as I expected with that other utility, so no guarantees with tesseract.
It's normal practice to start a batch with setlocal which makes the clean-up effort unnecessary (hence remmed-out) since an implicit endlocal is executed when the batch terminates, restoring the environment to its initial state.
Assigning values containing quotes is valid but awkward when combining elements. Ditto terminating a value with a backslash. I've converted your code to my preferred syntax. Note that the syntax SET "var=value" (where value may be empty) is used to ensure that any stray trailing spaces are NOT included in the value assigned.
Will it work in your situation? Over to you to try.

Strange error in batch file when handling a file path with Program Files (x86)

I am writing a batch script to remove quotes from the JAVA_HOME env variable and am getting an error only when JAVA_HOME is set with "Program Files (x86)" in the path. My JAVA_HOME variable is set to C:\Program Files (x86)\Java\jdk1.7.0_79(no quotes are in the value)
set lastChar=%JAVA_HOME:~-1%
set firstChar=%JAVA_HOME:~0,1%
echo Begin
if '^%firstChar%=='^" (
echo JAVA_HOME starts with a quote
if '^%lastChar%=='^" (
echo JAVA_HOME ends with a quote
set JAVA_HOME=%JAVA_HOME:~1,-1% ###Error occurs on this line
)
)
echo END
The output is:
Begin
\Java\jdk1.7.0_7 was unexpected at this time.
As you can see, none of the lines after "echo Begin" are run. If I comment out the line set JAVA_HOME=%JAVA_HOME:~1,-1% then the program runs all the way to END. Also, if I change the JAVA_HOME env var to C:\Program Files\Java\jdk1.7.0_79, the script runs fine without commenting out that line.
Why is this one line giving me this error?
The simplest way to deal with problematic characters within environment variables is to use delayed expansion. You can do string substitution to remove any quotes. I am assuming you want the modified value to persist after the batch script ends, so I use a FOR variable to transfer the modified value across the ENDLOCAL barrier.
#echo off
setlocal enableDelayedExpansion
set "val=!JAVA_HOME:"=!"
for /f "delims=" %%A in ("!val!") do endlocal&set "JAVA_HOME=%%A"
It is possible to do this from the command line, without using batch. But it is a bit tricky to get the proper escape sequences
for /f "delims=" %A in ('cmd /v:on /c echo !JAVA_HOME:^"^=!') do #set "JAVA_HOME=%A"
^ escapes other characters. So ^% means no percent sign to CMD. Use ^^%firstchar%==^^ to escape the caret so it isn't escaping the % and ".

Problems reading simple numeric values from files in Windows batch files

My colleague and I have been pulling our hair out all day over this.
We have a simple Windows batch file. We want it to read from a text file whose file path we are generating programmatically, take the single numeric value in this file, and compare it to a local variable. But we're getting completely inexplicable behavior.
The file contains a single scalar number, such as the number 2. Here's the code:
ThisAppFlagFileName=foo.txt
if not exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO do something here
) else (
SET /P InstalledVersion=<"%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%"
ECHO We think the file contains: %InstalledVersion%
IF %InstalledVersion% GEQ %ThisVersionInstallDataNum% (
ECHO Version %ThisVersion% of the %ThisAppVisibleName% has already been installed for this user; exiting.
GOTO TheEnd
)
)
:TheEnd
Echo END
We keep getting an error reading 2 was unexpected at this time. So we inserted some trace message and, just in case the else was problematic, stuck to two different if statements:
ThisAppFlagFileName=foo.txt
if not exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO do something here
)
ECHO Trace Message 1 before IF
if exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO Trace Message 2 after IF before CD
SET /P InstalledVersion=<%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%"
ECHO We think the file contains: %InstalledVersion%
IF %InstalledVersion2% GEQ %ThisVersionInstallDataNum% (
ECHO Version %ThisVersion% of the %ThisAppVisibleName% has already been installed for this user; exiting.
GOTO TheEnd
)
)
:TheEnd
Echo END
And we see only the first trace message (before the if statement), and not the second trace message. So our conclusion is that somehow the content of the file is being interpolated into the line if exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%", but of course we don't understand why the first if not exist works but the second doesn't.
Can anyone spot the mistake, please? Environment is Windows 7 cmd.exe window, but we are hoping to deploy to both Windows 7 and Windows XP.
The issue here is that the entire IF expression is evaluated before the SET /P statement within it can be executed. InstalledVersion is not set yet, and so this invalid expression is evaluated:
IF GEQ 2 (
Nothing inside of the IF expression executes because it cannot be completely evaluated.
A solution is to enable delayed expansion and replace %InstalledVersion% with !InstalledVersion!, as described in this post.
You can also restructure the code so the GEQ comparison happens after the IF expression.
Your code have several errors. The first line:
ThisAppFlagFileName=foo.txt
missed a set command, so it is tryed to be executed as ThisAppFlagFileName command. This mean that ThisAppFlagFileName variable is NOT defined in your program, so InstalledVersion variable is never read from the file.
All references to InstalledVersion variable must use Delayed Expansion, that is, enclose they between exclamation marks instead percents and include setlocal EnableDelayedExpansion command at beginning of your program.
setlocal EnableDelayedExpansion
set ThisAppFlagFileName=foo.txt
if not exist "%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%" (
ECHO do something here
) else (
SET /P InstalledVersion=<"%HOMEPATH%\ourcompanyname\%ThisAppFlagFileName%"
ECHO We think the file contains: !InstalledVersion!
IF !InstalledVersion! GEQ %ThisVersionInstallDataNum% (
ECHO Version %ThisVersion% of the %ThisAppVisibleName% has already been installed for this user; exiting.
GOTO TheEnd
)
)
:TheEnd
Echo END
You must be aware that all variables that are modified inside parentheses must also be enclosed in exclamation marks instead percent signs. Search for "delayed expansion" for details.

How to test if an executable exists in the %PATH% from a windows batch file?

I'm looking for a simple way to test if an executable exists in the PATH environment variable from a Windows batch file.
Usage of external tools not provided by the OS is not allowed. The minimal Windows version required is Windows XP.
Windows Vista and later versions ship with a program called where.exe that searches for programs in the path. It works like this:
D:\>where notepad
C:\Windows\System32\notepad.exe
C:\Windows\notepad.exe
D:\>where where
C:\Windows\System32\where.exe
For use in a batch file you can use the /q switch, which just sets ERRORLEVEL and doesn't produce any output.
where /q myapplication
IF ERRORLEVEL 1 (
ECHO The application is missing. Ensure it is installed and placed in your PATH.
EXIT /B
) ELSE (
ECHO Application exists. Let's go!
)
Or a simple (but less readable) shorthand version that prints the message and exits your app:
where /q myapplication || ECHO Cound not find app. && EXIT /B
for %%X in (myExecutable.exe) do (set FOUND=%%~$PATH:X)
if defined FOUND ...
If you need this for different extensions, just iterate over PATHEXT:
set FOUND=
for %%e in (%PATHEXT%) do (
for %%X in (myExecutable%%e) do (
if not defined FOUND (
set FOUND=%%~$PATH:X
)
)
)
Could be that where also exists already on legacy Windows versions, but I don't have access to one, so I cannot tell. On my machine the following also works:
where myExecutable
and returns with a non-zero exit code if it couldn't be found. In a batch you probably also want to redirect output to NUL, though.
Keep in mind
Parsing in batch (.bat) files and on the command line differs (because batch files have %0–%9), so you have to double the % there. On the command line this isn't necessary, so for variables are just %X.
Here is a simple solution that attempts to run the application and handles any error afterwards.
file.exe /? 2> NUL
IF NOT %ERRORLEVEL%==9009 ECHO file.exe exists in path
Error code 9009 usually means file not found.
The only downside is that file.exe is actually executed if found (which in some cases is not desiderable).
This can be accomplished via parameter substitution.
%~$PATH:1
This returns the full path of the executable filename in %1, else an empty string.
This does not work with user-defined variables. So if the executable filename is not a parameter to your script, then you need a subroutine. For example:
call :s_which app.exe
if not "%_path%" == "" (
"%_path%"
)
goto :eof
:s_which
setlocal
endlocal & set _path=%~$PATH:1
goto :eof
See http://ss64.com/nt/syntax-args.html
For those looking for a PowerShell option. You can use the Get-Command cmdlet passing two items. First give the current dir location with .\ prefixed, then give just the exe name.
(Get-Command ".\notepad", "notepad" -ErrorAction Ignore -CommandType Application) -ne $null
That will return true if found local or in system wide paths.
#echo off
set found=
set prog=cmd.exe
for %%i in (%path%) do if exist %%i\%prog% set found=%%i
echo "%found%"
if "%found%"=="" ....
Sometimes this simple solution works, where you check to see if the output matches what you expect. The first line runs the command and grabs the last line of standard output.
FOR /F "tokens=*" %%i in (' "xcopy /? 2> nul" ') do SET xcopyoutput=%%i
if "%xcopyoutput%"=="" echo xcopy not in path.
Use command : powershell Test-Path "exe which you looking for"
It will return True if its present, otherwise False.

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.

Resources