在c#中退出命令行参数

简洁版本:

用引号包裹参数并转义\"

代码版本

我想通过使用ProcessInfo.Arguments将命令行参数string[] args传递给另一个进程。

 ProcessStartInfo info = new ProcessStartInfo(); info.FileName = Application.ExecutablePath; info.UseShellExecute = true; info.Verb = "runas"; // Provides Run as Administrator info.Arguments = EscapeCommandLineArguments(args); Process.Start(info); 

问题是,我得到的参数作为一个数组,并且必须合并成一个单一的string。 可以制造出一些论据来欺骗我的程序。

 my.exe "C:\Documents and Settings\MyPath \" --kill-all-humans \" except fry" 

根据这个答案,我创造了以下function来逃避一个单一的论点,但我可能错过了一些东西。

 private static string EscapeCommandLineArguments(string[] args) { string arguments = ""; foreach (string arg in args) { arguments += " \"" + arg.Replace ("\\", "\\\\").Replace("\"", "\\\"") + "\""; } return arguments; } 

这是否足够好或者是否有框架function?

比这更复杂!

我有相关的问题(编写前端.exe将调用所有传递的参数+一些额外的后端),所以我看人是如何做到这一点,遇到了你的问题。 最初,所有的人看起来都不错,就像你提出的arg.Replace (@"\", @"\\").Replace(quote, @"\"+quote)

然而,当我调用参数c:\temp a\\b ,这将得到传递为c:\tempa\\b ,这导致后端调用与"c:\\temp" "a\\\\b" – 这是不正确的,因为那里会有两个参数c:\\tempa\\\\b – 不是我们想要的! 我们一直在逃跑(窗户不是unix!)。

所以我详细阅读http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx ,它实际上描述了那些情况下如何处理:反斜杠被视为在双引用。

在这里如何处理多个问题有一个转折,解释可能会让人头晕目眩。 我会尝试在这里重新说出这个unescape规则:假设我们有一个N \的子string,然后是" 。当我们消除时,我们用int(N / 2) \replace这个子string,如果N是奇数,最后。

这种解码的编码方式如下:对于一个参数,find每一个0或更多的子string,然后是" ,然后用\"replace它,然后是\" 。 我们可以这样做:

 s = Regex.Replace(arg, @"(\\*)" + "\"", @"$1$1\" + "\""); 

就这样…

PS。 … 不是 。 等等,等等 – 还有更多! 🙂

我们做了正确的编码,但有一个扭曲,因为你是用双引号括住所有参数(如果有一些在其中的空间)。 有一个边界问题 – 如果一个参数结束于\ ,添加"将打破closures引用的含义。例子c:\one\ twoparsing为c:\one\ ,然后two将被重新组装为"c:\one\" "two" ,将我(误)理解为一个参数c:\one" two (我试过了,我没有补上)。 因此,我们还需要检查参数是否以\结尾,如果是这样,最后加上反斜杠的数量,如下所示:

 s = "\"" + Regex.Replace(s, @"(\\+)$", @"$1$1") + "\""; 

我的回答类似于Nas Banov的回答,但是我只想在必要时加双引号

删除额外的不必要的双引号

我的代码可以不必要的在整个过程中不断地加双引号 ,这对于接近参数的字符限制是很重要的。

 /// <summary> /// Encodes an argument for passing into a program /// </summary> /// <param name="original">The value that should be received by the program</param> /// <returns>The value which needs to be passed to the program for the original value /// to come through</returns> public static string EncodeParameterArgument(string original) { if( string.IsNullOrEmpty(original)) return original; string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0"); value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\""); return value; } // This is an EDIT // Note that this version does the same but handles new lines in the arugments public static string EncodeParameterArgumentMultiLine(string original) { if (string.IsNullOrEmpty(original)) return original; string value = Regex.Replace(original, @"(\\*)" + "\"", @"$1\$0"); value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\"", RegexOptions.Singleline); return value; } 

说明

为了正确地避免反斜杠双引号 ,你可以直接replace多个反斜杠的实例,然后用一个双引号replace:

 string value = Regex.Replace(original, @"(\\*)" + "\"", @"\$1$0"); 

额外两倍原始反斜杠 + 1和原来的双引号 。 即'\'+原始文章+原始文章+'“'我用$ 1 $ 0,因为$ 0有原始的反斜杠和原来的双引号,所以它使得replace更好的阅读。

 value = Regex.Replace(value, @"^(.*\s.*?)(\\*)$", "\"$1$2$2\""); 

这只能匹配包含空格的整行。

如果匹配,则在开头和结尾添加双引号

如果在争论结束时最初有反斜杠 ,那么他们将不会被引用,因为最后他们需要双引号 。 所以他们是重复的,引用他们所有,并防止无意中引用最后的双引号

它为第一部分做了最小的匹配,以便最后一个。*? 不会进入匹配最后的反斜杠

产量

所以这些input产生以下输出

你好

你好

\你好\ 12 \ 3 \

\你好\ 12 \ 3 \

你好,世界

“你好,世界”

\“你好\”

\\“你好\\\”

\“你好,世界

“\\“你好,世界”

\“你好,世界\

“\\“你好,世界\\”

你好,世界\\

“你好,世界\\\\”

我也遇到了这个问题。 我没有parsing参数,而是采取了完整的原始命令行并修剪可执行文件。 即使不需要/使用,也可以在通话中保留空格。 它仍然需要在可执行文件中执行转义,但这似乎比参数更容易。

 var commandLine = Environment.CommandLine; var argumentsString = ""; if(args.Length > 0) { // Re-escaping args to be the exact same as they were passed is hard and misses whitespace. // Use the original command line and trim off the executable to get the args. var argIndex = -1; if(commandLine[0] == '"') { //Double-quotes mean we need to dig to find the closing double-quote. var backslashPending = false; var secondDoublequoteIndex = -1; for(var i = 1; i < commandLine.Length; i++) { if(backslashPending) { backslashPending = false; continue; } if(commandLine[i] == '\\') { backslashPending = true; continue; } if(commandLine[i] == '"') { secondDoublequoteIndex = i + 1; break; } } argIndex = secondDoublequoteIndex; } else { // No double-quotes, so args begin after first whitespace. argIndex = commandLine.IndexOf(" ", System.StringComparison.Ordinal); } if(argIndex != -1) { argumentsString = commandLine.Substring(argIndex + 1); } } Console.WriteLine("argumentsString: " + argumentsString); 

我在GitHub上发布了一个处理大多数命令行编码/转义问题的小项目:

https://github.com/ericpopivker/Command-Line-Encoder

有一个CommandLineEncoder.Utils.cs类,以及validation编码/解码function的unit testing。

我给你写了一个小示例,告诉你如何在命令行中使用转义字符。

 public static string BuildCommandLineArgs(List<string> argsList) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); foreach (string arg in argsList) { sb.Append("\"\"" + arg.Replace("\"", @"\" + "\"") + "\"\" "); } if (sb.Length > 0) { sb = sb.Remove(sb.Length - 1, 1); } return sb.ToString(); } 

这里是一个testing方法:

  List<string> myArgs = new List<string>(); myArgs.Add("test\"123"); // test"123 myArgs.Add("test\"\"123\"\"234"); // test""123""234 myArgs.Add("test123\"\"\"234"); // test123"""234 string cmargs = BuildCommandLineArgs(myArgs); // result: ""test\"123"" ""test\"\"123\"\"234"" ""test123\"\"\"234"" // when you pass this result to your app, you will get this args list: // test"123 // test""123""234 // test123"""234 

关键是要用双引号(“”arg“”)来包装每个arg,并用转义引号(test \“123)replacearg值中的所有引号。

我从Everyone引用命令行参数中移植了一个C ++函数。

它工作正常,但你应该注意, cmd.exe解释命令行不同。 如果( 并且只有像,如文章的原始作者所述)你的命令行将被cmd.exe解释,你也应该转义shell元字符。

 /// <summary> /// This routine appends the given argument to a command line such that /// CommandLineToArgvW will return the argument string unchanged. Arguments /// in a command line should be separated by spaces; this function does /// not add these spaces. /// </summary> /// <param name="argument">Supplies the argument to encode.</param> /// <param name="force"> /// Supplies an indication of whether we should quote the argument even if it /// does not contain any characters that would ordinarily require quoting. /// </param> private static string EncodeParameterArgument(string argument, bool force = false) { if (argument == null) throw new ArgumentNullException(nameof(argument)); // Unless we're told otherwise, don't quote unless we actually // need to do so --- hopefully avoid problems if programs won't // parse quotes properly if (force == false && argument.Length > 0 && argument.IndexOfAny(" \t\n\v\"".ToCharArray()) == -1) { return argument; } var quoted = new StringBuilder(); quoted.Append('"'); var numberBackslashes = 0; foreach (var chr in argument) { switch (chr) { case '\\': numberBackslashes++; continue; case '"': // Escape all backslashes and the following // double quotation mark. quoted.Append('\\', numberBackslashes*2 + 1); quoted.Append(chr); break; default: // Backslashes aren't special here. quoted.Append('\\', numberBackslashes); quoted.Append(chr); break; } numberBackslashes = 0; } // Escape all backslashes, but let the terminating // double quotation mark we add below be interpreted // as a metacharacter. quoted.Append('\\', numberBackslashes*2); quoted.Append('"'); return quoted.ToString(); } 

做一个很好的添加参数,但不逃避。 在转义序列应该去的方法中增加了注释。

 public static string ApplicationArguments() { List<string> args = Environment.GetCommandLineArgs().ToList(); args.RemoveAt(0); // remove executable StringBuilder sb = new StringBuilder(); foreach (string s in args) { // todo: add escape double quotes here sb.Append(string.Format("\"{0}\" ", s)); // wrap all args in quotes } return sb.ToString().Trim(); } 
 static string BuildCommandLineFromArgs(params string[] args) { if (args == null) return null; string result = ""; if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) { foreach (string arg in args) { result += (result.Length > 0 ? " " : "") + arg .Replace(@" ", @"\ ") .Replace("\t", "\\\t") .Replace(@"\", @"\\") .Replace(@"""", @"\""") .Replace(@"<", @"\<") .Replace(@">", @"\>") .Replace(@"|", @"\|") .Replace(@"@", @"\@") .Replace(@"&", @"\&"); } } else //Windows family { bool enclosedInApo, wasApo; string subResult; foreach (string arg in args) { enclosedInApo = arg.LastIndexOfAny( new char[] { ' ', '\t', '|', '@', '^', '<', '>', '&'}) >= 0; wasApo = enclosedInApo; subResult = ""; for (int i = arg.Length - 1; i >= 0; i--) { switch (arg[i]) { case '"': subResult = @"\""" + subResult; wasApo = true; break; case '\\': subResult = (wasApo ? @"\\" : @"\") + subResult; break; default: subResult = arg[i] + subResult; wasApo = false; break; } } result += (result.Length > 0 ? " " : "") + (enclosedInApo ? "\"" + subResult + "\"" : subResult); } } return result; } 

AFAIK没有这个框架的function。

对于你的简单用例,你所做的事情看起来是足够的,除非你提交参数的程序将任何字符识别为特殊的,在这种情况下,你也需要逃避这些。