-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use 'CommandLineStringSplitter.Instance.Split' parse bug. #1740
Comments
Can you explain a bit more about what you're trying to do and why this output is your expectation? Also, examples using precise strings without the C# escaping would be a little clearer, I think. For context, the Let's use a foreach(var arg in args)
{
Console.WriteLine(arg);
} Your "dotnet publish \"xxx.csproj\" -c Release -o \"./bin/latest/\" -r linux-x64 --self-contained false" Running the above program from the command line in PowerShell (keeping in mind that these examples will differ in other shells) with that string produces this output: dotnet publish \
xxx.csproj\ -c Release -o \./bin/latest/\ -r linux-x64 --self-contained false So that looks like it's working as designed, but at least in this example, it's probably not what you're really looking for. |
using System.CommandLine;
using System.CommandLine.NamingConventionBinder;
var rootCommand = new RootCommand("A set command.");
rootCommand.Name = "SET";
rootCommand.AddArgument(new Argument()
{
Name = "key",
ValueType = typeof(string),
Description = "A string"
});
rootCommand.AddArgument(new Argument()
{
Name = "value",
ValueType = typeof(string),
Description = "A string"
});
rootCommand.Handler = CommandHandler.Create<SetCommand>(cmd =>
{
return cmd.InvokeAsync();
});
while (true)
{
Console.Write("> ");
var line = Console.ReadLine();
if (line == null || (line = line.Trim()).Length == 0) continue;
if (line == "exit") break;
try
{
await rootCommand.InvokeAsync(line);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
class SetCommand
{
public string Key { get; set; } = null!;
public string? Value { get; set; } = null!;
public Task<int> InvokeAsync()
{
Console.WriteLine("Key: {0}", this.Key);
Console.WriteLine("Value: {0}", this.Value);
return Task.FromResult(0);
}
} So, When i call Input
Output
How set char |
This is complicated and hard to talk clearly about. 😅 Going back to the previous example and taking System.CommandLine out of the picture for a moment, the following would work for PowerShell when starting your app (i.e. for the > json '"{\"a\":1}"' Here's what's happening:
If you were to now pass that But, since you're building more of a REPL-style interaction, this shell escaping won't affect the value you get from |
OK. I see. share my code /// <summary>
/// 表示一个命令行的解析器。
/// </summary>
public static class CommandLineParser
{
private static bool TryReadFirstChar(this StringReader reader, out char c)
{
var i = reader.Read();
if (i == -1)
{
c = char.MinValue;
return false;
}
else
{
c = (char)i;
if (char.IsWhiteSpace(c)) return reader.TryReadFirstChar(out c);
return true;
}
}
private static IEnumerable<char> ParseToken(StringReader reader)
{
if (!reader.TryReadFirstChar(out var c)) yield break;
var isQueteString = false;
var qc = char.MinValue;
if(c is '=' or ':')
{
yield break;
}
else if (c is '\"' or '\'')
{
isQueteString = true;
qc = c;
if (reader.Peek() == -1)
{
throw new InvalidDataException("Invalid quete in the string.");
}
}
else
{
yield return c;
}
int i;
while (true)
{
i = reader.Read();
if (i == -1) break;
c = (char)i;
if (isQueteString)
{
var pi = reader.Peek();
if (pi == -1) throw new InvalidDataException("Invalid quete in the string.");
var peek = (char)pi;
if (peek == qc)
{
reader.Read();
if (c == '\\')
{
yield return peek;
}
else
{
yield return c;
yield break;
}
}
else if (c == '\\' && peek == '\\')
{
reader.Read();
yield return peek;
}
else
{
yield return c;
}
}
else
{
if (char.IsWhiteSpace(c) || (c is ':' or '=')) yield break;
yield return c;
}
}
}
/// <summary>
/// 解析指定的命令行。
/// </summary>
/// <param name="commandLine">命令行。</param>
/// <returns>一个命令行参数的列表。</returns>
public static IEnumerable<string> Parse(string commandLine)
{
if (string.IsNullOrWhiteSpace(commandLine)) yield break;
commandLine = commandLine.Trim();
using var reader = new StringReader(commandLine);
do
{
var chars = ParseToken(reader).ToArray();
if (chars.Length == 0) continue;
if (chars.Length == 1 && (chars[0] is ':' or '=')) continue;
var arg = new string(chars);
yield return arg;
} while (reader.Peek() != -1);
}
} xunit [Fact]
public void AllTest()
{
Assert.Equal(new string[] { "text", "abc" }
, CommandLineParser.Parse("text abc"));
Assert.Equal(new string[] { "text", "Hello\"" }
, CommandLineParser.Parse("text Hello\""));
Assert.Equal(new string[] { "text", "Hello\"" }
, CommandLineParser.Parse(" \t text Hello\" \t "));
}
[Fact]
public void QueteTest()
{
var args1 = "\"{\\\"a\\t\\\":1}\"";
Assert.Equal(new string[] { "text", "{\"a\\t\":1}" }
, CommandLineParser.Parse("text " + args1).ToArray());
}
[Fact]
public void Quete2Test()
{
var args1 = "'{\"a\\t\":1}'";
Assert.Equal(new string[] { "text", "{\"a\\t\":1}" }
, CommandLineParser.Parse("text " + args1).ToArray());
}
[Fact]
public void SetTest()
{
Assert.Equal(new string[] { "a", "b", "c", "d", "e", "f", "g", "h" }, CommandLineParser.Parse("a=b c =d e = f g= h").ToArray());
Assert.Equal(new string[] { "a", "b", "c", "d", "e", "f", "g", "h" }, CommandLineParser.Parse("a:b c :d e : f g: h").ToArray());
Assert.Equal(new string[] { "a", ":b" }, CommandLineParser.Parse(" a= ':b'").ToArray());
} |
Thanks @jonsequitur for the insight. I'm not using |
Its parsed array:
But expected array like:
The text was updated successfully, but these errors were encountered: