diff --git a/src/parser/shell_expand/ranges.rs b/src/parser/shell_expand/ranges.rs
index b755f5c2a7f95259afa5452bb5c957a746d1e624..dd1aa903256592bc8f3456a6949a3be559b51c92 100644
--- a/src/parser/shell_expand/ranges.rs
+++ b/src/parser/shell_expand/ranges.rs
@@ -75,13 +75,12 @@ fn char_range(start: u8, mut end: u8, step: isize, inclusive: bool) -> Option<Ve
         return None;
     }
 
-    let char_step = match step.checked_abs() {
-        Some(v) => if v > u8::MAX as isize {
+    let char_step = {
+        let v = step.checked_abs()?;
+        if v > u8::MAX as isize {
             return None;
-        } else {
-            v as u8
-        },
-        None => return None,
+        }
+        v as u8
     };
 
     if start < end {
diff --git a/src/shell/binary/prompt.rs b/src/shell/binary/prompt.rs
index f0c058bd05d1b9678dfbfc0a2367892523dc8e94..176fe083153b4517fb94c6b4ab72b423be45541e 100644
--- a/src/shell/binary/prompt.rs
+++ b/src/shell/binary/prompt.rs
@@ -17,10 +17,7 @@ pub(crate) fn prompt(shell: &mut Shell) -> String {
 }
 
 pub(crate) fn prompt_fn(shell: &mut Shell) -> Option<String> {
-    let function = match shell.functions.get("PROMPT") {
-        Some(func) => func as *const Function,
-        None => return None,
-    };
+    let function = shell.functions.get("PROMPT")? as *const Function;
 
     let mut output = None;
 
diff --git a/src/shell/pipe_exec/command_not_found.rs b/src/shell/pipe_exec/command_not_found.rs
new file mode 100644
index 0000000000000000000000000000000000000000..55954949b9e9d2c91c589edd646114f43cc49793
--- /dev/null
+++ b/src/shell/pipe_exec/command_not_found.rs
@@ -0,0 +1,24 @@
+use super::super::{Capture, Function, Shell};
+use std::process;
+use sys;
+
+pub(crate) fn command_not_found(shell: &mut Shell, command: &str) -> bool {
+    let function = match shell.functions.get("COMMAND_NOT_FOUND") {
+        Some(func) => func as *const Function,
+        None => return false
+    };
+
+    if let Err(err) = shell.fork(Capture::None, |child| {
+        let result = unsafe { function.read() }.execute(child, &["ion", command]);
+        if let Err(err) = result {
+            eprintln!("ion: COMMAND_NOT_FOUND function call: {}", err);
+        }
+    }) {
+        eprintln!("ion: fork error: {}", err);
+        return false;
+    }
+
+    // Ensure that the parent retains ownership of the terminal before exiting.
+    let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id());
+    true
+}
diff --git a/src/shell/pipe_exec/mod.rs b/src/shell/pipe_exec/mod.rs
index 44123729583b639c76eb3ff1e4b3b5516ec193ae..84d1c10c7b9805bb60d1ee9e3180de16d7584dc9 100644
--- a/src/shell/pipe_exec/mod.rs
+++ b/src/shell/pipe_exec/mod.rs
@@ -4,11 +4,13 @@
 //! IDs, watching foreground and background tasks, sending foreground tasks to the background,
 //! handling pipeline and conditional operators, and std{in,out,err} redirections.
 
+mod command_not_found;
 pub mod foreground;
 mod fork;
 pub mod job_control;
 mod streams;
 
+use self::command_not_found::command_not_found;
 use self::fork::{create_process_group, fork_pipe};
 use self::job_control::JobControl;
 use self::streams::{duplicate_streams, redir, redirect_streams};
@@ -495,7 +497,9 @@ impl PipelineExecution for Shell {
                     self.watch_foreground(child.id(), child.id(), move || long, |_| ())
                 }
                 Err(e) => if e.kind() == io::ErrorKind::NotFound {
-                    eprintln!("ion: command not found: {}", short);
+                    if !command_not_found(self, &short) {
+                        eprintln!("ion: command not found: {}", short);
+                    }
                     NO_SUCH_COMMAND
                 } else {
                     eprintln!("ion: error spawning process: {}", e);
@@ -749,7 +753,9 @@ pub(crate) fn pipe(
                                         },
                                         Err(e) => {
                                             return if e.kind() == io::ErrorKind::NotFound {
-                                                eprintln!("ion: command not found: {}", short);
+                                                if !command_not_found(shell, &short) {
+                                                    eprintln!("ion: command not found: {}", short);
+                                                }
                                                 NO_SUCH_COMMAND
                                             } else {
                                                 eprintln!("ion: error spawning process: {}", e);