Debido a que quería el mismo comportamiento que OP (dividir una cadena exactamente igual que lo haría Windows cmd), escribí un montón de casos de prueba y probé las respuestas aquí publicadas:
Test( 0, m, "One", new[] { "One" });
Test( 1, m, "One ", new[] { "One" });
Test( 2, m, " One", new[] { "One" });
Test( 3, m, " One ", new[] { "One" });
Test( 4, m, "One Two", new[] { "One", "Two" });
Test( 5, m, "One Two", new[] { "One", "Two" });
Test( 6, m, "One Two", new[] { "One", "Two" });
Test( 7, m, "\"One Two\"", new[] { "One Two" });
Test( 8, m, "One \"Two Three\"", new[] { "One", "Two Three" });
Test( 9, m, "One \"Two Three\" Four", new[] { "One", "Two Three", "Four" });
Test(10, m, "One=\"Two Three\" Four", new[] { "One=Two Three", "Four" });
Test(11, m, "One\"Two Three\" Four", new[] { "OneTwo Three", "Four" });
Test(12, m, "One\"Two Three Four", new[] { "OneTwo Three Four" });
Test(13, m, "\"One Two\"", new[] { "One Two" });
Test(14, m, "One\" \"Two", new[] { "One Two" });
Test(15, m, "\"One\" \"Two\"", new[] { "One", "Two" });
Test(16, m, "One\\\" Two", new[] { "One\"", "Two" });
Test(17, m, "\\\"One\\\" Two", new[] { "\"One\"", "Two" });
Test(18, m, "One\"", new[] { "One" });
Test(19, m, "\"One", new[] { "One" });
Test(20, m, "One \"\"", new[] { "One", "" });
Test(21, m, "One \"", new[] { "One", "" });
Test(22, m, "1 A=\"B C\"=D 2", new[] { "1", "A=B C=D", "2" });
Test(23, m, "1 A=\"B \\\" C\"=D 2", new[] { "1", "A=B \" C=D", "2" });
Test(24, m, "1 \\A 2", new[] { "1", "\\A", "2" });
Test(25, m, "1 \\\" 2", new[] { "1", "\"", "2" });
Test(26, m, "1 \\\\\" 2", new[] { "1", "\\\"", "2" });
Test(27, m, "\"", new[] { "" });
Test(28, m, "\\\"", new[] { "\"" });
Test(29, m, "'A B'", new[] { "'A", "B'" });
Test(30, m, "^", new[] { "^" });
Test(31, m, "^A", new[] { "A" });
Test(32, m, "^^", new[] { "^" });
Test(33, m, "\\^^", new[] { "\\^" });
Test(34, m, "^\\\\", new[] { "\\\\" });
Test(35, m, "^\"A B\"", new[] { "A B" });
Test(36, m, @"/src:""C:\tmp\Some Folder\Sub Folder"" /users:""abcdefg@hijkl.com"" tasks:""SomeTask,Some Other Task"" -someParam foo", new[] { @"/src:C:\tmp\Some Folder\Sub Folder", @"/users:abcdefg@hijkl.com", @"tasks:SomeTask,Some Other Task", @"-someParam", @"foo" });
Test(37, m, "", new string[] { });
Test(38, m, "a", new[] { "a" });
Test(39, m, " abc ", new[] { "abc" });
Test(40, m, "a b ", new[] { "a", "b" });
Test(41, m, "a b \"c d\"", new[] { "a", "b", "c d" });
Test(42, m, "this is a test ", new[] { "this", "is", "a", "test" });
Test(43, m, "this \"is a\" test", new[] { "this", "is a", "test" });
Test(44, m, "\"C:\\Program Files\"", new[] { "C:\\Program Files" });
Test(45, m, "\"He whispered to her \\\"I love you\\\".\"", new[] { "He whispered to her \"I love you\"." });
el valor "esperado" proviene de probarlo directamente con cmd.exe en mi máquina (Win10 x64) y un programa de impresión simple:
static void Main(string[] args) => Console.Out.WriteLine($"Count := {args.Length}\n{string.Join("\n", args.Select((v,i) => $"[{i}] => '{v}'"))}");
Estos son los resultados:
Solution | Failed Tests
------------------------------|-------------------------------------
Atif Aziz (749653) | 2, 3, 10, 11, 12, 14, 16, 17, 18, 26, 28, 31, 32, 33, 34, 35, 36, 37, 39, 45
Jeffrey L Whitledge (298968) | 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45
Daniel Earwicker (298990) | 10, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 45
Anton (299795) | 12, 16, 17, 18, 19, 21, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 45
CS. (467313) | 12, 18, 19, 21, 27, 31, 32, 33, 34, 35
Vapour in the Alley (2132004) | 10, 11, 12, 14, 16, 17, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 45
Monoman (7774211) | 14, 16, 17, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 45
Thomas Petersson (19091999) | 2, 3, 10, 11, 12, 14, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 39, 45
Fabio Iotti (19725880) | 1, 2, 3, 7, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 23, 25, 26, 28, 29, 30, 35, 36, 37, 39, 40, 42, 44, 45
ygoe (23961658) | 26, 31, 32, 33, 34, 35
Kevin Thach (24829691) | 10, 11, 12, 14, 18, 19, 20, 21, 22, 23, 26, 27, 31, 32, 33, 34, 35, 36
Lucas De Jesus (31621370) | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45
HarryP (48008872) | 24, 26, 31, 32, 33, 34, 35
TylerY86 (53290784) | 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 41, 43, 44, 45
Louis Somers (55903304) | 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 31, 32, 33, 34, 35, 36, 39, 41, 43, 44, 45
user2126375 (58233585) | 5, 6, 15, 16, 17, 31, 32, 33, 34, 35
DilipNannaware (59131568) | 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 31, 32, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43, 44, 45
Mikescher (this) | -
Debido a que ninguna respuesta parecía correcta (al menos según mi caso de uso) aquí está mi solución, actualmente pasa todos los casos de prueba (pero si alguien tiene casos de esquina adicionales (fallidos), comente):
public static IEnumerable<string> SplitArgs(string commandLine)
{
var result = new StringBuilder();
var quoted = false;
var escaped = false;
var started = false;
var allowcaret = false;
for (int i = 0; i < commandLine.Length; i++)
{
var chr = commandLine[i];
if (chr == '^' && !quoted)
{
if (allowcaret)
{
result.Append(chr);
started = true;
escaped = false;
allowcaret = false;
}
else if (i + 1 < commandLine.Length && commandLine[i + 1] == '^')
{
allowcaret = true;
}
else if (i + 1 == commandLine.Length)
{
result.Append(chr);
started = true;
escaped = false;
}
}
else if (escaped)
{
result.Append(chr);
started = true;
escaped = false;
}
else if (chr == '"')
{
quoted = !quoted;
started = true;
}
else if (chr == '\\' && i + 1 < commandLine.Length && commandLine[i + 1] == '"')
{
escaped = true;
}
else if (chr == ' ' && !quoted)
{
if (started) yield return result.ToString();
result.Clear();
started = false;
}
else
{
result.Append(chr);
started = true;
}
}
if (started) yield return result.ToString();
}
El código que utilicé para generar los resultados de la prueba se puede encontrar aquí