Fix escapeShellArgument (#3663)

diff --git a/lib/src/io.dart b/lib/src/io.dart
index 6f99b5b..46d910c 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -1090,5 +1090,5 @@
 /// Otherwise, wrap with single quotation, and use '\'' to insert single quote.
 String escapeShellArgument(String x) =>
     RegExp(r'^[a-zA-Z0-9-_=@.]+$').stringMatch(x) == null
-        ? "'${x.replaceAll(r'\', '\\').replaceAll("'", r"'\''")}'"
+        ? "'${x.replaceAll(r'\', r'\\').replaceAll("'", r"'\''")}'"
         : x;
diff --git a/test/dependency_services/dependency_services_test.dart b/test/dependency_services/dependency_services_test.dart
index 02a32a0..1451d42 100644
--- a/test/dependency_services/dependency_services_test.dart
+++ b/test/dependency_services/dependency_services_test.dart
@@ -21,7 +21,7 @@
   String catFile(String filename) {
     final path = p.join(d.sandbox, appPath, filename);
     if (File(path).existsSync()) {
-      final contents = filterUnstableLines(File(path).readAsLinesSync());
+      final contents = File(path).readAsLinesSync().map(filterUnstableText);
 
       return '''
 \$ cat $filename
@@ -82,7 +82,7 @@
 Future<Iterable<String>> outputLines(Stream<List<int>> stream) async {
   final s = await utf8.decodeStream(stream);
   if (s.isEmpty) return [];
-  return filterUnstableLines(s.split('\n'));
+  return s.split('\n').map(filterUnstableText);
 }
 
 Future<void> _listReportApply(
diff --git a/test/io_test.dart b/test/io_test.dart
index 80bd1be..d94e289 100644
--- a/test/io_test.dart
+++ b/test/io_test.dart
@@ -562,6 +562,13 @@
       });
     }
   });
+
+  test('escapeShellArgument', () {
+    expect(escapeShellArgument(r'abc'), r'abc');
+    expect(escapeShellArgument(r'ab c'), r"'ab c'");
+    expect(escapeShellArgument(r'ab\c'), r"'ab\\c'");
+    expect(escapeShellArgument(r"ab\'c"), r"'ab\\'\''c'");
+  });
 }
 
 /// Like [withTempDir], but canonicalizes the path before passing it to [fn].
diff --git a/test/test_pub.dart b/test/test_pub.dart
index 57dc3a0..5680407 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -865,19 +865,17 @@
 /// A [StreamMatcher] that matches multiple lines of output.
 StreamMatcher emitsLines(String output) => emitsInOrder(output.split('\n'));
 
-/// Removes output from pub known to be unstable.
-Iterable<String> filterUnstableLines(List<String> input) {
-  return input
-      // Any paths in output should be relative to the sandbox and with forward
-      // slashes to be stable across platforms.
-      .map((line) {
-    line = line.replaceAll(d.sandbox, r'$SANDBOX').replaceAll(r'\', '/');
-    var port = _globalServer?.port;
-    if (port != null) {
-      line = line.replaceAll(port.toString(), '\$PORT');
-    }
-    return line;
-  });
+/// Removes output from pub known to be unstable across runs or platforms.
+String filterUnstableText(String input) {
+  // Any paths in output should be relative to the sandbox and with forward
+  // slashes to be stable across platforms.
+  input = input.replaceAll(d.sandbox, r'$SANDBOX');
+  input = input.replaceAllMapped(RegExp(r'\\(\S)'), (match) => '/${match[1]}');
+  var port = _globalServer?.port;
+  if (port != null) {
+    input = input.replaceAll(port.toString(), '\$PORT');
+  }
+  return input;
 }
 
 /// Runs `pub outdated [args]` and appends the output to [buffer].
@@ -910,12 +908,13 @@
   //       .join('\n'));
   // }
   final pipe = stdin == null ? '' : ' echo ${escapeShellArgument(stdin)} |';
-  buffer.writeln(filterUnstableLines([
-    '\$$pipe pub ${args.map(escapeShellArgument).join(' ')}',
-    ...await process.stdout.rest.toList(),
-  ]).join('\n'));
-  for (final line in filterUnstableLines(await process.stderr.rest.toList())) {
-    buffer.writeln('[STDERR] $line');
+  buffer.writeln(
+      '\$$pipe pub ${args.map(filterUnstableText).map(escapeShellArgument).join(' ')}');
+  for (final line in await process.stdout.rest.toList()) {
+    buffer.writeln(filterUnstableText(line));
+  }
+  for (final line in await process.stderr.rest.toList()) {
+    buffer.writeln('[STDERR] ${filterUnstableText(line)}');
   }
   if (exitCode != 0) {
     buffer.writeln('[EXIT CODE] $exitCode');
diff --git a/test/testdata/goldens/help_test/pub add --help.txt b/test/testdata/goldens/help_test/pub add --help.txt
index 9aa4016..ecdabff 100644
--- a/test/testdata/goldens/help_test/pub add --help.txt
+++ b/test/testdata/goldens/help_test/pub add --help.txt
@@ -30,7 +30,7 @@
   * Add a git dependency:
     `$topLevelProgram pub add 'foo{"git":"https://github.com/foo/foo"}'`
   * Add a git dependency with a path and ref specified:
-    `$topLevelProgram pub add /
+    `$topLevelProgram pub add \
       'foo{"git":{"url":"../foo.git","ref":"branch","path":"subdir"}}'`
 
 Usage: pub add [options] [dev:]<package>[:descriptor] [[dev:]<package>[:descriptor]