visual studio sintaxis pseudocodigo programacion programa para nivel general estructura consola comandos codigos codigo basicos c# command-line text-parsing

studio - estructura general de un programa en c# a nivel de consola



Separa la cadena que contiene los parámetros de la línea de comando en la cadena[] en C# (20)

Actualmente, este es el código que tengo:

private String[] SplitCommandLineArgument(String argumentString) { StringBuilder translatedArguments = new StringBuilder(argumentString); bool escaped = false; for (int i = 0; i < translatedArguments.Length; i++) { if (translatedArguments[i] == ''"'') { escaped = !escaped; } if (translatedArguments[i] == '' '' && !escaped) { translatedArguments[i] = ''/n''; } } string[] toReturn = translatedArguments.ToString().Split(new char[] { ''/n'' }, StringSplitOptions.RemoveEmptyEntries); for(int i = 0; i < toReturn.Length; i++) { toReturn[i] = RemoveMatchingQuotes(toReturn[i]); } return toReturn; } public static string RemoveMatchingQuotes(string stringToTrim) { int firstQuoteIndex = stringToTrim.IndexOf(''"''); int lastQuoteIndex = stringToTrim.LastIndexOf(''"''); while (firstQuoteIndex != lastQuoteIndex) { stringToTrim = stringToTrim.Remove(firstQuoteIndex, 1); stringToTrim = stringToTrim.Remove(lastQuoteIndex - 1, 1); //-1 because we''ve shifted the indicies left by one firstQuoteIndex = stringToTrim.IndexOf(''"''); lastQuoteIndex = stringToTrim.LastIndexOf(''"''); } return stringToTrim; }

No funciona con las comillas escapadas, pero funciona para los casos que me he encontrado hasta ahora.

Tengo una sola cadena que contiene los parámetros de la línea de comando para pasarla a otro ejecutable y necesito extraer la cadena [] que contiene los parámetros individuales de la misma manera que C # si los comandos se hubieran especificado en la línea de comandos. La cadena [] se usará cuando se ejecute otro punto de entrada de conjuntos por reflexión.

¿Hay una función estándar para esto? ¿O hay un método preferido (regex?) Para dividir los parámetros correctamente? Debe manejar '''' '''' cadenas delimitadas que pueden contener espacios correctamente, por lo que no puedo simplemente dividir ''''.

Ejemplo de cadena:

string parameterString = @"/src:""C:/tmp/Some Folder/Sub Folder"" /users:""[email protected]"" tasks:""SomeTask,Some Other Task"" -someParam foo";

Ejemplo de resultado:

string[] parameterArray = new string[] { @"/src:C:/tmp/Some Folder/Sub Folder", @"/users:[email protected]", @"tasks:SomeTask,Some Other Task", @"-someParam", @"foo" };

No necesito una biblioteca de análisis de línea de comandos, solo una forma de obtener el String [] que debería generarse.

Actualización : tuve que cambiar el resultado esperado para que coincida con lo que realmente se genera con C # (se eliminaron los "extra" en las cadenas divididas)


Además de la buena y pura solución gestionada por Earwicker , puede valer la pena mencionar, para mayor completitud, que Windows también proporciona la función CommandLineToArgvW para dividir una cadena en una matriz de cadenas:

LPWSTR *CommandLineToArgvW( LPCWSTR lpCmdLine, int *pNumArgs);

Analiza una cadena de línea de comandos Unicode y devuelve una matriz de punteros a los argumentos de línea de comando, junto con un recuento de tales argumentos, de una manera similar a los valores argv y argc en tiempo de ejecución C estándar.

Un ejemplo de llamar a esta API desde C # y desempaquetar la matriz resultante en código administrado se puede encontrar en " Convertir cadena de línea de comando a Args [] usando CommandLineToArgvW () API ." A continuación se muestra una versión ligeramente más simple del mismo código:

[DllImport("shell32.dll", SetLastError = true)] static extern IntPtr CommandLineToArgvW( [MarshalAs(UnmanagedType.LPWStr)] string lpCmdLine, out int pNumArgs); public static string[] CommandLineToArgs(string commandLine) { int argc; var argv = CommandLineToArgvW(commandLine, out argc); if (argv == IntPtr.Zero) throw new System.ComponentModel.Win32Exception(); try { var args = new string[argc]; for (var i = 0; i < args.Length; i++) { var p = Marshal.ReadIntPtr(argv, i * IntPtr.Size); args[i] = Marshal.PtrToStringUni(p); } return args; } finally { Marshal.FreeHGlobal(argv); } }


Aquí hay un trazador de líneas que hace su trabajo (vea la línea que hace todo el trabajo dentro del método BurstCmdLineArgs (...)). No es lo que yo llamaría la línea de código más legible, pero puedes dividirla por motivos de legibilidad. Es simple a propósito y no funciona bien para todos los casos de argumento (como los argumentos de nombre de archivo que contienen el delimitador de caracteres de cadena dividida en ellos). Esta solución ha funcionado bien en mis soluciones que la usan. Como dije, hace el trabajo sin un código de código para manejar cada posible formato de argumento n-factorial.

using System; using System.Collections.Generic; using System.Linq; namespace CmdArgProcessor { class Program { static void Main(string[] args) { // test switches and switches with values // -test1 1 -test2 2 -test3 -test4 -test5 5 string dummyString = string.Empty; var argDict = BurstCmdLineArgs(args); Console.WriteLine("Value for switch = -test1: {0}", argDict["test1"]); Console.WriteLine("Value for switch = -test2: {0}", argDict["test2"]); Console.WriteLine("Switch -test3 is present? {0}", argDict.TryGetValue("test3", out dummyString)); Console.WriteLine("Switch -test4 is present? {0}", argDict.TryGetValue("test4", out dummyString)); Console.WriteLine("Value for switch = -test5: {0}", argDict["test5"]); // Console output: // // Value for switch = -test1: 1 // Value for switch = -test2: 2 // Switch -test3 is present? True // Switch -test4 is present? True // Value for switch = -test5: 5 } public static Dictionary<string, string> BurstCmdLineArgs(string[] args) { var argDict = new Dictionary<string, string>(); // Flatten the args in to a single string separated by a space. // Then split the args on the dash delimiter of a cmd line "switch". // E.g. -mySwitch myValue // or -JustMySwitch (no value) // where: all values must follow a switch. // Then loop through each string returned by the split operation. // If the string can be split again by a space character, // then the second string is a value to be paired with a switch, // otherwise, only the switch is added as a key with an empty string as the value. // Use dictionary indexer to retrieve values for cmd line switches. // Use Dictionary::ContainsKey(...) where only a switch is recorded as the key. string.Join(" ", args).Split(''-'').ToList().ForEach(s => argDict.Add(s.Split()[0], (s.Split().Count() > 1 ? s.Split()[1] : ""))); return argDict; } } }


El analizador de línea de comandos de Windows se comporta como usted dice, dividir en espacio a menos que haya una comilla sin cerrar antes. Yo recomendaría escribir el analizador usted mismo. Algo como esto tal vez:

static string[] ParseArguments(string commandLine) { char[] parmChars = commandLine.ToCharArray(); bool inQuote = false; for (int index = 0; index < parmChars.Length; index++) { if (parmChars[index] == ''"'') inQuote = !inQuote; if (!inQuote && parmChars[index] == '' '') parmChars[index] = ''/n''; } return (new string(parmChars)).Split(''/n''); }


En su pregunta, pidió una expresión regular, y soy un gran admirador y usuario de ellos, así que cuando tuve que hacer este mismo argumento dividido como usted, escribí mi propia expresión regular después de buscar en Google y no encontrar una solución simple. Me gustan las soluciones cortas, así que hice una y aquí está:

var re = @"/G(""((""""|[^""])+)""|(/S+)) *"; var ms = Regex.Matches(CmdLine, re); var list = ms.Cast<Match>() .Select(m => Regex.Replace( m.Groups[2].Success ? m.Groups[2].Value : m.Groups[4].Value, @"""""", @"""")).ToArray();

Maneja espacios en blanco y comillas entre comillas, y convierte "" en "adjunto". ¡Siéntase libre de usar el código!


Esta es una respuesta al código de Anton, que no funciona con las comillas escapadas. Modifiqué 3 lugares.

  1. El constructor de StringBuilder en SplitCommandLineArguments , reemplazando cualquier / " con / r
  2. En el bucle for en SplitCommandLineArguments , ahora reemplazo el carácter / r a / " .
  3. Se cambió el método SplitCommandLineArgument de estático privado a público .

public static string[] SplitCommandLineArgument( String argumentString ) { StringBuilder translatedArguments = new StringBuilder( argumentString ).Replace( "///"", "/r" ); bool InsideQuote = false; for ( int i = 0; i < translatedArguments.Length; i++ ) { if ( translatedArguments[i] == ''"'' ) { InsideQuote = !InsideQuote; } if ( translatedArguments[i] == '' '' && !InsideQuote ) { translatedArguments[i] = ''/n''; } } string[] toReturn = translatedArguments.ToString().Split( new char[] { ''/n'' }, StringSplitOptions.RemoveEmptyEntries ); for ( int i = 0; i < toReturn.Length; i++ ) { toReturn[i] = RemoveMatchingQuotes( toReturn[i] ); toReturn[i] = toReturn[i].Replace( "/r", "/"" ); } return toReturn; } public static string RemoveMatchingQuotes( string stringToTrim ) { int firstQuoteIndex = stringToTrim.IndexOf( ''"'' ); int lastQuoteIndex = stringToTrim.LastIndexOf( ''"'' ); while ( firstQuoteIndex != lastQuoteIndex ) { stringToTrim = stringToTrim.Remove( firstQuoteIndex, 1 ); stringToTrim = stringToTrim.Remove( lastQuoteIndex - 1, 1 ); //-1 because we''ve shifted the indicies left by one firstQuoteIndex = stringToTrim.IndexOf( ''"'' ); lastQuoteIndex = stringToTrim.LastIndexOf( ''"'' ); } return stringToTrim; }


Este artículo de proyecto de código es lo que he usado en el pasado, es un buen código, pero podría funcionar.

Este artículo de msdn es lo único que pude encontrar que explica cómo C # analiza los argumentos de línea de comando.

¡Espero que ayude!



La buena y pura solución administrada por Earwicker no pudo manejar argumentos como este:

Test("/"He whispered to her ///"I love you///"./"", "He whispered to her /"I love you/".");

Devolvió 3 elementos:

"He whispered to her /"I love you/"."

Así que aquí hay una solución para apoyar el "citado /" escape / "cita":

public static IEnumerable<string> SplitCommandLine(string commandLine) { bool inQuotes = false; bool isEscaping = false; return commandLine.Split(c => { if (c == ''//' && !isEscaping) { isEscaping = true; return false; } if (c == ''/"'' && !isEscaping) inQuotes = !inQuotes; isEscaping = false; return !inQuotes && Char.IsWhiteSpace(c)/*c == '' ''*/; }) .Select(arg => arg.Trim().TrimMatchingQuotes(''/"'').Replace("///"", "/"")) .Where(arg => !string.IsNullOrEmpty(arg)); }

Probado con 2 casos adicionales:

Test("/"C://Program Files/"", "C://Program Files"); Test("/"He whispered to her ///"I love you///"./"", "He whispered to her /"I love you/".");

También se observó que la respuesta aceptada por Atif Aziz que usa CommandLineToArgvW también falló. Devolvió 4 elementos:

He whispered to her / I love you".

Espero que esto ayude a alguien a buscar esa solución en el futuro.


Me gustan los iteradores, y hoy en día Linq hace que IEnumerable sea tan fácil de usar como arreglos de cadenas, así que mi respuesta al espíritu de la respuesta de Jeffrey L Whitledge es (como método de extensión para la cadena):

public static IEnumerable<string> ParseArguments(this string commandLine) { if (string.IsNullOrWhiteSpace(commandLine)) yield break; var sb = new StringBuilder(); bool inQuote = false; foreach (char c in commandLine) { if (c == ''"'' && !inQuote) { inQuote = true; continue; } if (c != ''"'' && !(char.IsWhiteSpace(c) && !inQuote)) { sb.Append(c); continue; } if (sb.Length > 0) { var result = sb.ToString(); sb.Clear(); inQuote = false; yield return result; } } if (sb.Length > 0) yield return sb.ToString(); }


Me molesta que no haya una función para dividir una cadena basada en una función que examina cada personaje. Si lo hubiera, podrías escribirlo así:

public static IEnumerable<string> SplitCommandLine(string commandLine) { bool inQuotes = false; return commandLine.Split(c => { if (c == ''/"'') inQuotes = !inQuotes; return !inQuotes && c == '' ''; }) .Select(arg => arg.Trim().TrimMatchingQuotes(''/"'')) .Where(arg => !string.IsNullOrEmpty(arg)); }

A pesar de haber escrito eso, ¿por qué no escribir los métodos de extensión necesarios? De acuerdo, me convenciste ...

En primer lugar, mi propia versión de Split toma una función que tiene que decidir si el carácter especificado debe dividir la cadena:

public static IEnumerable<string> Split(this string str, Func<char, bool> controller) { int nextPiece = 0; for (int c = 0; c < str.Length; c++) { if (controller(str[c])) { yield return str.Substring(nextPiece, c - nextPiece); nextPiece = c + 1; } } yield return str.Substring(nextPiece); }

Puede producir algunas cadenas vacías dependiendo de la situación, pero tal vez esa información será útil en otros casos, por lo que no elimino las entradas vacías en esta función.

En segundo lugar (y más mundano) un pequeño ayudante que recortará un par de citas que coincidan desde el principio y el final de una cadena. Es más quisquilloso que el método Trim estándar: solo recortará un carácter de cada extremo y no se recortará desde un solo extremo:

public static string TrimMatchingQuotes(this string input, char quote) { if ((input.Length >= 2) && (input[0] == quote) && (input[input.Length - 1] == quote)) return input.Substring(1, input.Length - 2); return input; }

Y supongo que querrás algunas pruebas también. Bueno, esta bien entonces. ¡Pero esto debe ser absolutamente lo último! Primero, una función de ayuda que compara el resultado de la división con los contenidos de la matriz esperada:

public static void Test(string cmdLine, params string[] args) { string[] split = SplitCommandLine(cmdLine).ToArray(); Debug.Assert(split.Length == args.Length); for (int n = 0; n < split.Length; n++) Debug.Assert(split[n] == args[n]); }

Entonces puedo escribir pruebas como esta:

Test(""); Test("a", "a"); Test(" abc ", "abc"); Test("a b ", "a", "b"); Test("a b /"c d/"", "a", "b", "c d");

Aquí está la prueba para sus requisitos:

Test(@"/src:""C:/tmp/Some Folder/Sub Folder"" /users:""[email protected]"" tasks:""SomeTask,Some Other Task"" -someParam", @"/src:""C:/tmp/Some Folder/Sub Folder""", @"/users:""[email protected]""", @"tasks:""SomeTask,Some Other Task""", @"-someParam");

Tenga en cuenta que la implementación tiene la característica adicional de que eliminará las comillas de un argumento si tiene sentido (gracias a la función TrimMatchingQuotes). Creo que eso es parte de la interpretación normal de la línea de comandos.


No creo que haya comillas simples o ^ comillas para las aplicaciones de C #. La siguiente función funciona bien para mí:

public static IEnumerable<String> SplitArguments(string commandLine) { Char quoteChar = ''"''; Char escapeChar = ''//'; Boolean insideQuote = false; Boolean insideEscape = false; StringBuilder currentArg = new StringBuilder(); // needed to keep "" as argument but drop whitespaces between arguments Int32 currentArgCharCount = 0; for (Int32 i = 0; i < commandLine.Length; i++) { Char c = commandLine[i]; if (c == quoteChar) { currentArgCharCount++; if (insideEscape) { currentArg.Append(c); // found /" -> add " to arg insideEscape = false; } else if (insideQuote) { insideQuote = false; // quote ended } else { insideQuote = true; // quote started } } else if (c == escapeChar) { currentArgCharCount++; if (insideEscape) // found // -> add // (only /" will be ") currentArg.Append(escapeChar + escapeChar); insideEscape = !insideEscape; } else if (Char.IsWhiteSpace(c)) { if (insideQuote) { currentArgCharCount++; currentArg.Append(c); // append whitespace inside quote } else { if (currentArgCharCount > 0) yield return currentArg.ToString(); currentArgCharCount = 0; currentArg.Clear(); } } else { currentArgCharCount++; if (insideEscape) { // found non-escaping backslash -> add / (only /" will be ") currentArg.Append(escapeChar); currentArgCharCount = 0; insideEscape = false; } currentArg.Append(c); } } if (currentArgCharCount > 0) yield return currentArg.ToString(); }


No estoy seguro de si te entendí, pero ¿el problema que el personaje usó como separador también se encuentra dentro del texto? (Excepto por eso se escapó con el doble "?)

De ser así, crearía un bucle for y reemplazaría todas las instancias en las que <"> esté presente con <|> (u otro carácter" seguro ", pero asegúrese de que solo reemplaza <"> y no <"">

Después de iterar la cadena, haría lo que se publicó anteriormente, dividir la cadena, pero ahora en el personaje <|>

EDITAR: por legible, agregué, es decir, "está escrito como <">, ya que no estaba claro a qué me refería cuando escribía "y", o |


Prueba este código:

string[] str_para_linha_comando(string str, out int argumentos) { string[] linhaComando = new string[32]; bool entre_aspas = false; int posicao_ponteiro = 0; int argc = 0; int inicio = 0; int fim = 0; string sub; for(int i = 0; i < str.Length;) { if (entre_aspas) { // está entre aspas sub = str.Substring(inicio+1, fim - (inicio+1)); linhaComando[argc - 1] = sub; posicao_ponteiro += ((fim - posicao_ponteiro)+1); entre_aspas = false; i = posicao_ponteiro; } else { tratar_aspas: if (str.ElementAt(i) == ''/"'') { inicio = i; fim = str.IndexOf(''/"'', inicio + 1); entre_aspas = true; argc++; } else { // se não for aspas, então ler até achar o primeiro espaço em branco if (str.ElementAt(i) == '' '') { if (str.ElementAt(i + 1) == ''/"'') { i++; goto tratar_aspas; } // pular os espaços em branco adiconais while(str.ElementAt(i) == '' '') i++; argc++; inicio = i; fim = str.IndexOf('' '', inicio); if (fim == -1) fim = str.Length; sub = str.Substring(inicio, fim - inicio); linhaComando[argc - 1] = sub; posicao_ponteiro += (fim - posicao_ponteiro); i = posicao_ponteiro; if (posicao_ponteiro == str.Length) break; } else { argc++; inicio = i; fim = str.IndexOf('' '', inicio); if (fim == -1) fim = str.Length; sub = str.Substring(inicio, fim - inicio); linhaComando[argc - 1] = sub; posicao_ponteiro += fim - posicao_ponteiro; i = posicao_ponteiro; if (posicao_ponteiro == str.Length) break; } } } } argumentos = argc; return linhaComando; }

Está escrito en portugués.


Sé que esto es viejo, pero alguien podría encontrar una solución puramente administrada útil. Hay demasiados comentarios "problemáticos" para la función WINAPI y no está disponible en otras plataformas. Aquí está mi código que tiene un comportamiento bien definido (que puede cambiar si lo desea). Debería hacer lo mismo que lo que hace .NET / Windows al proporcionar ese parámetro string[] args , lo he comparado con una serie de valores "interesantes".

Esta es una implementación clásica de máquina de estado que toma cada carácter individual de la cadena de entrada y lo interpreta para el estado actual, produciendo salida y un nuevo estado. El estado se define en las variables escape , inQuote , hadQuote y prevCh , el resultado se recopila en currentArg y args .

Algunas de las especialidades que descubrí mediante experimentos en un símbolo del sistema real (Windows 7): // produce / , /" produce " , "" produce dentro de un rango citado " .

El personaje también parece ser mágico: siempre desaparece cuando no se lo dobla. De lo contrario, no tiene efecto en una línea de comando real. Mi implementación no es compatible con esto, ya que no he encontrado un patrón en este comportamiento. Tal vez alguien sepa más al respecto.

Algo que no encaja en este patrón es el siguiente comando:

cmd /c "argdump.exe "a b c""

El comando cmd parece captar las citas externas y tomar el resto al pie de la letra. Debe haber alguna salsa mágica especial en esto.

No hice ningún punto de referencia en mi método, pero lo considero razonablemente rápido. No utiliza Regex y no realiza ninguna concatenación de cadenas, sino que utiliza un StringBuilder para recopilar los caracteres de un argumento y los coloca en una lista.

/// <summary> /// Reads command line arguments from a single string. /// </summary> /// <param name="argsString">The string that contains the entire command line.</param> /// <returns>An array of the parsed arguments.</returns> public string[] ReadArgs(string argsString) { // Collects the split argument strings List<string> args = new List<string>(); // Builds the current argument var currentArg = new StringBuilder(); // Indicates whether the last character was a backslash escape character bool escape = false; // Indicates whether we''re in a quoted range bool inQuote = false; // Indicates whether there were quotes in the current arguments bool hadQuote = false; // Remembers the previous character char prevCh = ''/0''; // Iterate all characters from the input string for (int i = 0; i < argsString.Length; i++) { char ch = argsString[i]; if (ch == ''//' && !escape) { // Beginning of a backslash-escape sequence escape = true; } else if (ch == ''//' && escape) { // Double backslash, keep one currentArg.Append(ch); escape = false; } else if (ch == ''"'' && !escape) { // Toggle quoted range inQuote = !inQuote; hadQuote = true; if (inQuote && prevCh == ''"'') { // Doubled quote within a quoted range is like escaping currentArg.Append(ch); } } else if (ch == ''"'' && escape) { // Backslash-escaped quote, keep it currentArg.Append(ch); escape = false; } else if (char.IsWhiteSpace(ch) && !inQuote) { if (escape) { // Add pending escape char currentArg.Append(''//'); escape = false; } // Accept empty arguments only if they are quoted if (currentArg.Length > 0 || hadQuote) { args.Add(currentArg.ToString()); } // Reset for next argument currentArg.Clear(); hadQuote = false; } else { if (escape) { // Add pending escape char currentArg.Append(''//'); escape = false; } // Copy character from input, no special meaning currentArg.Append(ch); } prevCh = ch; } // Save last argument if (currentArg.Length > 0 || hadQuote) { args.Add(currentArg.ToString()); } return args.ToArray(); }


Tomé la respuesta de Jeffrey L Whitledge y la mejoré un poco. Todavía no tengo suficientes créditos para comentar su respuesta.

Ahora es compatible con comillas simples y dobles. Puede usar comillas en los parámetros usando otras citas escritas.

También quita las comillas de los argumentos, ya que estos no contribuyen a la información del argumento.

public static string[] SplitArguments(string commandLine) { var parmChars = commandLine.ToCharArray(); var inSingleQuote = false; var inDoubleQuote = false; for (var index = 0; index < parmChars.Length; index++) { if (parmChars[index] == ''"'' && !inSingleQuote) { inDoubleQuote = !inDoubleQuote; parmChars[index] = ''/n''; } if (parmChars[index] == ''/''' && !inDoubleQuote) { inSingleQuote = !inSingleQuote; parmChars[index] = ''/n''; } if (!inSingleQuote && !inDoubleQuote && parmChars[index] == '' '') parmChars[index] = ''/n''; } return (new string(parmChars)).Split(new[] { ''/n'' }, StringSplitOptions.RemoveEmptyEntries); }




Sí, el objeto de cadena tiene una función incorporada llamada Split () que toma un único parámetro que especifica el carácter a buscar como un delimitador, y devuelve una matriz de cadenas (cadena []) con los valores individuales en ella


public static string[] SplitArguments(string args) { char[] parmChars = args.ToCharArray(); bool inSingleQuote = false; bool inDoubleQuote = false; bool escaped = false; bool lastSplitted = false; bool justSplitted = false; bool lastQuoted = false; bool justQuoted = false; int i, j; for(i=0, j=0; i<parmChars.Length; i++, j++) { parmChars[j] = parmChars[i]; if(!escaped) { if(parmChars[i] == ''^'') { escaped = true; j--; } else if(parmChars[i] == ''"'' && !inSingleQuote) { inDoubleQuote = !inDoubleQuote; parmChars[j] = ''/n''; justSplitted = true; justQuoted = true; } else if(parmChars[i] == ''/''' && !inDoubleQuote) { inSingleQuote = !inSingleQuote; parmChars[j] = ''/n''; justSplitted = true; justQuoted = true; } else if(!inSingleQuote && !inDoubleQuote && parmChars[i] == '' '') { parmChars[j] = ''/n''; justSplitted = true; } if(justSplitted && lastSplitted && (!lastQuoted || !justQuoted)) j--; lastSplitted = justSplitted; justSplitted = false; lastQuoted = justQuoted; justQuoted = false; } else { escaped = false; } } if(lastQuoted) j--; return (new string(parmChars, 0, j)).Split(new[] { ''/n'' }); }

basado en la respuesta de Vapor in the Alley , este también admite ^ escapes

Ejemplos:

  • esto es una prueba
    • esta
    • es
    • un
    • prueba
  • esto es una prueba
    • esta
    • es un
    • prueba
  • esta ^ "es una ^" prueba
    • esta
    • "es
    • un"
    • prueba
  • este "" "es una prueba ^^"
    • esta
    • es una ^ prueba

también admite múltiples espacios (rompe args solo una vez por bloque de espacios)