calling batch file with semicolon in parameters from cygwin - windows

I need to call a batch file from inside CYGWIN however one of it's parameters is a path-like string containing semicolons. Normally in windows command line one could enclose that parameter in quotes (which would need to be trimmed later on). However this approach doesn't wok in cygwin
Example batch (echoes first 3 parameters)
echo %1
echo %2
echo %3
Windows cmd call
file.bat "a;b" c
Ouput
"a;b"
c
empty
Cygwin call
./file.bat "a;b" c
Output
a
b
c

Including space anywhere inside quotes will ensure that parameter with semicolon or comma is passed correctly. Although I have to admit that I do not understand this behavior whatsoever, it seems to be working flawlessly.
./file.bat "a;b " c
Output
"a;b"
c
As #jeb mentioned in his comment, enclosing quotes can be trimmed by accessing parameter variable like this
%~1

Recent battles with quoting led me to another technique.
Create a temporary batch file and pass it to cmd. (I used "filex.bat" in this example).
echo 'call file.bat "a;b" c' > filex.bat ; cmd /c filex.bat ; rm filex.bat

Related

proper way to remove double quotes from string in batch

I've got a batch script (app1.bat) calling another batch script (app2.bat) which itself calls a program in windows (program.exe).
app2.bat calls program.exe with a parameter after a flag in this way:
program.exe -f Parameter with whitespaces coming into the program
What I want to do is to pass the phrase that comes to program.exe from app1.bat into app2.bat but i don't know how to properly handle the doublequotes. Currently I am passing the phrase from app1.bat to app2.bat in double quotes and inside an app2.bat (prior to executing program.exe) I get rid of the quotes like that:
inside app1.bat
call app2.bat "Parameter with whitespaces coming into the program"
inside app2.bat
set old_phrase=%1%
set new_phrase=%old_phrase:"=%
program.exe -f %new_phrase%
old_phrase is
"Parameter with whitespaces coming into the program"
and new_phrase I end up with is
Parameter with whitespaces coming into the program
Is there any standard way to handle such a situation (being passing a string to an external program which expects a tring without quotes and being ok with whitespaces, whereas batch does not allow for no-quotes-and-whitespaces strings)
When you execute call /? from cmd to launch the help you will see quite a bit around expansion of %n
The first one states:
%~1 - expands %1 removing any surrounding quotes (")
You can therefore dump all the other set commands and simply run this in your batch file:
program.exe -f %~1

When are double quotes of quoted arguments passed to the called batch file / program?

Here's a simple .bat file that shows the first three arguments with which the bat file is executed:
#echo 1: %1
#echo 2: %2
#echo 3: %3
When I execute the bat file like so
c:\> x:\show_parameters.bat "foo bar" baz "one two three"
the output is
1: "foo bar"
2: baz
3: "one two three"
I was surprised, because I didn't expect the double quotes to be passed as part of the arguments.
When I use a Perl script to show the values of the parameters
my $arg_cnt = 1;
for my $arg (#ARGV) {
printf "%2d: %s\n", $arg_cnt, $arg;
$arg_cnt++;
}
and execute the script like so
c:\> x:\show_parameter.pl "foo bar" baz "one two three"
it prints
1: foo bar
2: baz
3: one two three
that is, without any double quotes. This is the, imho, expected behaviour for the bat variant.
So, Why are the arguments passed differently to the bat file?
TL;DR: It depends on the shell implementation. On windows, the cmd console quoting uses different rules from the bash shell. source
I believe you're looking for:
#echo 1: %~1
#echo 2: %~2
#echo 3: %~3
See the documentation.
The Tilde character has special "modifier" meaning with batch parameters. If you think about it, Perl and batch are two different languages, and when a program is sent parameters, think of it like it's passed a query string, except it's upto the language to decide how to parse it. Really what you pass to the program is one-long parameter but the program splits on spaces while keeping in mind quotes and escaping.
You can also see #echo 0: %0 will show you the program in quotes while #echo 0: %~0 removes the double quotes.
To see the full line of "arguments" passed to the script, without being parsed, you'd do:
#echo *: %*
As you can see, the script is really passed one long argument and has to be parsed first, keeping spaces and quotes, and escape characters such as ^ in mind, in order to create the concept of multiple "arguments".
In terms of Perl's behavior, my guess is that Perl does this automagically for you when populating ARGV. Could check its source code if you're interested in the logic Perl is using.
Edit:
After playing with it for a while, I'm starting to think this is beyond Perl's control. I'm noticing the same behavior with PHP as well when testing print_r($argv); from commandline and it also loses its quotes.
You can see Perl is getting sent the parameters with quotes by running:
use Win32::API;
my $GetCommandLine = Win32::API->new('kernel32',
'GetCommandLine', [ ] , 'P' );
$cmdline = $GetCommandLine->Call();
print $cmdline;
But then you'd need to parse that if you wanted just the parameters and not the full command line command.
There's a question posted here exactly like yours:
http://www.nntp.perl.org/group/perl.beginners/2002/07/msg29597.html
To paraphrasing Peter Scott's answer there on the 2nd page: the shell does the whitespace splitting and dequoting before the program sees the arguments and it makes no difference what language the program is written in. So you'll have to find a workaround.
The answer is pretty consistent that it's a shell issue the more I research it.
For example even in Python, it's the same issue.
So why does batch give different results? It depends on the shell implementation. On windows, the cmd console quoting uses different rules from the bash shell.
consider
call :somesubroutine %*
Without the quotes, somesubroutine would see 6 arguments. With its sees 3.
Really a matter of definition, but batch seems to tell the truth here.
Consider also what happens with
x:\show_parameters.bat "foo bar" "" "one two three"

What is `cmd /s` for?

The Windows command prompt (cmd.exe) has an optional /s parameter, which modifies the behavior of /c (run a particular command and then exit) or /k (run a particular command and then show a shell prompt). This /s parameter evidently has something to do with some arcane quote handling.
The docs are confusing, but as far as I can tell, when you do cmd /csomething, and the something contains quotation marks, then by default cmd will sometimes strip off those quotes, and /s tells it to leave them alone.
What I don't understand is when the quote removal would break anything, because that's the only time /s ("suppress the default quote-removal behavior") would be necessary. It only removes quotes under a certain arcane set of conditions, and one of those conditions is that the first character after the /c must be a quotation mark. So it's not removing quotes around arguments; it's either removing quotes around the path to the EXE you're running, or around the entire command line (or possibly around the first half of the command line, which would be bizarre).
If the path to the EXE is quoted, e.g. cmd /c "c:\tools\foo.exe" arg1 arg2, then quotes are unnecessary, and if cmd wants to remove them, fine. (It won't remove them if the path has a space in the name -- that's another of the arcane rules.) I can't imagine any reason to suppress the quote removal, so /s seems unnecessary.
If the entire command line is quoted, e.g. cmd /c "foo.exe arg1 arg2", then it seems like quote removal would be a necessity, since there's no EXE named foo.exe arg1 arg2 on the system; so it seems like opting out of quote removal using /s would actually break things. (In actual fact, however, it does not break things: cmd /s /c "foo.exe arg1 arg2" works just fine.)
Is there some subtlety to /s that's eluding me? When would it ever be necessary? When would it even make any difference?
Cmd /S is very useful as it saves you having to worry about "quoting quotes". Recall that the /C argument means "execute this command as if I had typed it at the prompt, then quit".
So if you have a complicated command which you want to pass to CMD.exe you either have to remember CMD's argument quoting rules, and properly escape all of the quotes, or use /S, which triggers a special non-parsing rule of "Strip first and last " and treat all other characters as the command to execute unchanged".
You would use it where you want to take advantage of the capabilities of the CMD shell, rather than directly calling another program. For example environment variable expansion, output or input redirection, or using CMD.exe built-ins.
Example:
Use a shell built-in: This executes as-if you had typed DEL /Q/S "%TMP%\TestFile" at the prompt:
CMD.exe /S /C " DEL /Q/S "%TMP%\TestFile" "
This executes SomeCommand.exe redirecting standard output to a temp file and standard error to the same place:
CMD.exe /S /C " "%UserProfile%\SomeCommand.exe" > "%TMP%\TestOutput.txt" 2>&1 "
So what does /S give you extra? Mainly it saves you from having to worry about quoting the quotes. It also helps where you are unsure whether for example an environtment variable contains quote characters. Just say /S and put an extra quote at the beginning and end.
Vaguely Related: $* in Bourne Shell.
Some background
Recall that the list of arguments to main() is a C-ism and Unix-ism. The Unix/Linux shell (e.g. Bourne Shell etc) interprets the command line, un-quotes the arguments, expands wildcards like * to lists of files, and passes a list of arguments to the called program.
So if you say:
$ vi *.txt
The vi command sees for example these arguments:
vi
a.txt
b.txt
c.txt
d.txt
This is because unix/linux operates internally on the basis of "list of arguments".
Windows, which derives ultimately from CP/M and VAX, does not use this system internally. To the operating system, the command line is just a single string of characters. It is the responsibility of the called program to interpret the command line, expand file globs (* etc) and deal with unquoting quoted arguments.
So the arguments expected by C, have to be hacked up by the C runtime library. The operating system only supplies a single string with the arguments in, and if your language is not C (or even if it is) it may not be interpreted as space-separated arguments quoted according to shell rules, but as something completely different.
Here's an example of how it can make a difference.
Suppose you have two executables: c:\Program.exe and c:\Program Files\foo.exe.
If you say
cmd /c "c:\Program Files\foo"
you'll run foo.exe (with no arguments) whereas if you say
cmd /s /c "c:\Program Files\foo"
you'll run Program.exe with Files\foo as the argument.
(Oddly enough, in the first example, if foo.exe didn't exist, Program.exe would run instead.)
Addendum: if you were to type
c:\Program Files\foo
at the command prompt, you would run Program.exe (as happens with cmd /s /c) rather than foo.exe (as happens with just cmd /c). So one reason for using /s would be if you want to make sure a command is parsed in exactly the same way as if it were being typed at the command prompt. This is probably more likely to be desirable in the scenario in the question Michael Burr linked to, where cmd.exe is being launched by CreateProcess rather than from a batch file or the command line itself..
That is, if you say
CreateProcess("cmd.exe", "cmd /s /c \"" MY_COMMAND "\"", ...)
then the string MY_COMMAND will be parsed exactly as if it were typed at the command prompt. If you're taking command-line input from the user, or if you're a library processing a command line provided by an application, that's probably a good idea. For example, the C runtime library system() function might be implemented in this way.
In all but one specific case, the /S won't actually make any difference.
The help for cmd.exe is accurate, if a bit complicated:
If /C or /K is specified, then the remainder of the command line after
the switch is processed as a command line, where the following logic is
used to process quote (") characters:
If all of the following conditions are met, then quote characters
on the command line are preserved:
no /S switch
exactly two quote characters
no special characters between the two quote characters,
where special is one of: &<>()#^|
there are one or more whitespace characters between the
two quote characters
the string between the two quote characters is the name
of an executable file.
Otherwise, old behavior is to see if the first character is
a quote character and if so, strip the leading character and
remove the last quote character on the command line, preserving
any text after the last quote character.
I'd summarize as follows:
Normal behavior:
If the rest of the command line after /K or /C starts with a quote, both that quote and the final quote are removed. (See exception below.) Other than that, no quotes are removed.
Exception:
If the rest of the command line after /K or /C starts with a quote, followed by the name of an executable file, followed by another quote, AND if those are the only two quotes, AND if the file name contains spaces but contains no special characters, then the quotes are not removed (even though they normally would have been removed according to the rule above).
The only effect of /S is to override this one exception, so that the two quote characters are still removed in that case.
If you always use /S, you can forget about the exception and just remember the "normal" case. The downside is that cmd.exe /S /C "file name with spaces.exe" argument1 won't work without adding an extra set of quotes, whereas without /S it would have worked... until you decide to replace argument1 with "argument1".

using the DOS start command when passed arguments have quotes

I have a question about the DOS start command.
I have already read this topic:
Using the DOS “start” command with parameters passed to the started program
Using the "start" command with parameters passed to the started program
but my question is a little different.
I have this problem: I need to pass paths that need to be quoted.
For example, if path have no quotes this works fine:
start "" app.exe -option c:\myapp\myfile.txt
but if path have double quotes it doesn't works.
I have this line in my BATCH file:
start "" myapp.exe -option %mypath%
and when %mypath% contains double quotes (paths that have spaces or other characters in names) the start command returns very strange results.
Thanks
Sandro
Normally it's not a problem to use parameters there with quotes, but you get problems if your app-path has also quotes.
Then you need to add an extra CALL statement.
start "" app.exe -option c:\myapp\myfile.txt - Works
start "" app.exe -option "c:\myapp\myfile.txt" - Works
start "" "app.exe" -option c:\myapp\myfile.txt - Works
start "" "app.exe" -option "c:\myapp\myfile.txt" - Don't works
start "" CALL "app.exe" -option "c:\myapp\myfile.txt" - Works
This might help, but it is a bit way round about method and slight modification may required to suit your need.
The idea is to:
Dump the environment variable which has quotes to a text file with a predefined name. Like:"set mypath2 > withQt.bat"
Use windows power shell or some third party tool to find and replace quotes in that file.
Create another text file (one time step only) containing string "Set "
Use copy command to append the file mentioned in step2 with the file created in step3 and create a batch file with a predefined name. Like: copy base.bat + withQt.bat withtqt.bat
Run the batch file, which creates another/replaces the environment variable with value without quotes.
Sorry, I couldn't get something more elegant at this time.

Batch file equivalent of Unix parameter expansion with quotes

There have been a lot of questions asked and answered about batch file parameters with regards to the %*, but I haven't found an answer for this.
Is there an equivalent syntax in batch files that can perform the same behavior as "$#" in Unix?
Some context:
#echo off
set MYPATH=%~dp0
set PYTHON=%MYPATH%..\python\python
set BASENAME=%~n0
set XTPY=%MYPATH%..\SGTools\bin\%BASENAME%.py
"%PYTHON%" "%XTPY%" %*
This is the .bat file that is being used a proxy to call a Python script. So I am passing all the parameters (except the script name) to the Python script. This works fine until there is a parameter in quotes and/or contains spaces.
In shell scripts you can use "$#" to take each parameter and enclose it in quotes. Is there something I can do to replicate this process?
Example calls:
xt -T sg -t "path with possible spaces" -sum "name with spaces" -p <tool_name> -o lin32 lin64 win32 <lots of other options with possibilities of spaces>
The command/file xt simply contains the code listed above, because the actual executable is Python code in a different folder. So the point is to create a self-contained package where you only add one directory (xbin directory) to your path.
I'm not sure what the cleanest solution is, but here is how I worked around the problem:
setlocal EnableDelayedExpansion
for %%i in (%*) do set _args= !_args! "%%~i"
echo %_args%
%_args% will now contain a quoted list of each individual parameter. For example, if you called the batch file as follows:
MYBATFILE "test'1'file" "test'2'file" "test 3 file"`
echo %_args%
will produce the original quoted input.
I needed this for CMD files that take unfriendly file or directory names and pass them to Cygwin Bash shell scripts which do the heavy lifting, but I couldn't afford to have the embedded single quotes or spaces lost in the transition.
Note the ~i in %%~i% which is necessary to remove quotes before we apply quotes. When a parameter containing spaces is passed (e.g., "test 3 file" above), the CMD shell will already have applied quotes to it. The tilde here makes sure that we don't double-quote parameters containing spaces.

Resources