反斜杠并在命令行参数中引用

以下行为是C#.NET中的某些function还是错误?

测试应用:

using System; using System.Linq; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("Arguments:"); foreach (string arg in args) { Console.WriteLine(arg); } Console.WriteLine(); Console.WriteLine("Command Line:"); var clArgs = Environment.CommandLine.Split(' '); foreach (string arg in clArgs.Skip(clArgs.Length - args.Length)) { Console.WriteLine(arg); } Console.ReadKey(); } } } 

使用命令行参数运行它:

 a "b" "\\x\\" "\x\" 

在我收到的结果中:

 Arguments: a b \\x\ \x" Command Line: a "b" "\\x\\" "\x\" 

传递给方法Main()的args中缺少反斜杠和未删除的引号。 除手动解析Environment.CommandLine外,正确的解决方法是什么?

根据Jon Galloway的这篇文章,在命令行参数中使用反斜杠时可能会遇到奇怪的行为。

最值得注意的是,它提到“ 大多数应用程序(包括.NET应用程序)使用CommandLineToArgvW来解码它们的命令行。它使用疯狂的转义规则来解释你所看到的行为。

它解释了第一组反斜杠不需要转义,但是alpha(也许是数字?)字符后面的反斜杠需要转义,并且引号总是需要转义。

根据这些规则,我相信你得到你想要的参数,你必须将它们传递给:

 a "b" "\\x\\\\" "\x\\" 

确实“哇哇”。

我从另一个方面逃过了这个问题……

我没有获取已经解析的参数,而是获取参数字符串,然后我使用自己的解析器:

 static void Main(string[] args) { var param = ParseString(Environment.CommandLine); ... } // The following template implements the following notation: // -key1 = some value -key2 = "some value even with '-' character " ... private const string ParameterQuery = "\\-(?\\w+)\\s*=\\s*(\"(?[^\"]*)\"|(?[^\\-]*))\\s*"; private static Dictionary ParseString(string value) { var regex = new Regex(ParameterQuery); return regex.Matches(value).Cast().ToDictionary(m => m.Groups["key"].Value, m => m.Groups["value"].Value); } 

此概念允许您在没有转义前缀的情况下键入引号。

经过多次实验,这对我有用。 我正在尝试创建一个发送到Windows命令行的命令。 文件夹名称位于命令中的-graphical选项之后,因为它可能包含空格,所以必须用双引号括起来。 当我使用反斜杠创建引号时,它们在命令中以文字forms出现。 所以这。 。 。 。

 string q = @"" + (char) 34; string strCmdText = string.Format(@"/C cleartool update -graphical {1}{0}{1}", this.txtViewFolder.Text, q); System.Diagnostics.Process.Start("CMD.exe", strCmdText); 

q是一个只包含双引号字符的字符串。 它之前是@ ,使它成为逐字字符串文字 。

命令模板也是逐字字符串文字,string.Format方法用于将所有内容编译为strCmdText

前几天我遇到了同样的问题,并且很难度过难关。 在我的谷歌搜索中,我遇到了关于VB.NET (我的应用程序的语言)的文章解决了这个问题,而不必根据参数更改我的任何其他代码。

在那篇文章中,他提到了为C#编写的原始文章 。 这是实际的代码,你传递给它Environment.CommandLine()

C#

 class CommandLineTools { ///  /// C-like argument parser ///  /// Command line string with arguments. Use Environment.CommandLine /// The args[] array (argv) public static string[] CreateArgs(string commandLine) { StringBuilder argsBuilder = new StringBuilder(commandLine); bool inQuote = false; // Convert the spaces to a newline sign so we can split at newline later on // Only convert spaces which are outside the boundries of quoted text for (int i = 0; i < argsBuilder.Length; i++) { if (argsBuilder[i].Equals('"')) { inQuote = !inQuote; } if (argsBuilder[i].Equals(' ') && !inQuote) { argsBuilder[i] = '\n'; } } // Split to args array string[] args = argsBuilder.ToString().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries); // Clean the '"' signs from the args as needed. for (int i = 0; i < args.Length; i++) { args[i] = ClearQuotes(args[i]); } return args; } ///  /// Cleans quotes from the arguments.
/// All signle quotes (") will be removed.
/// Every pair of quotes ("") will transform to a single quote.
///
/// A string with quotes. /// The same string if its without quotes, or a clean string if its with quotes. private static string ClearQuotes(string stringWithQuotes) { int quoteIndex; if ((quoteIndex = stringWithQuotes.IndexOf('"')) == -1) { // String is without quotes.. return stringWithQuotes; } // Linear sb scan is faster than string assignemnt if quote count is 2 or more (=always) StringBuilder sb = new StringBuilder(stringWithQuotes); for (int i = quoteIndex; i < sb.Length; i++) { if (sb[i].Equals('"')) { // If we are not at the last index and the next one is '"', we need to jump one to preserve one if (i != sb.Length - 1 && sb[i + 1].Equals('"')) { i++; } // We remove and then set index one backwards. // This is because the remove itself is going to shift everything left by 1. sb.Remove(i--, 1); } } return sb.ToString(); } }

VB.NET:

 Imports System.Text ' Original version by Jonathan Levison (C#)' ' http://sleepingbits.com/2010/01/command-line-arguments-with-double-quotes-in-net/ ' converted using http://www.developerfusion.com/tools/convert/csharp-to-vb/ ' and then some manual effort to fix language discrepancies Friend Class CommandLineHelper '''  ''' C-like argument parser '''  ''' Command line string with arguments. Use Environment.CommandLine ''' The args[] array (argv) Public Shared Function CreateArgs(commandLine As String) As String() Dim argsBuilder As New StringBuilder(commandLine) Dim inQuote As Boolean = False ' Convert the spaces to a newline sign so we can split at newline later on ' Only convert spaces which are outside the boundries of quoted text For i As Integer = 0 To argsBuilder.Length - 1 If argsBuilder(i).Equals(""""c) Then inQuote = Not inQuote End If If argsBuilder(i).Equals(" "c) AndAlso Not inQuote Then argsBuilder(i) = ControlChars.Lf End If Next ' Split to args array Dim args As String() = argsBuilder.ToString().Split(New Char() {ControlChars.Lf}, StringSplitOptions.RemoveEmptyEntries) ' Clean the '"' signs from the args as needed. For i As Integer = 0 To args.Length - 1 args(i) = ClearQuotes(args(i)) Next Return args End Function '''  ''' Cleans quotes from the arguments.
''' All signle quotes (") will be removed.
''' Every pair of quotes ("") will transform to a single quote.
'''
''' A string with quotes. ''' The same string if its without quotes, or a clean string if its with quotes. Private Shared Function ClearQuotes(stringWithQuotes As String) As String Dim quoteIndex As Integer = stringWithQuotes.IndexOf(""""c) If quoteIndex = -1 Then Return stringWithQuotes ' Linear sb scan is faster than string assignemnt if quote count is 2 or more (=always) Dim sb As New StringBuilder(stringWithQuotes) Dim i As Integer = quoteIndex Do While i < sb.Length If sb(i).Equals(""""c) Then ' If we are not at the last index and the next one is '"', we need to jump one to preserve one If i <> sb.Length - 1 AndAlso sb(i + 1).Equals(""""c) Then i += 1 End If ' We remove and then set index one backwards. ' This is because the remove itself is going to shift everything left by 1. sb.Remove(System.Math.Max(System.Threading.Interlocked.Decrement(i), i + 1), 1) End If i += 1 Loop Return sb.ToString() End Function End Class

这适用于我,它可以正常使用问题中的示例。

  ///  /// https://www.pinvoke.net/default.aspx/shell32/CommandLineToArgvW.html ///  ///  ///  static string[] SplitArgs(string unsplitArgumentLine) { int numberOfArgs; IntPtr ptrToSplitArgs; string[] splitArgs; ptrToSplitArgs = CommandLineToArgvW(unsplitArgumentLine, out numberOfArgs); // CommandLineToArgvW returns NULL upon failure. if (ptrToSplitArgs == IntPtr.Zero) throw new ArgumentException("Unable to split argument.", new Win32Exception()); // Make sure the memory ptrToSplitArgs to is freed, even upon failure. try { splitArgs = new string[numberOfArgs]; // ptrToSplitArgs is an array of pointers to null terminated Unicode strings. // Copy each of these strings into our split argument array. for (int i = 0; i < numberOfArgs; i++) splitArgs[i] = Marshal.PtrToStringUni( Marshal.ReadIntPtr(ptrToSplitArgs, i * IntPtr.Size)); return splitArgs; } finally { // Free memory obtained by CommandLineToArgW. LocalFree(ptrToSplitArgs); } } [DllImport("shell32.dll", SetLastError = true)] static extern IntPtr CommandLineToArgvW( [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs); [DllImport("kernel32.dll")] static extern IntPtr LocalFree(IntPtr hMem); static string Reverse(string s) { char[] charArray = s.ToCharArray(); Array.Reverse(charArray); return new string(charArray); } static string GetEscapedCommandLine() { StringBuilder sb = new StringBuilder(); bool gotQuote = false; foreach (var c in Environment.CommandLine.Reverse()) { if (c == '"') gotQuote = true; else if (gotQuote && c == '\\') { // double it sb.Append('\\'); } else gotQuote = false; sb.Append(c); } return Reverse(sb.ToString()); } static void Main(string[] args) { // Crazy hack args = SplitArgs(GetEscapedCommandLine()).Skip(1).ToArray(); }