2012-02-15 38 views
18

Hành vi sau là một số tính năng hoặc lỗi trong C# .NET?Dấu gạch chéo ngược và trích dẫn trong đối số dòng lệnh

ứng dụng thử nghiệm:

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(); 
     } 
    } 
} 

Run nó với các đối số dòng lệnh:

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

Trong kết quả nhận được:

Arguments: 
a 
b 
\\x\ 
\x" 

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

Có bị thiếu dấu xồ nguợc và không loại bỏ-quote trong args được chuyển đến phương thức Main(). Có ai biết về cách giải quyết đúng không, trừ khi phân tích cú pháp CommandLine theo cách thủ công?

+2

Nói chung, đó là * nhiều * nhiều khả năng rằng đó là một lỗi trong mã của bạn hơn là một lỗi trong ngôn ngữ. –

+2

Vui lòng tìm lỗi trong chương trình, các nguồn được hiển thị trong bài đăng gốc. Tôi sẽ thực sự đánh giá cao điều đó. –

+1

Dường như là một tính năng: http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx Nếu dấu ngoặc kép sau hai hoặc một số chẵn các dấu gạch chéo ngược, mỗi dấu gạch chéo ngược tiến hành cặp được thay thế bằng một dấu gạch chéo ngược và dấu ngoặc kép được loại bỏ. Nếu dấu ngoặc kép sau một số lẻ dấu gạch chéo ngược, bao gồm chỉ một, mỗi cặp trước đó được thay thế bằng một dấu gạch chéo ngược và dấu gạch chéo ngược còn lại sẽ bị xóa; tuy nhiên, trong trường hợp này dấu ngoặc kép không bị xóa. –

Trả lời

18

Theo điều này article by Jon Galloway, có thể có hành vi lạ có kinh nghiệm khi sử dụng dấu gạch chéo ngược trong đối số dòng lệnh.
Đáng chú ý nhất nó đề cập rằng "Hầu hết các ứng dụng (bao gồm cả các ứng dụng Net) sử dụng CommandLineToArgvW để giải mã dòng lệnh của họ. Nó sử dụng quy tắc thoát điên mà giải thích hành vi mà bạn đang nhìn thấy."

Nó giải thích rằng người đầu tiên thiết lập các dấu gạch chéo ngược không yêu cầu thoát, nhưng dấu gạch chéo ngược đến sau khi alpha (có thể số quá?) ký tự yêu cầu thoát và dấu ngoặc kép luôn luôn cần phải được thoát.

Dựa tắt các quy tắc này, tôi tin rằng để có được những lý lẽ bạn muốn, bạn sẽ phải vượt qua chúng như:

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

"whacky" thực sự.

+1

Dành cho người đọc trong tương lai: Tài liệu [CommandLineToArgvW] (https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391.aspx) hoặc là gây hiểu nhầm hoặc IMHO sai. May mắn CommandLineToArgvW và .Net hoạt động chính xác như [tài liệu VS] (https://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft.aspx) mô tả. Mô tả thậm chí tốt hơn: [Mọi người đều trích dẫn dòng lệnh đối số sai] (https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way /) –

0

Sau nhiều thử nghiệm, điều này làm việc cho tôi. Tôi đang cố tạo một lệnh để gửi đến dòng lệnh của Windows. Tên thư mục xuất hiện sau tùy chọn -graphical trong lệnh và vì nó có thể có dấu cách trong đó nên phải được đặt trong dấu ngoặc kép. Khi tôi sử dụng dấu gạch chéo ngược để tạo dấu ngoặc kép, chúng xuất hiện dưới dạng chữ trong lệnh. Vì vậy, điều này. . . .

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 là chuỗi chỉ giữ ký tự kép. Nó đứng trước @ để biến nó thành verbatim string literal.

Mẫu lệnh cũng là một chuỗi ký tự đúng nguyên văn và phương thức string.Format được sử dụng để biên dịch mọi thứ thành strCmdText.

+1

Bạn nghĩ gì '@" "+ (char) 34' đang hoạt động? Tại sao không chỉ viết '" \ "" '? Và, tại sao bạn cần sử dụng' string.Format' để chèn chúng vào vị trí đầu tiên? Bạn có thể viết '" 'trong một chuỗi nguyên văn là' @ "- đồ họa" " {0} "" ... "' –

1

Tôi đã thoát khỏi vấn đề theo cách khác ... Thay vì nhận lập luận đã parced Tôi nhận được chuỗi lập luận vì nó và sau đó tôi đang sử dụng phân tích cú pháp của riêng tôi:

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 = "\\-(?<key>\\w+)\\s*=\\s*(\"(?<value>[^\"]*)\"|(?<value>[^\\-]*))\\s*"; 

private static Dictionary<string, string> ParseString(string value) 
{ 
    var regex = new Regex(ParameterQuery); 
    return regex.Matches(value).Cast<Match>().ToDictionary(m => m.Groups["key"].Value, m => m.Groups["value"].Value); 
} 

khái niệm này chúng ta hãy bạn nhập dấu ngoặc kép mà không cần tiền tố thoát

0

Tôi đã gặp vấn đề tương tự này vào ngày khác và gặp khó khăn trong việc vượt qua nó. Trong Googling của tôi, tôi đã xem qua this article regarding VB.NET (ngôn ngữ của ứng dụng của tôi) đã giải quyết được sự cố mà không phải thay đổi bất kỳ mã nào khác của tôi dựa trên các đối số.

Trong bài viết đó, ông đề cập đến original article được viết cho C#.Dưới đây là đoạn code thực tế, bạn vượt qua nó Environment.CommandLine():

C#

class CommandLineTools 
{ 
    /// <summary> 
    /// C-like argument parser 
    /// </summary> 
    /// <param name="commandLine">Command line string with arguments. Use Environment.CommandLine</param> 
    /// <returns>The args[] array (argv)</returns> 
    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; 
    } 

    /// <summary> 
    /// Cleans quotes from the arguments.<br/> 
    /// All signle quotes (") will be removed.<br/> 
    /// Every pair of quotes ("") will transform to a single quote.<br/> 
    /// </summary> 
    /// <param name="stringWithQuotes">A string with quotes.</param> 
    /// <returns>The same string if its without quotes, or a clean string if its with quotes.</returns> 
    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 
    ''' <summary> 
    ''' C-like argument parser 
    ''' </summary> 
    ''' <param name="commandLine">Command line string with arguments. Use Environment.CommandLine</param> 
    ''' <returns>The args[] array (argv)</returns> 
    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 

    ''' <summary> 
    ''' Cleans quotes from the arguments.<br/> 
    ''' All signle quotes (") will be removed.<br/> 
    ''' Every pair of quotes ("") will transform to a single quote.<br/> 
    ''' </summary> 
    ''' <param name="stringWithQuotes">A string with quotes.</param> 
    ''' <returns>The same string if its without quotes, or a clean string if its with quotes.</returns> 
    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 
Các vấn đề liên quan