diff --git a/src/lib/shell/pipe_exec/streams.rs b/src/lib/shell/pipe_exec/streams.rs
index 4e6813874cfa026906c873e44311cfde97de645e..626c017de32a9dd419813c1d32b2d79f3147b33d 100644
--- a/src/lib/shell/pipe_exec/streams.rs
+++ b/src/lib/shell/pipe_exec/streams.rs
@@ -13,14 +13,13 @@ pub(crate) fn redir(old: RawFd, new: RawFd) {
 /// Duplicates STDIN, STDOUT, and STDERR; in that order; and returns them as `File`s.
 /// Why, you ask? A simple safety mechanism to ensure that the duplicated FDs are closed
 /// when dropped.
-pub(crate) fn duplicate_streams() -> io::Result<(File, File, File)> {
-    // Duplicates STDIN and converts it into a `File`.
-    sys::dup(sys::STDIN_FILENO).map(|fd| unsafe { File::from_raw_fd(fd) })
-        // Do the same for stdout, and then meld the result with stdin
-        .and_then(|stdin| sys::dup(sys::STDOUT_FILENO)
-            .map(|fd| unsafe { File::from_raw_fd(fd) })
-            .map(|stdout| (stdin, stdout))
-        )
+pub(crate) fn duplicate_streams() -> io::Result<(Option<File>, File, File)> {
+    // STDIN may have been closed for a background shell, so it is ok if it cannot be duplicated.
+    let stdin = sys::dup(sys::STDIN_FILENO).ok().map(|fd| unsafe { File::from_raw_fd(fd) });
+
+    sys::dup(sys::STDOUT_FILENO)
+        .map(|fd| unsafe { File::from_raw_fd(fd) })
+        .map(|stdout| (stdin, stdout))
         // And then meld stderr alongside stdin and stdout
         .and_then(|(stdin, stdout)| sys::dup(sys::STDERR_FILENO)
             .map(|fd| unsafe { File::from_raw_fd(fd) })
@@ -28,8 +27,10 @@ pub(crate) fn duplicate_streams() -> io::Result<(File, File, File)> {
         )
 }
 
-pub(crate) fn redirect_streams(inp: File, out: File, err: File) {
-    redir(inp.as_raw_fd(), sys::STDIN_FILENO);
+pub(crate) fn redirect_streams(inp: Option<File>, out: File, err: File) {
+    if let Some(inp) = inp {
+        redir(inp.as_raw_fd(), sys::STDIN_FILENO);
+    }
     redir(out.as_raw_fd(), sys::STDOUT_FILENO);
     redir(err.as_raw_fd(), sys::STDERR_FILENO);
 }