diff --git a/examples/methods.out b/examples/methods.out
index f60f29e72bea01e4a1403169e2f4b9aece321f08..030f77465e345b37456737878a0b00efec34cd23 100644
--- a/examples/methods.out
+++ b/examples/methods.out
@@ -62,7 +62,9 @@ two
 three
 one	two	three
 one\ntwo\nthree
-one\ntwo\nthree
+one\
+two\
+three
 one	two	three
 apple
 sauce
diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs
index 2f607991b18c4f12dfe4584a0afc695ef883d64f..e67284e46f49b01cbcd255fe0a1b1d19ccfe7753 100644
--- a/src/lib/parser/shell_expand/mod.rs
+++ b/src/lib/parser/shell_expand/mod.rs
@@ -203,13 +203,16 @@ pub(crate) fn expand_string<E: Expander>(
     loop {
         match word_iterator.next() {
             Some(word) => {
-                if let WordToken::Brace(_) = word {
-                    contains_brace = true;
+                match word {
+                    WordToken::Brace(_) => {
+                        contains_brace = true;
+                    }
+                    _ => (),
                 }
                 token_buffer.push(word);
             }
             None if original.is_empty() => {
-                token_buffer.push(WordToken::Normal("", true, false));
+                token_buffer.push(WordToken::Normal("".into(), true, false));
                 break;
             }
             None => break,
@@ -300,12 +303,12 @@ fn expand_braces<E: Expander>(
 
                 slice(&mut output, expanded, index.clone());
             }
-            WordToken::Normal(text, do_glob, tilde) => {
+            WordToken::Normal(ref text, do_glob, tilde) => {
                 expand(
                     &mut output,
                     &mut expanded_words,
                     expand_func,
-                    text,
+                    text.as_ref(),
                     do_glob,
                     tilde,
                 );
@@ -407,12 +410,12 @@ fn expand_single_string_token<E: Expander>(
 
     match *token {
         WordToken::StringMethod(ref method) => method.handle(&mut output, expand_func),
-        WordToken::Normal(text, do_glob, tilde) => {
+        WordToken::Normal(ref text, do_glob, tilde) => {
             expand(
                 &mut output,
                 &mut expanded_words,
                 expand_func,
-                text,
+                text.as_ref(),
                 do_glob,
                 tilde,
             );
@@ -552,12 +555,12 @@ pub(crate) fn expand_tokens<E: Expander>(
                     method.handle(&mut output, expand_func);
                 }
                 WordToken::Brace(_) => unreachable!(),
-                WordToken::Normal(text, do_glob, tilde) => {
+                WordToken::Normal(ref text, do_glob, tilde) => {
                     expand(
                         &mut output,
                         &mut expanded_words,
                         expand_func,
-                        text,
+                        text.as_ref(),
                         do_glob,
                         tilde,
                     );
diff --git a/src/lib/parser/shell_expand/words/mod.rs b/src/lib/parser/shell_expand/words/mod.rs
index 24d686ee968a8a46bd4d3d77adca5f4468cd3bea..eb71d50d1a8627aae18cc5f6d125fdb3e1888bd0 100644
--- a/src/lib/parser/shell_expand/words/mod.rs
+++ b/src/lib/parser/shell_expand/words/mod.rs
@@ -12,6 +12,8 @@ pub(crate) use self::{
     select::{Select, SelectWithSize},
 };
 use super::{super::ArgumentSplitter, expand_string, Expander};
+use shell::escape::unescape;
+use std::borrow::Cow;
 
 // Bit Twiddling Guide:
 // var & FLAG != 0 checks if FLAG is enabled
@@ -32,7 +34,7 @@ bitflags! {
 pub(crate) enum WordToken<'a> {
     /// Represents a normal string who may contain a globbing character
     /// (the second element) or a tilde expression (the third element)
-    Normal(&'a str, bool, bool),
+    Normal(Cow<'a, str>, bool, bool),
     Whitespace(&'a str),
     // Tilde(&'a str),
     Brace(Vec<&'a str>),
@@ -642,7 +644,7 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> {
             return None;
         }
 
-        let mut iterator = self.data.bytes().skip(self.read);
+        let mut iterator = self.data.bytes().skip(self.read).peekable();
         let mut start = self.read;
         let mut glob = false;
         let mut tilde = false;
@@ -710,7 +712,7 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> {
                         Some(b' ') | None => {
                             self.read += 1;
                             let output = &self.data[start..self.read];
-                            return Some(WordToken::Normal(output, glob, tilde));
+                            return Some(WordToken::Normal(output.into(), glob, tilde));
                         }
                         _ => {
                             self.read += 1;
@@ -737,7 +739,7 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> {
                             Some(b' ') | None => {
                                 self.read += 1;
                                 let output = &self.data[start..self.read];
-                                return Some(WordToken::Normal(output, glob, tilde));
+                                return Some(WordToken::Normal(output.into(), glob, tilde));
                             }
                             _ => {
                                 self.read += 1;
@@ -763,48 +765,49 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> {
             match character {
                 _ if self.flags.contains(Flags::BACKSL) => self.flags ^= Flags::BACKSL,
                 b'\\' if !self.flags.contains(Flags::SQUOTE) => {
-                    self.flags ^= Flags::BACKSL;
-
-                    let include_backsl = self.flags.contains(Flags::DQUOTE)
-                        && iterator.clone().next().map_or(false, |nextch| {
-                            nextch != b'$' && nextch != b'\\' && nextch != b'"'
-                        });
+                    pub(crate) fn maybe_unescape(input: &str, contains_escapeable: bool) -> Cow<str> {
+                        if !contains_escapeable {
+                            input.into()
+                        } else {
+                            unescape(input)
+                        }
+                    }
 
-                    let end = if include_backsl {
-                        self.read + 1
-                    } else {
-                        self.read
-                    };
-                    let output = &self.data[start..end];
+                    let next = iterator.next();
                     self.read += 1;
-                    return Some(WordToken::Normal(output, glob, tilde));
+
+                    if self.flags.contains(Flags::DQUOTE) {
+                        let _ = iterator.next();
+                        self.read += 1;
+                        return Some(WordToken::Normal(maybe_unescape(&self.data[start..self.read], next.map_or(true, |c| c == b'$' || c == b'@' || c == b'\\' || c == b'"')), glob, tilde));
+                    }
                 }
                 b'\'' if !self.flags.contains(Flags::DQUOTE) => {
                     self.flags ^= Flags::SQUOTE;
                     let output = &self.data[start..self.read];
                     self.read += 1;
-                    return Some(WordToken::Normal(output, glob, tilde));
+                    return Some(WordToken::Normal(output.into(), glob, tilde));
                 }
                 b'"' if !self.flags.contains(Flags::SQUOTE) => {
                     self.flags ^= Flags::DQUOTE;
                     let output = &self.data[start..self.read];
                     self.read += 1;
-                    return Some(WordToken::Normal(output, glob, tilde));
+                    return Some(WordToken::Normal(output.into(), glob, tilde));
                 }
                 b' ' | b'{' if !self.flags.intersects(Flags::SQUOTE | Flags::DQUOTE) => {
-                    return Some(WordToken::Normal(&self.data[start..self.read], glob, tilde))
+                    return Some(WordToken::Normal(unescape(&self.data[start..self.read]), glob, tilde))
                 }
                 b'$' | b'@' if !self.flags.contains(Flags::SQUOTE) => {
                     if let Some(&character) = self.data.as_bytes().get(self.read) {
                         if character == b' ' {
                             self.read += 1;
                             let output = &self.data[start..self.read];
-                            return Some(WordToken::Normal(output, glob, tilde));
+                            return Some(WordToken::Normal(output.into(), glob, tilde));
                         }
                     }
                     let output = &self.data[start..self.read];
                     if output != "" {
-                        return Some(WordToken::Normal(output, glob, tilde));
+                        return Some(WordToken::Normal(unescape(output), glob, tilde));
                     } else {
                         return self.next();
                     };
@@ -813,7 +816,7 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> {
                     if self.glob_check(&mut iterator) {
                         glob = true;
                     } else {
-                        return Some(WordToken::Normal(&self.data[start..self.read], glob, tilde));
+                        return Some(WordToken::Normal(self.data[start..self.read].into(), glob, tilde));
                     }
                 }
                 b'*' | b'?' if !self.flags.contains(Flags::SQUOTE) => {
@@ -827,7 +830,7 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> {
         if start == self.read {
             None
         } else {
-            Some(WordToken::Normal(&self.data[start..], glob, tilde))
+            Some(WordToken::Normal(unescape(&self.data[start..]), glob, tilde))
         }
     }
 }
diff --git a/src/lib/parser/shell_expand/words/tests.rs b/src/lib/parser/shell_expand/words/tests.rs
index 088e26b8b3680cae6cce810e0586d5c5cb676632..b03a965c68a8475e7de825f7c1b6a76d45814bc4 100644
--- a/src/lib/parser/shell_expand/words/tests.rs
+++ b/src/lib/parser/shell_expand/words/tests.rs
@@ -47,12 +47,11 @@ fn string_method() {
 
 #[test]
 fn escape_with_backslash() {
-    let input = "\\$FOO\\$BAR \\$FOO";
+    let input = r#"\$FOO\$BAR \$FOO"#;
     let expected = vec![
-        WordToken::Normal("$FOO", false, false),
-        WordToken::Normal("$BAR", false, false),
+        WordToken::Normal("$FOO$BAR".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Normal("$FOO", false, false),
+        WordToken::Normal("$FOO".into(), false, false),
     ];
     compare(input, expected);
 }
@@ -99,7 +98,7 @@ fn array_process_within_string_process() {
     compare(
         "echo $(let free=[@(free -h)]; echo @free[6]@free[8]/@free[7])",
         vec![
-            WordToken::Normal("echo", false, false),
+            WordToken::Normal("echo".into(), false, false),
             WordToken::Whitespace(" "),
             WordToken::Process(
                 "let free=[@(free -h)]; echo @free[6]@free[8]/@free[7]",
@@ -152,7 +151,7 @@ fn string_keys() {
 fn nested_processes() {
     let input = "echo $(echo $(echo one)) $(echo one $(echo two) three)";
     let expected = vec![
-        WordToken::Normal("echo", false, false),
+        WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Process("echo $(echo one)", false, Select::All),
         WordToken::Whitespace(" "),
@@ -165,7 +164,7 @@ fn nested_processes() {
 fn words_process_with_quotes() {
     let input = "echo $(git branch | rg '[*]' | awk '{print $2}')";
     let expected = vec![
-        WordToken::Normal("echo", false, false),
+        WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Process(
             "git branch | rg '[*]' | awk '{print $2}'",
@@ -177,7 +176,7 @@ fn words_process_with_quotes() {
 
     let input = "echo $(git branch | rg \"[*]\" | awk '{print $2}')";
     let expected = vec![
-        WordToken::Normal("echo", false, false),
+        WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Process(
             "git branch | rg \"[*]\" | awk '{print $2}'",
@@ -192,16 +191,16 @@ fn words_process_with_quotes() {
 fn test_words() {
     let input = "echo $ABC \"${ABC}\" one{$ABC,$ABC} ~ $(echo foo) \"$(seq 1 100)\"";
     let expected = vec![
-        WordToken::Normal("echo", false, false),
+        WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Variable("ABC", false, Select::All),
         WordToken::Whitespace(" "),
         WordToken::Variable("ABC", true, Select::All),
         WordToken::Whitespace(" "),
-        WordToken::Normal("one", false, false),
+        WordToken::Normal("one".into(), false, false),
         WordToken::Brace(vec!["$ABC", "$ABC"]),
         WordToken::Whitespace(" "),
-        WordToken::Normal("~", false, true),
+        WordToken::Normal("~".into(), false, true),
         WordToken::Whitespace(" "),
         WordToken::Process("echo foo", false, Select::All),
         WordToken::Whitespace(" "),
@@ -214,13 +213,9 @@ fn test_words() {
 fn test_multiple_escapes() {
     let input = "foo\\(\\) bar\\(\\)";
     let expected = vec![
-        WordToken::Normal("foo", false, false),
-        WordToken::Normal("(", false, false),
-        WordToken::Normal(")", false, false),
+        WordToken::Normal("foo()".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Normal("bar", false, false),
-        WordToken::Normal("(", false, false),
-        WordToken::Normal(")", false, false),
+        WordToken::Normal("bar()".into(), false, false),
     ];
     compare(input, expected);
 }
@@ -229,7 +224,7 @@ fn test_multiple_escapes() {
 fn test_arithmetic() {
     let input = "echo $((foo bar baz bing 3 * 2))";
     let expected = vec![
-        WordToken::Normal("echo", false, false),
+        WordToken::Normal("echo".into(), false, false),
         WordToken::Whitespace(" "),
         WordToken::Arithmetic("foo bar baz bing 3 * 2"),
     ];
@@ -240,9 +235,9 @@ fn test_arithmetic() {
 fn test_globbing() {
     let input = "barbaz* bingcrosb*";
     let expected = vec![
-        WordToken::Normal("barbaz*", true, false),
+        WordToken::Normal("barbaz*".into(), true, false),
         WordToken::Whitespace(" "),
-        WordToken::Normal("bingcrosb*", true, false),
+        WordToken::Normal("bingcrosb*".into(), true, false),
     ];
     compare(input, expected);
 }
@@ -251,15 +246,15 @@ fn test_globbing() {
 fn test_empty_strings() {
     let input = "rename '' 0 a \"\"";
     let expected = vec![
-        WordToken::Normal("rename", false, false),
+        WordToken::Normal("rename".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Normal("", false, false),
+        WordToken::Normal("".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Normal("0", false, false),
+        WordToken::Normal("0".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Normal("a", false, false),
+        WordToken::Normal("a".into(), false, false),
         WordToken::Whitespace(" "),
-        WordToken::Normal("", false, false),
+        WordToken::Normal("".into(), false, false),
     ];
     compare(input, expected);
 }
diff --git a/src/lib/shell/completer.rs b/src/lib/shell/completer.rs
index bdcde9798689b77e64927b1ae491e6f39c6f75a5..21e856f485d6cb9da91839d8d7ccf9a2dfd7388f 100644
--- a/src/lib/shell/completer.rs
+++ b/src/lib/shell/completer.rs
@@ -1,4 +1,4 @@
-use super::{directory_stack::DirectoryStack, variables::Variables};
+use super::{directory_stack::DirectoryStack, escape::{escape, unescape}, variables::Variables};
 use glob::glob;
 use liner::{Completer, FilenameCompleter};
 use smallvec::SmallVec;
@@ -149,40 +149,6 @@ where
     })
 }
 
-/// Escapes filenames from the completer so that special characters will be properly escaped.
-///
-/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
-/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
-fn escape(input: &str) -> String {
-    let mut output = Vec::with_capacity(input.len());
-    for character in input.bytes() {
-        match character {
-            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
-            | b'"' | b'\'' | b'#' | b'^' | b'*' => output.push(b'\\'),
-            _ => (),
-        }
-        output.push(character);
-    }
-    unsafe { String::from_utf8_unchecked(output) }
-}
-
-/// Unescapes filenames to be passed into the completer
-fn unescape(input: &str) -> String {
-    let mut output = Vec::with_capacity(input.len());
-    let mut bytes = input.bytes();
-    while let Some(b) = bytes.next() {
-        match b {
-            b'\\' => if let Some(next) = bytes.next() {
-                output.push(next);
-            } else {
-                output.push(b'\\')
-            },
-            _ => output.push(b),
-        }
-    }
-    unsafe { String::from_utf8_unchecked(output) }
-}
-
 /// A completer that combines suggestions from multiple completers.
 #[derive(Clone, Eq, PartialEq)]
 pub(crate) struct MultiCompleter<A, B>
diff --git a/src/lib/shell/escape.rs b/src/lib/shell/escape.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c23d3060f867c163b4e57952427d89f2160b4123
--- /dev/null
+++ b/src/lib/shell/escape.rs
@@ -0,0 +1,31 @@
+use std::borrow::Cow;
+
+/// Escapes filenames from the completer so that special characters will be properly escaped.
+///
+/// NOTE: Perhaps we should submit a PR to Liner to add a &'static [u8] field to
+/// `FilenameCompleter` so that we don't have to perform the escaping ourselves?
+pub(crate) fn escape(input: &str) -> String {
+    let mut output = Vec::with_capacity(input.len());
+    for character in input.bytes() {
+        match character {
+            b'(' | b')' | b'[' | b']' | b'&' | b'$' | b'@' | b'{' | b'}' | b'<' | b'>' | b';'
+            | b'"' | b'\'' | b'#' | b'^' | b'*' => output.push(b'\\'),
+            _ => (),
+        }
+        output.push(character);
+    }
+    unsafe { String::from_utf8_unchecked(output) }
+}
+
+/// Unescapes filenames to be passed into the completer
+pub(crate) fn unescape(input: &str) -> Cow<str> {
+    let mut input: Cow<str> = input.into();
+    while let Some(found) = input.find('\\') {
+        if input.as_ref().chars().skip(found + 1).next().is_some() {
+            input.to_mut().remove(found);
+        } else {
+            break;
+        }
+    }
+    input
+}
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index f8cb21febde67c5d62a2b6eedc8fd6f439b5f99e..a39642d7c4869e218f37f9aa4cf0835760268e57 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -3,6 +3,7 @@ pub(crate) mod binary;
 pub(crate) mod colors;
 mod completer;
 pub(crate) mod directory_stack;
+pub(crate) mod escape;
 pub mod flags;
 mod flow;
 pub(crate) mod flow_control;