diff --git a/Control/Shell/Base.hs b/Control/Shell/Base.hs
--- a/Control/Shell/Base.hs
+++ b/Control/Shell/Base.hs
@@ -91,16 +91,24 @@
 capture m = do
   env <- getEnv
   (r, w) <- unsafeLiftIO Proc.createPipe
+  v <- unsafeLiftIO $ Conc.newEmptyMVar
+  unsafeLiftIO $ Conc.forkIO $ do
+    s <- IO.hGetContents r
+    Conc.putMVar v $! length s `seq` s
   inEnv (env {envStdOut = w}) m
-  unsafeLiftIO $ IO.hClose w >> IO.hGetContents r
+  unsafeLiftIO $ IO.hClose w >> Conc.takeMVar v
 
 -- | Perform the given computation and return its standard error.
 captureStdErr :: Shell () -> Shell String
 captureStdErr m = do
   env <- getEnv
   (r, w) <- unsafeLiftIO Proc.createPipe
+  v <- unsafeLiftIO $ Conc.newEmptyMVar
+  unsafeLiftIO $ Conc.forkIO $ do
+    s <- IO.hGetContents r
+    Conc.putMVar v $! length s `seq` s
   inEnv (env {envStdErr = w}) m
-  unsafeLiftIO $ IO.hClose w >> IO.hGetContents r
+  unsafeLiftIO $ IO.hClose w >> Conc.takeMVar v
 
 -- | Perform the given computation and return its standard output and error,
 --   in that order.
@@ -109,12 +117,19 @@
   env <- getEnv
   (ro, wo) <- unsafeLiftIO Proc.createPipe
   (re, we) <- unsafeLiftIO Proc.createPipe
+  vo <- unsafeLiftIO $ Conc.newEmptyMVar
+  ve <- unsafeLiftIO $ Conc.newEmptyMVar
+  unsafeLiftIO $ Conc.forkIO $ do
+    so <- IO.hGetContents ro
+    se <- IO.hGetContents re
+    Conc.putMVar vo $! length so `seq` so
+    Conc.putMVar ve $! length se `seq` se
   inEnv (env {envStdOut = wo, envStdErr = we}) m
   unsafeLiftIO $ do
     IO.hClose wo
     IO.hClose we
-    o <- IO.hGetContents ro
-    e <- IO.hGetContents re
+    o <- Conc.takeMVar vo
+    e <- Conc.takeMVar ve
     return (o, e)
 
 -- | Perform the given computation and return its standard output and error,
@@ -124,12 +139,19 @@
   env <- getEnv
   (ro, wo) <- unsafeLiftIO Proc.createPipe
   (re, we) <- unsafeLiftIO Proc.createPipe
+  vo <- unsafeLiftIO $ Conc.newEmptyMVar
+  ve <- unsafeLiftIO $ Conc.newEmptyMVar
+  unsafeLiftIO $ Conc.forkIO $ do
+    so <- IO.hGetContents ro
+    se <- IO.hGetContents re
+    Conc.putMVar vo $! length so `seq` so
+    Conc.putMVar ve $! length se `seq` se
   res <- inEnv (env {envStdOut = wo, envStdErr = we}) $ try m
   unsafeLiftIO $ do
     IO.hClose wo
     IO.hClose we
-    o <- IO.hGetContents ro
-    e <- IO.hGetContents re
+    o <- Conc.takeMVar vo
+    e <- Conc.takeMVar ve
     return (o, e, either Failure (const Success) res)
 
 -- | Lift a pure function to a computation over standard input/output.
diff --git a/Control/Shell/Internal.hs b/Control/Shell/Internal.hs
--- a/Control/Shell/Internal.hs
+++ b/Control/Shell/Internal.hs
@@ -6,7 +6,7 @@
   , shell, runSh
   , exit, run, try, getEnv, inEnv, unsafeLiftIO, (|>)
   ) where
-import Control.Monad (when, ap)
+import Control.Monad (when, ap, forM)
 import qualified Control.Concurrent as Conc
 import qualified Control.Exception as Ex
 import qualified System.Exit as Exit
@@ -91,13 +91,12 @@
   Ex.catch (Right <$> m)
            (\(Ex.SomeException e) -> pure $ Left (Failure (show e)))
 runSh env (Pipe p) = flip Ex.catch except $ do
-  ((stepenv, step) : steps) <- mkEnvs env p
-  ma <- waitPids =<< mapM (uncurry (runStep True)) steps
-  mb <- waitPids . (:[]) =<< runStep False stepenv step
-  case (ma, mb) of
-    (Failure err, _) -> pure $ Left (Failure err)
-    (_, Failure err) -> pure $ Left (Failure err)
-    _                -> pure $ Right ()
+  steps <- mkEnvs env p
+  pids <- mapM (uncurry (runStep True)) steps
+  ma <- waitPids pids
+  case ma of
+    Failure err -> pure $ Left (Failure err)
+    _           -> pure $ Right ()
   where
     except = \(Ex.SomeException e) -> pure $ Left (Failure (show e))
 runSh _ Done = do
@@ -164,11 +163,12 @@
 mkEnvs :: Env -> [PipeStep] -> IO [(Env, PipeStep)]
 mkEnvs env = go [] (envStdIn env)
   where
+    go acc stdi [step] = do
+      let env' = env {envStdIn = stdi, envStdOut = envStdOut env}
+      pure ((env', step) : acc)
     go acc stdi (step : steps) = do
       (next, stdo) <- Proc.createPipe
       go ((env {envStdIn = stdi, envStdOut = stdo}, step):acc) next steps
-    go ((e, s) : steps) _ _ = do
-      pure ((e {envStdOut = envStdOut env}, s) : steps)
     go acc _ _ = pure acc
 
 -- | Terminate a pid, be it process or thread.
diff --git a/shellmate.cabal b/shellmate.cabal
--- a/shellmate.cabal
+++ b/shellmate.cabal
@@ -1,7 +1,7 @@
 name:                shellmate
-version:             0.3.4.1
+version:             0.3.4.2
 synopsis:            Simple interface for shell scripting in Haskell.
-description:         Aims to simplify development of cross-platform shell scripts and similar things.
+description:         Monadic EDSL for writing cross-platform shell scripts in Haskell.
 homepage:            https://github.com/valderman/shellmate
 license:             BSD3
 license-file:        LICENSE
@@ -40,8 +40,8 @@
     transformers >=0.3  && <0.6,
     bytestring   >=0.10 && <0.11,
     filepath     >=1.3  && <1.5,
-    process      >=1.1  && <1.5,
-    directory    >=1.1  && <1.3,
+    process      >=1.1  && <1.7,
+    directory    >=1.1  && <1.4,
     temporary    >=1.1  && <1.3
   default-language:
     Haskell2010
