2012-02-15 11 views
18

¿El comportamiento siguiente es una característica o un error en C# .NET?Barra diagonal inversa y comillas en los argumentos de la línea de comandos

aplicación de la prueba:

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

Ejecutar con argumentos de línea de comando:

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

En consecuencia recibir:

Arguments: 
a 
b 
\\x\ 
\x" 

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

No faltan barras invertidas y eliminado no cita en args pasó al método Main(). ¿Alguien sabe acerca de la solución correcta excepto el análisis manual de CommandLine?

+2

En general, es * mucho * más probable es que se trata de un error en el código de una error en el lenguaje. –

+2

Encuentra un error en el programa, cuyas fuentes se muestran en la publicación original. Realmente lo apreciaré. –

+1

Parece ser una característica: http://msdn.microsoft.com/en-us/library/system.environment.getcommandlineargs.aspx Si una comilla doble sigue a dos o un número par de barras diagonales inversas, cada una de las barras diagonales inversas procede el par se reemplaza por una barra invertida y se quita la comilla doble. Si una comilla doble sigue a un número impar de barras invertidas, incluyendo solo una, cada par precedente se reemplaza con una barra invertida y la barra invertida restante se elimina; sin embargo, en este caso las comillas dobles no se eliminan. –

Respuesta

18

De acuerdo con este article by Jon Galloway, puede haber un comportamiento extraño cuando se usan barras diagonales inversas en los argumentos de la línea de comandos.
Lo más notable es que menciona que "mayoría de las aplicaciones (incluyendo aplicaciones .Net) utilizan CommandLineToArgvW para decodificar sus líneas de comando. Utiliza reglas de escape locos que explican el comportamiento que se está viendo."

Se explica que la primera el conjunto de barras diagonales inversas no requiere escaparse, pero las barras invertidas que vienen después de los caracteres alfa (¿también numérico?) requieren escapes y las citas siempre deben ser escapadas.

Basado fuera de estas reglas, creo que para obtener los argumentos que desea que tendría que pasar a ellos como:

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

"chiflado" de hecho.

+1

Para lectores futuros: La [documentación de CommandLineToArgvW] (https://msdn.microsoft.com/en-us/library/windows/desktop/bb776391.aspx) puede ser engañosa o estar en mal estado. Afortunadamente, CommandLineToArgvW y .Net se comportan exactamente como la [documentación de VS] (https://msdn.microsoft.com/en-us/library/windows/desktop/17w5ykft.aspx) describe. Una descripción aún mejor: [Todo el mundo cita los argumentos de línea de comando de la manera incorrecta] (https://blogs.msdn.microsoft.com/twistylittlepasagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way /) –

0

Después de mucha experimentación, esto funcionó para mí. Intento crear un comando para enviar a la línea de comando de Windows. El nombre de una carpeta viene después de la opción -graphical en el comando, y dado que puede tener espacios, debe estar entre comillas dobles. Cuando utilicé barras invertidas para crear las comillas salieron como literales en el comando. Así que esto. . . .

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 es una cadena que contiene sólo un carácter de comillas dobles. Va precedido de @ para que sea verbatim string literal.

La plantilla de comando también es una literal litera literal, y el método de cadena.Formato se utiliza para compilar todo en strCmdText.

+1

¿Qué es lo que crees que '@" "+ (char) 34' está haciendo? ¿Por qué no simplemente escribir '" \ "" '? Y, ¿por qué necesita usar' string.Format' para insertarlos en primer lugar? Puede escribir '" 'en una cadena literal como' @ "- graphical" " {0} "" ... "' –

1

he escapado el problema a otro lado ... En lugar de obtener argumentos ya parced Me estoy haciendo la cadena de argumentos como es y entonces yo estoy usando mi propio analizador:

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

este concepto deja escribe citas sin el prefijo de escape

0

Me encontré con este mismo problema el otro día y tuve dificultades para superarlo. En mi Google, me encontré con this article regarding VB.NET (el idioma de mi aplicación) que resolvió el problema sin tener que cambiar mi otro código en función de los argumentos.

En ese artículo, se refiere al original article que se escribió para C#.Aquí está el código real, se le pasa 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 
Cuestiones relacionadas