Torn
Torn

Reputation: 346

Why does a Process.Start shell command fail with "unterminated quoted string" in Linux?

The problem

I am trying to run 7z from C# code to extract some files. The command works under Windows or if I just run it from the shell directly, but not from code. Maybe it's not the exact same, and I don't realize it.

So I'm doing this:

//Unpack dump with 7z
StringBuilder argument = new($"\"{Config.zipPath}\" x \"{dumpPath}\" -so | \"{Config.zipPath}\" x -y -si -ttar");
foreach (string file in new[] { "vn", "vn_titles" /* and other strings */ }) {
    argument.Append($" db/{file} db/{file}.header");
}
Process.Start(Config.shell, string.Format(Config.arguments, argument)).WaitForExit();

The config file is a list of key=value pairs that get loaded into the static Config class. So, it works for Windows:

shell=cmd.exe
zipPath=C:/Program Files/7-Zip-Zstandard/7z.exe
arguments=/C "{0}"

but not for Linux:

shell=ash
zipPath=7z
arguments=-c '{0}'

The ash command there fails with an error:
x: line 0: syntax error: unterminated quoted string


Some things I've tried / checked


The non-ideal found solution

The answer provided by knittl should be correct, as far as I understand. Whether Windows or Linux, the shell should be called with 2 arguments - -c (or /C for Windows' cmd) and then the command I want to run, which is correctly the argument string. This is generally the simpler method. But it doesn't work under Windows.

cmd does not seem to support spaces in path names inside the argument to \C, at all. Not enclosing them in quotation marks fails to recognize them as a single string, and enclosing them in quotation marks makes the path invalid, as quotation marks are not part of the path.

powershell doesn't have this problem, but it has a limit of 256 characters for the argument of -c, also making my command unusable.

So the solution is to detect which OS I'm on via Environment.OSVersion.Platform, and either pass the arguments as a string array for Unix, or use the special feature of the cmd /C command for Win32NT, where it doesn't actually parse the whole command if the first character of the command is a ", but instead just strips that and the last character if it's also a ", saving me from escaping those characters inside the command, which is the only reason my initial version worked at all.

Upvotes: 0

Views: 192

Answers (1)

knittl
knittl

Reputation: 265638

-c '...' must be two arguments exactly (-c and whatever is inside the quotes). I'm not sure how the Process.Start(string, string) overloads splits the second parameter into command line arguments, but from your question it looks like it is not splitting them as one (or your shell) would expect :)

You want to call the overload Start(string, IEnumerable<string>):

Process.Start(Config.shell, new[]{ "-c",  argument })

Upvotes: 2

Related Questions