Batch - How to pass argument with quotes within other quotes - batch-file

I'm using dotCover command line tool to run coverage on some Tests.
In order for it to run it needs to receive as arguments the path of the "Target executable" which in my case is Nunit and "Target Arguments" which is in my case are the arguments I pass to Nunit.
The thing is that one of the arguments I pass to Nunit is a path with white spaces. And when I pass the arguments of Nunit to dotCover it is also surrounded with quotes because it has white spaces.
So, for example to run the tests simply on nunit I run the command:
"%NunitDir%\nunit-console-x86.exe" /nologo /noshadow "%DllDir%\Tests.dll"
/config=Release /domain=%Domain% /xml=%resultDir%\NUnitTestResults.xml
and to run coverage on tests I need to run something like:
set NunitArgs=/nologo /noshadow "%DllDir%\Tests.dll"
/config=Release /domain=%Domain% /xml=%resultDir%\NUnitTestResults.xml
%dotCoverDir%\dotCover.exe cover /TargetExecutable="%NunitDir%\nunit-console-x86.exe"
/TargetArguments="%NunitArgs%" /Output="%outputDir%\NUnitTestResults.xml"
The problem is that NunitArgs already contain quotes, and when I run the dotCover command it only read the arguments from first quotes to the second quotes.

Try /TargetArguments="%NunitArgs:"=""%" or even /TargetArguments="""%NunitArgs:"=""%""".
Read How Command Line Parameters Are Parsed by David Deley © 2009 (Updated 2014) (especially Windows®: 4. Everyone Parses Differently). Good luck!

Related

Batch Scripting percent tilde string and starting a process syntax understanding

I need help with understanding the following Batch script structure:
This is named Profile_something_schedule.bat
call somePath\lib.cmd :someLabel reqPath
call somePath\lib.cmd :someLabel reqKey
%reqPath% "%~someFileName" /vv_pwd=%reqKey% /bProfile_something_schedule /min
I have a lot of difficulty understanding why this script works.
I do not know why the 3rd line is valid. The behavior produced is that the someFileName is run. I understand it as starting the file as a process. Then why isn't the start command needed? I don't see any batch documentation saying you can simply run a file by writing its pathed filename.
I do not understand the syntax of "%~someFileName". From online searching about it almost every source shows you the batch call parameter table, saying things like %~1 expands %1..., %~f1 expands %1 some other way, etc. All of them involve some kind of number from 0 to 9 to correspond to the parameter position. However, I cannot find any specification of %~someString being legal. There is no parameter positional information from the someFileName string, it is a filename.extension string. Still, it is quite likely this line is trying to run this format.
What does "/vv_pwd=%reqKey% /bProfile_something_schedule" mean? In the lib.cmd that was called previously, there were variables reqPath and reqKey and I am quite certain it is trying to pass the value of reqPath and reqKey from the lib.cmd into the variables here and then I guess it is trying to use the reqKey value as a parameter, which is a password required to run the file.
Inspecting the file, it contains some script of some paid software specific format, it only has variable name v_pwd inside but not vv_pwd. I do not know what the /bProfile_... is for. The part without the /b is exactly this batch file's name. But together with the /b I don't know what it means. The /v and /b look like some kind of options to me but I cannot see any specification explaining as there is no command beginning line 3 just some path. I guess the /min option refers to starting window minimized which is an option for the command start, yet there is no option of /v. The /B in start means to start application without creating window, which is quite unnecessary to have /min if you are not going to create a window in the first place. And it doesn't make sense to use /B directly followed by some string of Profile_something_Schedule.
FYI, the lib.cmd starts with call %*, which I would consider as trying to call all passed parameters and assuming those parameters are actually batch files that can be called.
Another thought I have is that the 2nd line call connects with the 3rd line so the 3rd line may not need a command. But I can't make sense of it. The someFileName is not of the Batch extension so I doubt it can be called as the call doc says it is for batch programs. If I want to run non-batch programs I need to use start right?
Would greatly appreciate your help!
The variable pathext contains a semicolon-separated list of executable filenames that may be appended as an extension to myexecutable. if the first string on a batch line is not a cmd internal command (like set, for etc.) then cmd tries to find myexecutable + each of the extensions in pathext in turn, first in the current directory, and then in each directory in the path (another semicolon-separated list of directories) and runs the first name found, or fails if none are found. That first string may also have an Associated extension, which then runs the application with which the extension is associated (like .txt runs notepad by default)
Neither do I, and I can't see that even knowing what the actual strings being executed by %reqPath% are would assist. See for /? from the prompt for more documentation on other ~ operators - or search SO for thousands of uses.
vv_pwd=%reqKey% : %reqKey% is replaced by the value of the variable reqKey evidently returned by the previous line. / is used in Windows to mean "here's a switch parameter for the executable", so evidently /vv_pwd=[the contents of reqKey], /bProfile_something_schedule and /min mean something to the executable %reqPath%. Quite what is anyone's guess.
The fact that lib.cmd's first line is call %* would mean that lib.cmd contains a library of routines. Since each call you have shown is of the form :string1 string2 then the resultant command executed would be call :string1 string2. call :string1 will call the routine contained within "lib.cmd" with the label string1: supplying string2 (and presumably optionally string2 string3... as parameters. Evidently, string2 is the name of the variable into which lib.cmd places the required data.
Without the :, string1 would be any executable that cmd can locate using the method in (1). It does not have to be a batch, but commonly is a batch.

What is the proper way to escape quotes and slashes when running a Jest test with --collectCoverageFrom from Powershell via npm scripts?

I am a React developer, and am used to running commands like the example below from Bash in Linux, but now I have to make it work in a Windows environment from Powershell:
npm test ExampleComponent --collectCoverageFrom='["**/ExampleComponent.tsx"]' --watch
The npm test script just has "jest" in it, but I need to use it via the script so that I am using the version of jest in the project, not a global.
I use this when trying to fill-in gaps in code coverage in a specific file (ExampleComponent.tsx), so I want Jest to reassess the coverage for just that one file when I save changes to the test file (ExampleComponent.test.tsx in this case).
This is what I ran in Powershell (Added the quotes in "--" because Powershell doesn't treat -- as bash does, and switched from forward slash to backslash for Windows):
npm test "--" ExampleComponent --collectCoverageFrom='["**\ExampleComponent.tsx"]' --watch
This is how jest gets called as per the output from npm:
jest "ExampleComponent" "--collectCoverageFrom=[**\ExampleComponent.tsx]" "--watch"
In Powershell (version 7.2, if that matters), the command above doesn't restrict the coverage check to the specified file. It does only test the specified file, but the collectCoverageFrom option is ignored presumably because the pattern jest receives is mangled by Powershell.
How can I escape the quotes and slashes from a Powershell commandline to replicate the bash invocation at the top? Based on answers regarding escaping in Powershell, I've tried combinations of multiple backslashes and multiple quotes in a myriad of permutations, but none of them have worked so far.
Update as of Powershell version 7.2.1:
The triple-backslash doesn't seem to be necessary anymore; the syntax in this answer's previous version is no longer working for me. But the following works:
npm t "--" ExampleComponent --collectCoverageFrom="'[\""**/ExampleComponent.tsx\""]'"
Jest echoes back the same as before as mentioned below with this syntax.
Previous version:
I have found a version that works:
npm t "--" ExampleComponent --collectCoverageFrom="'[\\\""**/ExampleComponent.tsx\\\""]'"
The command seems to be running as expected, calculating coverage for the specified file only.
Jest echoes back the command as:
jest "ExampleComponent" "--collectCoverageFrom='[\"**/ExampleComponent.tsx\"]'"
The processed version has backslashes before the double-quotes since the entire argument is already wrapped in double-quotes. I also had to change the backslash back to a forward slash.
Thanks to #mklement0 for his input in the question's comments.
Your solution is effective, but let me add important background information:
The core problem is the sad reality that up to at least PowerShell 7.2 an extra, manual layer of \ -escaping of embedded " characters is required in arguments passed to external programs. This may get fixed in a future version, which may require opt-in. See this answer for details.
In your particular case, the problem is apparently compounded by having to perform this escaping twice, because two calls are involved: first, to npm, which then relays some of the arguments it received to jest.
Ultimately, what you want jest to see verbatim, after it has parsed its own command line (on Windows) is the following string:
--collectCoverageFrom='["**/ExampleComponent.tsx"]'
As an aside: Your bash command does not achieve that, because the ' have syntactic function (as they do in PowerShell) and are removed by bash before the resulting string is passed on.
If the PowerShell bug weren't in the picture, you'd use the following:
npm t '--' ExampleComponent --collectCoverageFrom='''["**/ExampleComponent.tsx"]'''
Note the '' embedded in the overall '...' verbatim string, which are escaped ' chars. to be included verbatim in the string.
Alternatively, you could use "..." for the outer quoting, which then requires you to escaped the embedded " chars. as `" (or ""[1]):
npm t '--' ExampleComponent --collectCoverageFrom="'[`"**/ExampleComponent.tsx`"]'"
Note, however, that such double-quoted strings are expandable strings, i.e. subject to string interpolation. It's generally better to use verbatim strings ('...') for strings that do not require interpolation, for conceptual clarity and to prevent potentially unwanted interpolation (not a concern here, given that the string contains no meant-to-be verbatim $ or `characters).
With no bug regarding embedded " characters present, there is also no problem with relaying arguments - irrespective of the number of times this is performed.
In fact, if you're on PowerShell 7.2, you can make this work, but only if you explicitly enable the PSNativeCommandArgumentPassing experimental feature:
Enable-ExperimentalFeature PSNativeCommandArgumentPassing
Then start a new session for the change to take effect.
In the new session, run $PSNativeCommandArgumentPassing = 'Standard' so as to also apply the fix to batch files (*.cmd, *.bat), which is necessary because the npm CLI is implemented as one.
This unfortunate need for an additional, per-session step is the result of a highly unfortunate design decision to by default (value 'Windows') exempt batch files and WSH script files (JScript, VBScript) from the fix, instead of applying target scripting engine-appropriate fixes automatically - see the conversation in GitHub issue #15143
Run Disable-ExperimentalFeature PSNativeCommandArgumentPassing later to disable it again.
With the bug present:
The embedded " characters in your string require manual \-escaping, due to the bug - irrespective of whether you use a '...' or a "..." string:
If only one call were involved, this means:
# Note the \-escaped "
npm t '--' ExampleComponent --collectCoverageFrom='''[\"**/ExampleComponent.tsx\"]'''
This would make npm see the desired verbatim string.
Because npm then relays the argument in question to jest, an additional round of escaping must be applied:
# Note the \-escaped ", preceded by an \-escaped \
npm t '--' ExampleComponent --collectCoverageFrom='''[\\\"**/ExampleComponent.tsx\\\"]'''
That is:
The call to npm effectively turns the \" to "
Because the argument is then relayed it must again be manually \-escaped, so that the " chars. are also preserved in the call to jest.
By prepending \\ - i.e. an escaped \ to - \" (\\\"), npm ends up seeing verbatim \", which, when relayed via PowerShell, makes jest see just ", as desired.
[1] While "" works fine inside "...", it works only there. By contrast, `" uses PowerShell's general escape character, the backtick (`) - and having to remember only that one character, irrespective of context, reduces the memory burden for the user.

Eiffel: how do I set the command line arguments of an autotest?

Some of my tests needs something like
{EXECUTION_ENVIRONMENT}.arguments.separate_character_option_value ('l')
to be attached (not Void), how do I set commandline arguments to eiffel autotest?
At the moment there is no support to run Autotest from the command line and pass arguments, we are looking into it.
So, for now, It's possible to launch test cases from the command line using something like
ec -config testing.ecf -tests
but there is no way to filter test cases or even pass command line arguments.
But there is a workaround
You can set environment variables before to run the test cases from the command line or EiffelStudio IDE.
Define all the environment variables that you need to mimic the arguments
export ARGUMENT_C=my_value or set ARGUMENT_C=my_value.
Call the test cases from the command line or IDE
ec -config testing.ecf -tests
The test cases that need to get access to these values need to do something like this
if attached {EXECUTION_ENVIRONMENT}.get ("ARGUMENT_C") as l_val then
-- do something
end
Hope this helps

My batch script is reporting an error stating "was unexpected at this time"

The Windows batch file I am trying to run contains the following assignment:
set ANT_HOME="C:/Program Files/apache-ant-1.8.4"
The offending line is:
call %ANT_HOME%/bin/ant -f ../config/common.xml start_db
And when I run the script with echo on I get:
call "C:/Program_Files/apache-ant-1.8.4"/bin/ant -f ../config/common.xml start_db
Files/apache-ant-1.8.4""=="" was unexpected at this time.
I've moved the second quote to to the end of the path, after the ant, but I receive the same error message.
If ant were an .exe I would say your code should work. But I suspect that ant is a batch file, and the error is occurring within the ant script.
I base my conclusion on your error message - specifically the following portion of it: ""=="". The error message is a batch parsing error, and I don't see how your code could generate those characters. So I figure ant must be a batch script that is causing the problem.
I suspect ant.bat has #echo off at the top, so you are not seeing the actual line that is failing.
Not having access to the ant.bat script, I couldn't possibly diagnose exactly what is failing, nor can I guess on how to fix it.
Update - exact problem found
I found a copy of ant.bat online.
It has the following line of code within:
if "%ANT_HOME%"=="" set ANT_HOME=%DEFAULT_ANT_HOME%
Your definition of ANT_HOME includes enclosing quotes, so the code is trying to execute
if ""C:/Program Files/apache-ant-1.8.4""=="" set ANT_HOME=%DEFAULT_ANT_HOME%
The space is not quoted, and you have your error.
All you need to do to fix everything is to remove the quotes from the definition of ANT_HOME, and then add quotes to your CALL statement:
set "ANT_HOME=C:/Program Files/apache-ant-1.8.4"
call "%ANT_HOME%/bin/ant" -f ../config/common.xml start_db
Forward-slashes are not always reliable as folder delimiters within Windows. See Why does the cmd.exe shell on Windows fail with paths using a forward-slash ('/'') path separator?.
Better to use back-slashes.
set "ANT_HOME=C:\Program Files\apache-ant-1.8.4"
call "%ANT_HOME%\bin\ant" -f ..\config\common.xml start_db
The quotes have to completely surround a file name. You can't use them for partial names. Try this instead:
set ANT_HOME=C:\Program Files\apache-ant-1.8.4
call "%ANT_HOME%\bin\ant" -f ../config/common.xml start_db
Oh, and I changed some of your slashes to backslashes (DOS doesn't like forward slashes). I assume you are allowed to use / in that parameter you are passing though.

Stupid Batch File Behavior. Tries to execute comments

I have tried prefixing lines with semicolons, 'REM', etc.. but no matter what when I run my batch file I keep getting "unknown command REM whatever"
"REM test" It is not recognized, and it is windows vista. I simply get "rem" output back to my console.
That's entirely normal behavior. Batch files are simply sequences of commands that are run one after another. So every line will get output to the console as if it were typed there.
H:\>echo rem test > test.cmd
H:\>test
yields the output
H:\>rem test
as if I typed rem test directly to the console.
You can suppress this by either prefixing the line with #:
#rem test
or by including echo off in the batch file:
#echo off
rem test
If I put ":: test" and execute it I get back "Test".
Can't reproduce here.
If I put "; test" it recursively executes itself
A semicolon at the start of the line seemingly gets ignored.
If you're talking about cmd.exe batch files under Windows, you can use:
rem this method or
:: this method.
For bash and a lot of other UNIX-type shells, you use:
# this method.
I'm pretty certain you're not using cmd.exe since that would give you an error like:
'rem' is not recognized as an internal or external command,
operable program or batch file.
rather then:
Unknown command ...
If you are using a UNIX-type shell, the # character is almost certainly what you're after. If you let us know exactly the shell you're using, we can probably help out further.
you probably created an UNICODE file. These files contain 2 bytes header named BOM
which is not shown by any editor but cmd attempts to execute them and fails.
To make sure this is indeed an issue: type any other command at the very beginning
of your file and see it throws the same error - for example #echo test
To fix it, just create a new plain text file and copy content of the original file there.
then remove the original file and replace it by the newly created one.
In my case the problems are line endings. Somehow Maven or the Jenkins pipeline running on a Linux machine changed the line endings from Windows style (CR LF) to Unix style (LF). Changing them back solves the issue for me.

Resources