shellSplit function

List<String> shellSplit(
  1. String input
)

Splits a shell command tail into the words the shell would pass as arguments, so --message "hello world" becomes two words rather than three.

shellQuote round-trips the result: quoting each word back into a command reproduces the same argument list.

A backslash escapes the next character, except inside single quotes and except on Windows, where cmd gives it no such meaning and it has to stay a path separator.

ponytail: an unterminated quote takes the rest of the input as one word rather than failing.

Implementation

List<String> shellSplit(String input) {
  final words = <String>[];
  final current = StringBuffer();
  var hasWord = false;
  var escaped = false;
  String? openQuote;

  for (final char in input.split('')) {
    if (escaped) {
      current.write(char);
      escaped = false;
    } else if (!Platform.isWindows && char == r'\' && openQuote != "'") {
      escaped = true;
      hasWord = true;
    } else if (openQuote == null && (char == ' ' || char == '\t')) {
      if (hasWord) {
        words.add(current.toString());
        current.clear();
        hasWord = false;
      }
    } else if (openQuote == null && (char == "'" || char == '"')) {
      openQuote = char;
      hasWord = true;
    } else if (openQuote == char) {
      openQuote = null;
    } else {
      current.write(char);
      hasWord = true;
    }
  }

  if (hasWord) words.add(current.toString());
  return words;
}