diff --git a/src/common.cpp b/src/common.cpp
index 305b85a31..a666a3083 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -1429,6 +1429,7 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in
 
     const bool unescape_special = static_cast<bool>(flags & UNESCAPE_SPECIAL);
     const bool allow_incomplete = static_cast<bool>(flags & UNESCAPE_INCOMPLETE);
+    const bool ignore_backslashes = static_cast<bool>(flags & UNESCAPE_NO_BACKSLASHES);
 
     // The positions of open braces.
     std::vector<size_t> braces;
@@ -1451,21 +1452,23 @@ static bool unescape_string_internal(const wchar_t *const input, const size_t in
         if (mode == mode_unquoted) {
             switch (c) {
                 case L'\\': {
-                    // Backslashes (escapes) are complicated and may result in errors, or appending
-                    // INTERNAL_SEPARATORs, so we have to handle them specially.
-                    auto escape_chars = read_unquoted_escape(input + input_position, &result,
-                                                             allow_incomplete, unescape_special);
-                    if (!escape_chars) {
-                        // A none() return indicates an error.
-                        errored = true;
-                    } else {
-                        // Skip over the characters we read, minus one because the outer loop will
-                        // increment it.
-                        assert(*escape_chars > 0);
-                        input_position += *escape_chars - 1;
+                    if (!ignore_backslashes) {
+                        // Backslashes (escapes) are complicated and may result in errors, or appending
+                        // INTERNAL_SEPARATORs, so we have to handle them specially.
+                        auto escape_chars = read_unquoted_escape(input + input_position, &result,
+                                                                 allow_incomplete, unescape_special);
+                        if (!escape_chars) {
+                            // A none() return indicates an error.
+                            errored = true;
+                        } else {
+                            // Skip over the characters we read, minus one because the outer loop will
+                            // increment it.
+                            assert(*escape_chars > 0);
+                            input_position += *escape_chars - 1;
+                        }
+                        // We've already appended, don't append anything else.
+                        to_append_or_none = none();
                     }
-                    // We've already appended, don't append anything else.
-                    to_append_or_none = none();
                     break;
                 }
                 case L'~': {
diff --git a/src/common.h b/src/common.h
index 471946b6b..310d27b29 100644
--- a/src/common.h
+++ b/src/common.h
@@ -121,7 +121,8 @@ enum escape_string_style_t {
 enum {
     UNESCAPE_DEFAULT = 0,         // default behavior
     UNESCAPE_SPECIAL = 1 << 0,    // escape special fish syntax characters like the semicolon
-    UNESCAPE_INCOMPLETE = 1 << 1  // allow incomplete escape sequences
+    UNESCAPE_INCOMPLETE = 1 << 1,  // allow incomplete escape sequences
+    UNESCAPE_NO_BACKSLASHES = 1 << 2,  // don't handle backslash escapes
 };
 typedef unsigned int unescape_flags_t;