I have a batch script that takes arguments from the command line. One of the arguments has a * in it. In spite of putting the argument in quotes, the * gets expanded before the argument gets used in the batch script.
I am using the following code to parse the arguments:
set CMDLINE_ARGS=%~1
shift
:get_args
if "%~1" == "" goto execute
set CMDLINE_ARGS=%CMDLINE_ARGS% %~1
shift
goto :get_args
This works on Windows Server 2003 but not on Server 2008 for some reason.
It has been a long time since I did anything like this but take a look at
SETLOCAL ENABLEDELAYEDEXPANSION
Not sure what Windows Server 2008 defaults to.
Related
I got a batch file that asks for the user input.
The batch script prod_release.bat is like:
set /p CH1=Select the build option:
if '%CH1%'=='1' goto SELECT_APP_L
if '%CH1%'=='2' goto SELECT_APP_M
if '%CH1%'=='3' goto SELECT_APP_H
if '%CH1%'!='1' && '%CH1%'!='2' && '%CH1%'!='3' goto START
I need to give the CH1 value from the command line so the script will automatically take it.
I tried as
set CH1=2 && prod_release.bat
But still, it asks for the user input and waits till it is given.
Can you please help me with this
They can't change this file since they are using this for other purposes.
I can just use this in my application but cannot edit this.
I need help on similar like Powershell and shell scripts too.
I want to run this bat script via Jenkins (or) add to another bat or python application to run with my desired inputs without asking for the user interaction.
I can't change the batch file. Think of it as I can only call it from
another batch file. For example, I have to call this batch file from Jenkins bat"prod_release.bat" then it has to run without waiting for the choice.
Instead of setting CH1=Select the build option: you can set the CH1=%1 and pass the value while executing the file like this
set /p CH1=%1
if '%CH1%'=='1' goto SELECT_APP_L
if '%CH1%'=='2' goto SELECT_APP_M
if '%CH1%'=='3' goto SELECT_APP_H
prod_release.bat 2
Now when file executes value 2 will be assign to CH1 variable. In the same way you can pass whatever value you required for CH1 while file execution example
prod_release.bat 1
A choice prompt should be done with command CHOICE and not with set /P:
:SelectBuildOption
%SystemRoot%\System32\choice.exe /C 123 /N /M "Select the build option:"
if errorlevel 3 goto SELECT_APP_H
if errorlevel 2 goto SELECT_APP_M
if errorlevel 1 goto SELECT_APP_L
goto SelectBuildOption
Please read my answer on How to stop Windows command interpreter from quitting batch file execution on an incorrect user input? It explains in full details the usage of set /P and the usage of CHOICE and explains also the advantages and disadvantages of both solutions for prompting a user for something.
There can be used the following code above the label SelectBuildOption in the batch file prod_release.bat if the batch file is executed by Jenkins with an argument like 1 for build as defined by SELECT_APP_L or 2 for a build as defined by SELECT_APP_M or 3 for a build as defined by SELECT_APP_H.
if "%~1" == "1" goto SELECT_APP_L
if "%~1" == "2" goto SELECT_APP_M
if "%~1" == "3" goto SELECT_APP_H
It would be of course also possible to use other, more meaningful strings than 1 and 2 and 3 for the optional build option like:
if /I "%~1" == "APP_L" goto SELECT_APP_L
if /I "%~1" == "APP_M" goto SELECT_APP_M
if /I "%~1" == "APP_H" goto SELECT_APP_H
With that code the batch file can be called by Jenkins with APP_L or APP_M or APP_H (case-insensitive) without or with being enclosed in ".
The code below the three IF conditions which is the code below the label SelectBuildOption is executed by cmd.exe if none of the three conditions is true because of batch file is called without any argument or the first argument is not equal with one of the three compared strings.
Run call /? in a command prompt to get output the usage help of the command CALL explaining how to reference batch file arguments.
I recommend to read also my answer on Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files. It explains in full details how a string comparison is done by the internal command IF of the Windows Command Processor cmd.exe.
If the Jenkins job defines an environment variable for the build option to use, then just replace %~1 by %VariableName% on the three IF command lines and the batch file works also for usage by Jenkins with build option defined by the environment variable while the batch file can be still used for manual execution.
Example with batch file supporting optionally an environment variable BuildOption defined outside of the batch file by the parent process:
if not defined BuildOption goto SelectBuildOption
if /I "%BuildOption%" == "APP_L" goto SELECT_APP_L
if /I "%BuildOption%" == "APP_M" goto SELECT_APP_M
if /I "%BuildOption%" == "APP_H" goto SELECT_APP_H
It is common practice that scripts used for build processes support optionally either one or more arguments to control the build process or one or more environment variables for automated builds while prompting the user of the script for the necessary options controlling the build process on using the script without the optional argument(s) or without the optional environment variable(s).
So I strongly recommend to contact the author of the batch file and ask to enhance the batch file prod_release.bat as suggested above for easy usage in a fully automated build process. The execution behavior for manual usage does not change with thesse modifications which are important for the fully automated build by Jenkins.
Another possibility would be to output the answer for the prompt and redirect this output from stdout of cmd.exe to stdin of the batch file.
Example:
(echo 1)|prod_release.bat
This command line lets cmd.exe redirect the string with the bytes 0x31 0x0D 0x0A (1 as ASCII character + carriage return + line-feed) to standard input stream of the batch file which is read by first set /P or choice in the batch file from the input stream and therefore results in an automatic answer of the first prompt with (hopefully) an acceptable choice.
But such an approach is very uncommon for scripts used in build processes. A small modification of the called script file like running an executable or command reading first from the input stream could result with this workaround solution in a wrong execution of the script and results therefore again in a not working automated build process. This workaround solution is for that reason really awful in comparison to making the enhancement of the script to support optional arguments or optional environment variables to define the necessary build options by the parent process instead of always prompting a user for the necessary build options.
A good coded script used for a build process supports optional arguments or optional environment variables defined outside of the script to control the options for the build process and prompts for these options if the script is executed without the argument(s) or without the environment variable(s) defined by the parent process, i.e. when the script is executed by a user.
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.
I'm over my head with this - spent too much time searching already - evidently I don't understand the basics of CMD variables etc. - and it always gives me such a headache
why wouldn't this work?
for %a in (*) do ( set tmpx=%a & echo %tmpx% )
the above code outputs the value of %tmpx% in some other scope - and it is always constant
yes, i run setlocal ENABLEDELAYEDEXPANSION
basically i need to do a simple rename of all files in folder from constantstring_somenameXX.tif to somenameXX.tif, where i.e. constantstring=0000000005
i had to use set because other posts rightly suggested that %a in a for loop has a special behaviour, and the substitutions wouldn't work for it as it is.
i would prefer not to use scripts and/or powershell - unless not using them is impossible
thank you
for %a in (*) do ( set tmpx=%a & echo %tmpx% )
The problem with the previous code is delayed expansion. Yes, you enabled it, but you have not used it, and depending on how you enabled it, it will not work
In cmd, when a line or block of lines (code inside parenthesis) is reached, it is first parsed and then executed. During the parse phase, variable read operations are removed from the command, replaced with the value in the variable before the command starts to execute. So, if you change the value of a variable inside a line/block you can not retrieve the changed value inside the same line/block as there are no variable reads (they were replaced)
setlocal enabledelayedexpansion allows to replace (where needed) the variable read syntax from %var% to !var!, indicating to the parser that the read operation will be delayed until the execution phase.
So, in your case, your code should have been something like
setlocal enabledelayedexpansion & for %a in (*) do ( set "tmpx=%a" & echo !tmpx! )
BUT this will not work (in default configured environments).
cmd has two execution modes: batch file and command line. In your case, you are using command line (no escaped percent sign in for loop) and in command line mode the setlocal enabledelayedexpansion will not work. It is intended for batch files (see setlocal /?)
How to make it work from the command line? By default cmd is started with delayed expansion disabled and you can not enable it if not inside a batch file. But you can start cmd with delayed expansion enabled and run your command in this started instance (see cmd /?)
cmd /v:on /c "for %a in (*) do ( set "tmpx=%a" & echo !tmpx! )"
Anyway, to solve your rename problem, delayed expansion is not needed
for %a in (*_*.tif) do for /f "tokens=1,* delims=_" %b in ("%~nxa") do echo ren "%a" "%c"
That is, for each tif file with an underscore, take the name and extension of the file (%~nxa) as a string, and using the underscore as a delimiter between tokens, retrieve the first token (the text on the left of the first underscore) in %b and the rest of the text (to the right of the underscore) into %c. Now, just rename the original file name (stored in %a) to the contents of %c (the text on the right of the underscore)
In this code rename operations are only echoed to console. If the output is correct, remove the echo command.
! is the character to use rather than % when wanting execution time value. % does when it's read value.
CMD was written by IBM engineers and they were trying to make MSDos a programming language while making sure Dos commands ran the same. So we get a hodge podge.
& seperates commands on a line.
&& executes this command only if previous command's errorlevel is 0.
|| (not used above) executes this command only if previous command's errorlevel is NOT 0
> output to a file
>> append output to a file
< input from a file
| output of one command into the input of another command
^ escapes any of the above, including itself, if needed to be passed to a program
" parameters with spaces must be enclosed in quotes
+ used with copy to concatinate files. E.G. copy file1+file2 newfile
, used with copy to indicate missing parameters. This updates the files modified date. E.G. copy /b file1,,
%variablename% a inbuilt or user set environmental variable
!variablename! a user set environmental variable expanded at execution time, turned with SelLocal EnableDelayedExpansion command
%<number> (%1) the nth command line parameter passed to a batch file. %0 is the batchfile's name.
%* (%*) the entire command line.
%<a letter> or %%<a letter> (%A or %%A) the variable in a for loop. Single % sign at command prompt and double % sign in a batch file.
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.
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%