diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs
index 36c52326fc03a49516677cd650a53a21d1d43d65..7300fd697387e3576afebf1bda85426f063ea4a6 100644
--- a/src/lib/shell/pipe_exec/mod.rs
+++ b/src/lib/shell/pipe_exec/mod.rs
@@ -709,50 +709,31 @@ impl PipelineExecution for Shell {
         stdout: &Option<File>,
         stderr: &Option<File>,
     ) -> i32 {
-        return match unsafe { sys::fork() } {
-            Ok(0) => {
-                if let Some(ref file) = *stdin {
-                    redir(file.as_raw_fd(), sys::STDIN_FILENO);
-                    let _ = sys::close(file.as_raw_fd());
+        let result = sys::fork_and_exec(
+                name,
+                &args,
+                if let Some(ref f) = *stdin { Some(f.as_raw_fd()) } else { None },
+                if let Some(ref f) = *stdout { Some(f.as_raw_fd()) } else { None },
+                if let Some(ref f) = *stderr { Some(f.as_raw_fd()) } else { None },
+                false,
+                || prepare_child(false)
+            );
+
+            match result {
+                Ok(pid) => {
+                    self.watch_foreground(pid as i32, "")
                 }
-
-                if let Some(ref file) = *stdout {
-                    redir(file.as_raw_fd(), sys::STDOUT_FILENO);
-                    let _ = sys::close(file.as_raw_fd());
+                Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
+                    if !command_not_found(self, &name) {
+                        eprintln!("ion: command not found: {}", name);
+                    }
+                    NO_SUCH_COMMAND
                 }
-
-                if let Some(ref file) = *stderr {
-                    redir(file.as_raw_fd(), sys::STDERR_FILENO);
-                    let _ = sys::close(file.as_raw_fd());
+                Err(ref err) => {
+                    eprintln!("ion: command exec error: {}", err);
+                    FAILURE
                 }
-
-                prepare_child(false);
-                let code = match sys::execve(&name, &args, false) {
-                    ref err if err.kind() == io::ErrorKind::NotFound => {
-                        if !command_not_found(self, &name) {
-                            eprintln!("ion: command not found: {}", name);
-                        }
-                        NO_SUCH_COMMAND
-                    }
-                    ref err => {
-                        eprintln!("ion: command exec error: {}", err);
-                        FAILURE
-                    }
-                };
-                sys::fork_exit(code);
-            },
-            Ok(pid) => {
-                close(stdin);
-                close(stdout);
-                close(stderr);
-                // TODO: get long string
-                self.watch_foreground(pid as i32, "")
-            }
-            Err(why) => {
-                eprintln!("ion: failed to fork: {}", why);
-                COULD_NOT_EXEC
             }
-        }
     }
 }
 
@@ -966,45 +947,28 @@ fn spawn_proc(
     match cmd {
         RefinedJob::External { ref name, ref args, ref stdout, ref stderr, ref stdin} => {
             let args: Vec<&str> = args.iter().skip(1).map(|x| x as &str).collect();
-            match unsafe { sys::fork() } {
-                Ok(0) => {
-                    if let Some(ref file) = *stdin {
-                        redir(file.as_raw_fd(), sys::STDIN_FILENO);
-                        let _ = sys::close(file.as_raw_fd());
-                    }
-                    if let Some(ref file) = *stdout {
-                        redir(file.as_raw_fd(), sys::STDOUT_FILENO);
-                        let _ = sys::close(file.as_raw_fd());
-                    }
-                    if let Some(ref file) = *stderr {
-                        redir(file.as_raw_fd(), sys::STDERR_FILENO);
-                        let _ = sys::close(file.as_raw_fd());
-                    }
-
-                    prepare_child(child_blocked);
-                    let code = match sys::execve(&name, &args, false) {
-                        ref err if err.kind() == io::ErrorKind::NotFound => {
-                            if !command_not_found(shell, &name) {
-                                eprintln!("ion: command not found: {}", name);
-                            }
-                            NO_SUCH_COMMAND
-                        }
-                        ref err => {
-                            eprintln!("ion: command exec error: {}", err);
-                            FAILURE
-                        }
-                    };
-                    sys::fork_exit(code);
-                },
+            let result = sys::fork_and_exec(
+                name,
+                &args,
+                if let Some(ref f) = *stdin { Some(f.as_raw_fd()) } else { None },
+                if let Some(ref f) = *stdout { Some(f.as_raw_fd()) } else { None },
+                if let Some(ref f) = *stderr { Some(f.as_raw_fd()) } else { None },
+                false,
+                || prepare_child(child_blocked)
+            );
+
+            match result {
                 Ok(pid) => {
-                    close(stdin);
-                    close(stdout);
-                    close(stderr);
                     *last_pid = *current_pid;
                     *current_pid = pid;
-                },
-                Err(e) => {
-                    eprintln!("ion: failed to fork {}: {}", short, e);
+                }
+                Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
+                    if !command_not_found(shell, &name) {
+                        eprintln!("ion: command not found: {}", name);
+                    }
+                }
+                Err(ref err) => {
+                    eprintln!("ion: command exec error: {}", err);
                 }
             }
         }
diff --git a/src/lib/sys/redox.rs b/src/lib/sys/redox.rs
index 3889d783652d9c5a5a071fc206d5f40db66d3d86..95b652fe019e8dfbf209a037ac22470d0bf0b35c 100644
--- a/src/lib/sys/redox.rs
+++ b/src/lib/sys/redox.rs
@@ -84,7 +84,15 @@ pub(crate) fn setpgid(pid: u32, pgid: u32) -> io::Result<()> {
     cvt(syscall::setpgid(pid as usize, pgid as usize)).and(Ok(()))
 }
 
-pub(crate) fn execve(prog: &str, args: &[&str], clear_env: bool) -> io::Result<()> {
+pub(crate) fn fork_and_exec<F: Fn()>(
+    prog: &str,
+    args: &[&str],
+    stdin: Option<RawFd>,
+    stdout: Option<RawFd>,
+    stderr: Option<RawFd>,
+    clear_env: bool,
+    before_exec: F
+) -> io::Result<u32> {
     // Construct a valid set of arguments to pass to execve. Ensure
     // that the program is the first argument.
     let mut cvt_args: Vec<[usize; 2]> = Vec::new();
@@ -125,14 +133,104 @@ pub(crate) fn execve(prog: &str, args: &[&str], clear_env: bool) -> io::Result<(
     }
 
     if let Some(prog) = prog {
-        // If we found the program. Run it!
-        cvt(syscall::execve(prog.as_os_str().as_bytes(), &cvt_args)).and(Ok(()))
+        unsafe {
+            match fork()? {
+                0 => {
+                    if let Some(stdin) = stdin {
+                        let _ = dup2(stdin, STDIN_FILENO);
+                        let _ = close(stdin);
+                    }
+
+                    if let Some(stdout) = stdout {
+                        let _ = dup2(stdout, STDOUT_FILENO);
+                        let _ = close(stdout);
+                    }
+
+                    if let Some(stderr) = stderr {
+                        let _ = dup2(stderr, STDERR_FILENO);
+                        let _ = close(stderr);
+                    }
+
+                    before_exec();
+
+                    let error = syscall::execve(prog.as_os_str().as_bytes(), &cvt_args);
+                    let error = io::Error::from_raw_os_error(error.err().unwrap().errno);
+                    eprintln!("ion: command exec: {}", error);
+                    fork_exit(1);
+                }
+                pid => {
+                    if let Some(stdin) = stdin {
+                        let _ = close(stdin);
+                    }
+
+                    if let Some(stdout) = stdout {
+                        let _ = close(stdout);
+                    }
+
+                    if let Some(stderr) = stderr {
+                        let _ = close(stderr);
+                    }
+
+                    Ok(pid)
+                }
+            }
+        }
     } else {
         // The binary was not found.
         Err(io::Error::from_raw_os_error(syscall::ENOENT))
     }
 }
 
+pub(crate) fn execve(prog: &str, args: &[&str], clear_env: bool) -> io::Error {
+    // Construct a valid set of arguments to pass to execve. Ensure
+    // that the program is the first argument.
+    let mut cvt_args: Vec<[usize; 2]> = Vec::new();
+    cvt_args.push([prog.as_ptr() as usize, prog.len()]);
+    for arg in args {
+        cvt_args.push([arg.as_ptr() as usize, arg.len()]);
+    }
+
+    // Get the PathBuf of the program if it exists.
+    let prog = if prog.contains(':') || prog.contains('/') {
+        // This is a fully specified scheme or path to an
+        // executable.
+        Some(PathBuf::from(prog))
+    } else if let Ok(paths) = env::var("PATH") {
+        // This is not a fully specified scheme or path.
+        // Iterate through the possible paths in the
+        // env var PATH that this executable may be found
+        // in and return the first one found.
+        env::split_paths(&paths)
+            .filter_map(|mut path| {
+                path.push(prog);
+                if path.exists() {
+                    Some(path)
+                } else {
+                    None
+                }
+            })
+            .next()
+    } else {
+        None
+    };
+
+    // If clear_env set, clear the env.
+    if clear_env {
+        for (key, _) in env::vars() {
+            env::remove_var(key);
+        }
+    }
+
+    if let Some(prog) = prog {
+        // If we found the program. Run it!
+        let error = syscall::execve(prog.as_os_str().as_bytes(), &cvt_args);
+        io::Error::from_raw_os_error(error.err().unwrap().errno)
+    } else {
+        // The binary was not found.
+        io::Error::from_raw_os_error(syscall::ENOENT)
+    }
+}
+
 #[allow(dead_code)]
 pub(crate) fn signal(signal: i32, handler: extern "C" fn(i32)) -> io::Result<()> {
     let new = SigAction {
@@ -209,7 +307,7 @@ pub mod job_control {
     use std::os::unix::process::ExitStatusExt;
     use std::process::ExitStatus;
     use std::sync::{Arc, Mutex};
-    use syscall::{ECHILD, waitpid};
+    use syscall::{self, ECHILD, waitpid};
     use super::{SIGINT, SIGPIPE};
 
     pub(crate) fn watch_background(
@@ -234,11 +332,11 @@ pub mod job_control {
             }
         }
 
-        let pid = get_pid_value(pid);
+        let pgid = get_pid_value(pid);
 
         loop {
             status = 0;
-            let result = waitpid(pid, &mut status, 0);
+            let result = waitpid(pgid, &mut status, 0);
             match result {
                 Err(error) => {
                     match error.errno {
@@ -259,7 +357,7 @@ pub mod job_control {
                             eprintln!("ion: process ended by signal {}", signal);
                             match signal {
                                 SIGINT => {
-                                    let _ = syscall::kill(pid, signal as usize);
+                                    let _ = syscall::kill(pgid, signal as usize);
                                     shell.break_flow = true;
                                 }
                                 _ => {
diff --git a/src/lib/sys/unix/mod.rs b/src/lib/sys/unix/mod.rs
index adf0926627a0ca386ee0619792f6a2aec322107d..f10fa882babf3da9aadc0bf76ad3f86ab82257ca 100644
--- a/src/lib/sys/unix/mod.rs
+++ b/src/lib/sys/unix/mod.rs
@@ -86,6 +86,124 @@ pub(crate) fn killpg(pgid: u32, signal: i32) -> io::Result<()> {
     cvt(unsafe { libc::kill(-(pgid as pid_t), signal as c_int) }).and(Ok(()))
 }
 
+pub(crate) fn fork_and_exec<F: Fn()>(
+    prog: &str,
+    args: &[&str],
+    stdin: Option<RawFd>,
+    stdout: Option<RawFd>,
+    stderr: Option<RawFd>,
+    clear_env: bool,
+    before_exec: F,
+) -> io::Result<u32> {
+    let prog_str = match CString::new(prog) {
+        Ok(prog) => prog,
+        Err(_) => {
+            return Err(io::Error::last_os_error());
+        }
+    };
+
+    // Create a vector of null-terminated strings.
+    let mut cvt_args: Vec<CString> = Vec::new();
+    cvt_args.push(prog_str.clone());
+    for &arg in args.iter() {
+        match CString::new(arg) {
+            Ok(arg) => cvt_args.push(arg),
+            Err(_) => {
+                return Err(io::Error::last_os_error());
+            }
+        }
+    }
+
+    // Create a null-terminated array of pointers to those strings.
+    let mut arg_ptrs: Vec<*const c_char> = cvt_args.iter().map(|x| x.as_ptr()).collect();
+    arg_ptrs.push(ptr::null());
+
+    // Get the PathBuf of the program if it exists.
+    let prog = if prog.contains('/') {
+        // This is a fully specified path to an executable.
+        Some(prog_str)
+    } else if let Ok(paths) = env::var("PATH") {
+        // This is not a fully specified scheme or path.
+        // Iterate through the possible paths in the
+        // env var PATH that this executable may be found
+        // in and return the first one found.
+        env::split_paths(&paths)
+            .filter_map(|mut path| {
+                path.push(prog);
+                match (path.exists(), path.to_str()) {
+                    (true, Some(path)) => CString::new(path).ok(),
+                    _ => None,
+                }
+            })
+            .next()
+    } else {
+        None
+    };
+
+    let mut env_ptrs: Vec<*const c_char> = Vec::new();
+    let mut env_vars: Vec<CString> = Vec::new();
+
+    // If clear_env is not specified build envp
+    if !clear_env {
+        for (key, value) in env::vars() {
+            match CString::new(format!("{}={}", key, value)) {
+                Ok(var) => env_vars.push(var),
+                Err(_) => {
+                    return Err(io::Error::last_os_error());
+                }
+            }
+        }
+        env_ptrs = env_vars.iter().map(|x| x.as_ptr()).collect();
+    }
+    env_ptrs.push(ptr::null());
+
+    if let Some(prog) = prog {
+        unsafe {
+            match fork()? {
+                0 => {
+                    if let Some(stdin) = stdin {
+                        let _ = dup2(stdin, STDIN_FILENO);
+                        let _ = close(stdin);
+                    }
+
+                    if let Some(stdout) = stdout {
+                        let _ = dup2(stdout, STDOUT_FILENO);
+                        let _ = close(stdout);
+                    }
+
+                    if let Some(stderr) = stderr {
+                        let _ = dup2(stderr, STDERR_FILENO);
+                        let _ = close(stderr);
+                    }
+
+                    before_exec();
+
+                    libc::execve(prog.as_ptr(), arg_ptrs.as_ptr(), env_ptrs.as_ptr());
+                    eprintln!("ion: command exec: {}", io::Error::last_os_error());
+                    fork_exit(1);
+                }
+                pid => {
+                    if let Some(stdin) = stdin {
+                        let _ = close(stdin);
+                    }
+
+                    if let Some(stdout) = stdout {
+                        let _ = close(stdout);
+                    }
+
+                    if let Some(stderr) = stderr {
+                        let _ = close(stderr);
+                    }
+
+                    Ok(pid)
+                }
+            }
+        }
+    } else {
+        Err(io::Error::from_raw_os_error(libc::ENOENT))
+    }
+}
+
 pub(crate) fn execve(prog: &str, args: &[&str], clear_env: bool) -> io::Error {
     let prog_str = match CString::new(prog) {
         Ok(prog) => prog,