C# run process with multiple spaces in arguments - c#

I'm trying to start a process which contains multiple spaces within the arguments. The arguments that are passed are dynamically built. For example:
// These three strings will be built dynamically
string consolePath = "C:\\My Path\\nunit3-console.exe"
string dll = "C:\\My Path\\My.Test.dll"
string where = "--where \"test == My.Test.Example \""
string cmdText = $" \"{consolePath }\" \"{dll}\" {where}";
//cmdText = "\"C:\\My Path\\nunit3-console.exe\" \"C:\\My Path\\My.Test.dll\" --where \"test == My.Test.Example \""
var processInfo = new ProcessStartInfo("cmd.exe", $"/c {cmdText}");
processInfo.CreateNoWindow = false;
Process process = Process.Start(processInfo);
process.WaitForExit();
process.Close();
This does not work as any text beyond the first space will be ignored. I will get a message such as 'C:\My' is not recognized as an internal or external command, operable program or batch file.
I tried adding parentheses around the arguments, as noted here, but it didn't work. What is the correct way to do this?

You probably have to add a further double-quote around anything that may include spaces within one single argument. Usually a space means the end of an argument. So to preserve this, you´d have to put the string into double-quotes.
So consolePath should actually be this:
var consolePath = "\"C:\\My Path....exe\"";

In addition to previous answer, # could be used to avoid \\ like this:
#"""C:\My Path\nunit3-console.exe"""
or:
"\"" + #"C:\My Path\nunit3-console.exe" + "\""
More about # here:
What's the # in front of a string in C#?

After searching two days for working string I finally found it.
Just wrap string like this:
string Arguments = "/c ""path" --argument "";
string Arguments = "/c \""path" --argument \"";
Notice bold quotes from first string.

Related

Remove \ from end of value

I have the following code:
if (BrowserName.ToUpper().Contains("FIREFOX"))
privateModeParam = " -private-window";
string extraspeech = "\"";
string both = BrowserName + extraspeech + privateModeParam;
Process.Start(both, URLFromDB);
When it run's it returns the following value:
BrowserName = "c:\\program files\\mozilla firefox\\firefox.exe"
both = "c:\\program files\\mozilla firefox\\firefox.exe\" -private-window"
privateModeParam = " -private-window"
What I need to do is, trim the \ from both string because it won't open firefox with that back slash.
I should add if I simply do:
string both = BrowserName + privateModeParam;
the value returned is "c:\program files\mozilla firefox\firefox.exe\ -private-window"
what won't open Firefox
What causes your problems is the double-quote ("), not the back-slash. There is no backslash at this position in the string, it's only displayed like that by your debugger because c# uses \ to escape things like " inside string literals.
So your problem seems to be that you forgot to add extraspeech before the executable, too:
string both = extraspeech + BrowserName + extraspeech + privateModeParam;
or better
string both = $"\"{BrowserName}\" {privateModeParam}"; // C#6
string both = string.Format("\"{0}\" {1}", BrowserName, privateModeParam); // pre C#6
Update:
But the real problem here seems to be that you pass one command line argument in the fileName parameter and one via the arguments parameter.
You should actually call Process.Start like that:
Process.Start(BrowserName, $"{privateModeParam} {URLFromDb}");
Simply pass all arguments in side the arguments parameter. Then there is also no need to wrap the executable in double-quotes as it is the only string in the fileName argument. See documentation for more information about the parameters to Process.Start().
The easiest way would be to use Substring:
MyString = MyString.Substring(0, MyString.Length - 1);
if (BrowserName.EndsWith("\\")){
BrowserName = BrowserName.Substring(0, BrowserName.Length - 1);
}
or
both = both.Replace("\\\"", "");
may fix your problem

What is the correct syntax for passing multiple arguments

I have this code to start a command line app:
private void LaunchCommandLineApp(string latestStudents, string latestTopics)
{
// Use ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = false;
startInfo.FileName = "ConsoleApplication2.exe";
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments =
try
{
// Start the process with the info we specified.
// Call WaitForExit and then the using statement will close.
using (Process exeProcess = Process.Start(startInfo))
{
exeProcess.WaitForExit();
}
}
catch
{
// Log error.
}
}
What is the correct syntax for passing latestStudents & latestTopics at the line startInfo.Arguments = as arguments? I've tried everything I can think of and some but I still don't get it!
Arguments is a string, which the documentation unhelpfully says is interpreted entirely by the target application. It does say now .NET applications will interpret it, so it really depends on what process you're launching.
The only way to know how to make that arguments string do the right thing for the process you're trying to pass it to is to find out how that process handles its arguments (try running it from the command line if you need to experiment). Mostly you can expect it to expect them to be separated with spaces. It's possible that you can just do something like (assuming C# 6):
$"{latestStudents} {latestTopics}"
But that might not work, depending on what's inside those variables. They may need to be quoted, especially if they contain spaces themselves.
There really is no definitive answer I can give you.
It depends on the program that is interpreting the arguments, but generally if you separate the arguments with spaces then they will be presented to the program as an array of strings.
For example, if you specify the argument string as:
startInfo.Arguments = "one two three \"fo ur\" \"\\\"fi ve\"\\\""
Then if the program is a C# console application, the Main(string[] args) method will receive an args array as follows:
args[0] == "one"
args[1] == "two"
args[2] == "three"
args[3] == "fo ur"
args[4] == "\"fi ve\""
Note that consecutive spaces such as those between "two" and "three" in my example are ignored.
Also note that the quotes around "fo ur" cause that to be passed as a single argument.
Finally, note that if you want to pass quotes as part of a parameter, you have to escape them with a backslash. In C#, of course, you have to escape the backslash and the quotes, so instead of "\"fi ve\"" in my example, I have to write the even-more unwieldy \"\\\"fi ve\"\\\""
ProcessStartInfo arguments are passed as a string, similar to if you were running ConsoleApplication2.exe via a command line.
For example if you had a command window open in Windows and were to run something like ConsoleApplication2.exe /help, that would be passing "/help" in as a command argument.
So for your case (and it depends how ConsoleApplication2.exe is coded), you'll want to do something like:
startInfo.Arguments = latestStudents + " " latestTopics;
...assuming ConsoleApplication2.exe accepts those two parameters in that order.
Examples:
ProcessStartInfo startInfo = new ProcessStartInfo("argsecho.exe");
startInfo.WindowStyle = ProcessWindowStyle.Normal;
// Start with one argument.
// Output of ArgsEcho:
// [0]=/a
startInfo.Arguments = "/a";
Process.Start(startInfo);
// Start with multiple arguments separated by spaces.
// Output of ArgsEcho:
// [0] = /a
// [1] = /b
// [2] = c:\temp
startInfo.Arguments = "/a /b c:\\temp";
Process.Start(startInfo);
// An argument with spaces inside quotes is interpreted as multiple arguments.
// Output of ArgsEcho:
// [0] = /a
// [1] = literal string arg
startInfo.Arguments = "/a \"literal string arg\"";
Process.Start(startInfo);
// An argument inside double quotes is interpreted as if the quote weren't there,
// that is, as separate arguments. Equivalent verbatim string is #"/a /b:""string with quotes"""
// Output of ArgsEcho:
// [0] = /a
// [1] = /b:string
// [2] = in
// [3] = double
// [4] = quotes
startInfo.Arguments = "/a /b:\"\"string in double quotes\"\"";
Process.Start(startInfo);
// Triple-escape quotation marks to include the character in the final argument received
// by the target process. Equivalent verbatim string: #"/a /b:""""""quoted string""""""";
// [0] = /a
// [1] = /b:"quoted string"
startInfo.Arguments = "/a /b:\"\"\"quoted string\"\"\"";
Process.Start(startInfo);
It is amazing what you can find while Googling for something...
This is the link I used as source for this above code samples.

command line using string.Format() to handle escape characters

Thank for reading my thread.
Here is the command line I like to call within my C# code:
C:\>"D:\fiji-win64\Fiji.app\ImageJ-win64.exe" -eval "run('Bio-Formats','open=D:\\Output\\Untitiled032\\ChanA_0001_0001_0001_0001.tif display_ome-xml')"
This is the exact command I can see from my console window, and it runs and gives me what I need. I want to run this command line from from my C# code, so there is escape character problem I don;t know how to handle
There are two strings I'd like to make them flexible
D:\fiji-win64\Fiji.app\ImageJ-win64.exe
D:\Output\Untitiled032\ChanA_0001_0001_0001_0001.tif
I am wondering how I can use string.Format() to formulate this command line?
This is my current code, it opens the image, but the display_ome-xml did not get called:
string bioformats = "Bio-Formats";
string options = string.Format("open={0} display_ome-xml", fileName.Replace("\\", "\\\\"));
string runCommand = string.Format("run(\"'{0}'\",\"'{1}'\")", bioformats, options);
string fijiCmdText = string.Format("/C \"\"{0}\" -eval {1}", fijiExeFile, runCommand);
where fijiExeFile works fins, it is just the runCommand keeps ignoring the display_ome-xml. Anyone has any suggestions? It is really really confusing. Thanks a lot.
As #Kristian pointed out, # can help here. It also appears that there may be some extra or misplaced \" in the code above. This seems to give the desired output string:
string fijiExeFile = #"D:\fiji-win64\Fiji.app\ImageJ-win64.exe";
string fileName = #"D:\\Output\\Untitiled032\\ChanA_0001_0001_0001_0001.tif";
string bioformats = "Bio-Formats";
string options = string.Format("open={0} display_ome-xml", fileName);
string runCommand = string.Format("run('{0}','{1}')", bioformats, options);
string fijiCmdText = string.Format("\"{0}\" -eval \"{1}\"", fijiExeFile, runCommand);
The easiest way is to use the verbatim string literal.
Just put a # before your string like so:
#"c:\abcd\efgh"
This will disable the backslash escape character
If you need " inside your string, you will have to escape the the quotation marks like so:
#"c:\abcd\efgh.exe ""param1"""
Your example could be:
String.Format(#"""{0}"" -eval ""run('Bio-Formats','open={1} display_ome-xml')""", #"D:\fiji-win64\Fiji.app\ImageJ-win64.exe", #"D:\Output\Untitiled032\ChanA_0001_0001_0001_0001.tif")
or
string p1 = "D:\\fiji-win64\\Fiji.app\\ImageJ-win64.exe";
string p2 = "D:\\Output\\Untitiled032\\ChanA_0001_0001_0001_0001.tif";
String.Format(#"""{0}"" -eval ""run('Bio-Formats','open={1} display_ome-xml')""", p1, p2);

C# Format String with double quotes around directories with spaces

I am trying to use a String.Format to create the following string
2MSFX.exe "C:\Users\Avidan\Documents\Visual Studio 2010\Projects\DefferedRenderer\DummyGame\DummyGameContent\Shaders\Clear.fx" "C:\Users\Avidan\Documents\Visual Studio 2010\Projects\DefferedRenderer\DummyGame\DummyGameContent\Shaders\Clear.mxfb"
so i am trying to use String.Format, but i just can't seen to get my head around it for some reason :|
The code is (where last 2 params are String.Empty):
String outputFile = Path.Combine(destDir, Path.ChangeExtension(Path.GetFileName(fxFile), "mgxf"));
String command = String.Format("\"{0}\" \"{1}\" \"{2}\" \"{3}\"", Path.GetFullPath(fxFile), Path.GetFullPath(outputFile), DX11Support, DebugSupport);
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = MGFXApp,
Arguments = command,
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
But that appears to be giving me
\"C:\Users\Avidan\Documents\Visual Studio 2010\Projects\DefferedRenderer\DummyGame\DummyGameContent\Shaders\ClearGBuffer.fx\" \"C:\Users\Avidan\Documents\Visual Studio 2010\Projects\DefferedRenderer\DummyGame\DummyGameContent\Shaders\MGFX\ClearGBuffer.mgxf\" \"\" \"\"
If i use the verbatim string i can't get it to create the string i want.
Any Ideas?
Thanks.
Update
You should use String.Concat().
String.Concat("\"", Path.GetFullPath(fxFile), "\" " , Path.GetFullPath(outputFile), "\" " DX11Support,"\" " ,DebugSupport, "\"")
For a simple case like this, I wouldn't think it necessary, but you could create an extension method to automatically put quotes around the strings.
public static class StringExtensions
{
public static string Quotify(this string s)
{
return string.Format("\"{0}\"", s);
}
}
Then your command format looks like this:
String command = String.Join(" ",
Path.GetFullPath(fxFile).Quotify(),
Path.GetFullPath(outputFile).Quotify(),
DX11Support.Quotify(), DebugSupport.Quotify());
You need to use a combination of the # literal to avoid '\' squirlyness, and ""'s to make "'s
This example works for me:
string s = #"""C:\Test Dir\file.fx"" ""C:\Test Dir\SubDir\input.dat""";
Console.WriteLine(s);
Console output looks like this:
"C:\Test Dir\file.fx" "C:\Test Dir\SubDir\input.dat"
Just remember that two quotes makes a single quote, so the triple quote at the beginning and end of the string are the quote to start the string definition, and then a double quote to make a quote. Possibly one of the more confusing string formats out there, but that's how it works.

How do I launch a subprocess in C# with an argv? (Or convert agrv to a legal arg string)

I have a C# command-line application that I need to run in windows and under mono in unix. At some point I want to launch a subprocess given a set of arbitrary paramaters passed in via the command line. For instance:
Usage: mycommandline [-args] -- [arbitrary program]
Unfortunately, System.Diagnostics.ProcessStartInfo only takes a string for args. This is a problem for commands such as:
./my_commandline myarg1 myarg2 -- grep "a b c" foo.txt
In this case argv looks like :
argv = {"my_commandline", "myarg1", "myarg2", "--", "grep", "a b c", "foo.txt"}
Note that the quotes around "a b c" are stripped by the shell so if I simply concatenate the arguments in order to create the arg string for ProcessStartInfo I get:
args = "my_commandline myarg1 myarg2 -- grep a b c foo.txt"
Which is not what I want.
Is there a simple way to either pass an argv to subprocess launch under C# OR to convert an arbitrary argv into a string which is legal for windows and linux shell?
Any help would be greatly appreciated.
MSDN has a description of how the MS Visual C Runtime parses the string returned by GetCommandLine() into an argv array.
You might also be interested in the list2cmdline() function from the Python standard library that is used by Python's subprocess module to emulate the Unix argv behavior in a Win32 environment.
In windowsland, it's simple really...add extra quotation marks in the string you pass to the System.Diagnostics.ProcessStartInfo object.
e.g. "./my_commandline" "myarg1 myarg2 -- grep \"a b c\" foo.txt"
Thanks to all for the suggestions. I ended up using the algorithm from shquote (http://www.daemon-systems.org/man/shquote.3.html).
/**
* Let's assume 'command' contains a collection of strings each of which is an
* argument to our subprocess (it does not include arg0).
*/
string args = "";
string curArg;
foreach (String s in command) {
curArg = s.Replace("'", "'\\''"); // 1.) Replace ' with '\''
curArg = "'"+curArg+"'"; // 2.) Surround with 's
// 3.) Is removal of unnecessary ' pairs. This is non-trivial and unecessary
args += " " + curArg;
}
I've only tested this on linux. For windows you can just concatenate the args.
You will need to run a new subprocess using grep and all arguments that grep will be needing.
void runProcess(string processName, string args)
{
using (Process p = new Process())
{
ProcessStartInfo info = new ProcessStartInfo(processName);
info.Arguments = args;
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
string output = p.StandardOutput.ReadToEnd();
// process output
}
}
then make a call to runProcess("grep", "a", "b", "c", "foo.txt");
Edit: Updated args handling.
Just use a Regex to check if a string has spaces of any kind, and replace the original string with a new one with quotes:
using System.Text.RegularExpressions;
// ...
for(int i=0; i<argv.Length; i++) {
if (Regex.IsMatch(i, "(\s|\")+")) {
argv[i] = "\"" + argv[i] + "\"";
argv[i].Replace("\"", "\\\"");
}
}

Categories