C#
command line
escape characters
programming
arguments management

Escape command line arguments in c

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Command line argument escaping in C# is mostly about avoiding ambiguity when spaces, quotes, or backslashes appear inside values. The safest strategy is to pass arguments as a list instead of manually building one big command string. This article covers both modern and legacy approaches so your apps behave correctly on Windows, macOS, and Linux.

Understand What Gets Parsed

Your program receives arguments through Main(string[] args). The shell and process launcher decide how raw text becomes tokens before Main runs.

Use this tiny app to inspect what your program actually receives:

csharp
1using System;
2
3class Program
4{
5    static void Main(string[] args)
6    {
7        Console.WriteLine($"Count: {args.Length}");
8        for (int i = 0; i < args.Length; i++)
9        {
10            Console.WriteLine($"[{i}] = {args[i]}");
11        }
12    }
13}

Build and run with quoted values:

bash
dotnet run -- "alpha beta" "say \"hello\"" "C:\\Temp\\file.txt"

This test immediately shows whether spaces and inner quotes are being preserved.

Prefer ProcessStartInfo.ArgumentList

If your C# app launches another process, use ArgumentList instead of concatenating a single argument string. ArgumentList avoids manual escaping in most cases because each item is passed as one logical argument.

csharp
1using System;
2using System.Diagnostics;
3
4class Program
5{
6    static void Main()
7    {
8        var psi = new ProcessStartInfo
9        {
10            FileName = "dotnet",
11            RedirectStandardOutput = true,
12            RedirectStandardError = true,
13            UseShellExecute = false
14        };
15
16        psi.ArgumentList.Add("run");
17        psi.ArgumentList.Add("--");
18        psi.ArgumentList.Add("alpha beta");
19        psi.ArgumentList.Add("say \"hello\"");
20        psi.ArgumentList.Add(@"C:\Temp\report 2026.txt");
21
22        using var process = Process.Start(psi);
23        string output = process!.StandardOutput.ReadToEnd();
24        string err = process.StandardError.ReadToEnd();
25        process.WaitForExit();
26
27        Console.WriteLine(output);
28        if (!string.IsNullOrWhiteSpace(err))
29        {
30            Console.Error.WriteLine(err);
31        }
32    }
33}

This is usually the best answer for robust argument passing in modern .NET.

Handle Legacy APIs and Shell Calls Carefully

Some older APIs and scripts still expect a single argument string. In that case, you must quote and escape consistently for the target platform.

For Windows style parsing, this pattern is common:

csharp
1static string QuoteWindowsArg(string value)
2{
3    if (string.IsNullOrEmpty(value)) return "\"\"";
4    if (!value.Contains(' ') && !value.Contains('"')) return value;
5
6    return "\"" + value.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\"";
7}

Then compose:

csharp
string arg1 = QuoteWindowsArg("alpha beta");
string arg2 = QuoteWindowsArg("say \"hello\"");
string commandLine = $"tool.exe {arg1} {arg2}";

Use this only when you cannot move to ArgumentList. Manual escaping is easy to get wrong, especially with trailing backslashes and nested quotes.

If you need shell features such as pipes or redirects, call the shell explicitly and pass only trusted input. Never inject unvalidated user text into shell command strings.

Validate Behavior with Repeatable Tests

Argument bugs often hide until production because manual testing covers only a few inputs. Add an integration test matrix with spaces, quotes, backslashes, and Unicode strings so your process boundary is always verified.

csharp
1string[] samples =
2{
3    "plain",
4    "two words",
5    "quote: \"ok\"",
6    @"C:\Temp\folder name\file.txt",
7    "naive café"
8};
9
10foreach (var sample in samples)
11{
12    Console.WriteLine($"Test value: {sample}");
13    // Launch child process with ArgumentList and assert exact round-trip text.
14}

Round-trip checks give confidence that the argument your parent process sends is exactly what the child process receives. This is especially helpful for build agents where shell behavior can differ from local machines.

Common Pitfalls

A frequent bug is mixing shell escaping rules with process escaping rules. A string that works in cmd may fail in PowerShell or Bash. Test in the exact runtime environment you deploy.

Another problem is hand built argument strings for user input. This creates command injection risk and parsing bugs. Prefer ProcessStartInfo.ArgumentList and avoid UseShellExecute unless there is a clear requirement.

Developers also forget that file paths with spaces need to stay a single argument. If your downstream tool reports missing file errors, log the exact argument array to confirm token boundaries.

Summary

  • Main(string[] args) receives already parsed tokens, not raw command text.
  • Use ProcessStartInfo.ArgumentList for reliable and secure argument passing.
  • Keep manual escaping only for legacy scenarios that cannot use argument lists.
  • Validate behavior on the same shell and operating system used in production.
  • Add round-trip tests for edge-case values before shipping automation code.

Course illustration
Course illustration

All Rights Reserved.