diff --git a/Control/Shell.hs b/Control/Shell.hs
--- a/Control/Shell.hs
+++ b/Control/Shell.hs
@@ -1,23 +1,29 @@
-{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, DeriveDataTypeable #-}
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, DeriveDataTypeable,
+             MultiParamTypeClasses, FunctionalDependencies,
+             UndecidableInstances #-}
 -- | Simple interface for shell scripting-like tasks.
 module Control.Shell ( 
-    Shell, shell,
+    Shell,
+    Guard (..),
+    shell,
     mayFail, orElse,
     withEnv, getEnv, lookupEnv,
-    run, run_, runInteractive, sudo,
+    run, run_, runInteractive, genericRun, sudo,
     cd, cpDir, pwd, ls, mkdir, rmdir, inDirectory, isDirectory,
     withHomeDirectory, inHomeDirectory, withAppDirectory, inAppDirectory,
+    forEachFile, cpFilter,
     isFile, rm, mv, cp, file,
     withTempFile, withTempDirectory, inTempDirectory,
     hPutStr, hPutStrLn, echo,
     module System.FilePath, liftIO
   ) where
 import Control.Applicative
-import Control.Monad (ap)
+import Control.Monad (ap, forM, filterM, forM_, when)
 import Control.Monad.IO.Class
 import Data.Time.Clock
 import Data.Typeable
 import System.FilePath
+import System.IO.Unsafe
 import qualified System.Process as Proc
 import qualified System.Directory as Dir
 import qualified System.Exit as Exit
@@ -167,6 +173,38 @@
     s <- IO.hGetContents out
     return (Right s, [Pid p pid])
 
+-- | Run a program and return a boolean indicating whether the command
+--   succeeded, the output from stdout, and the output from stderr.
+--   This command will never fail.
+genericRun :: FilePath -> [String] -> String -> Shell (Bool, String, String)
+genericRun p args stdin = do
+    (Just inp, Just out, Just err, pid) <- createproc
+    Shell $ \_ -> do
+      let feed str = do
+            case splitAt 4096 str of
+              ([], [])      -> IO.hClose inp
+              (first, str') -> IO.hPutStr inp first >> feed str'
+      Conc.forkIO $ feed stdin
+      o <- IO.hGetContents out
+      e <- IO.hGetContents err
+      merr <- waitPids [Pid p pid]
+      return (Right (maybe True (const False) merr, o, e), [])
+  where
+    createproc = Shell $ \env -> do
+      (inp, out, err, pid) <- Proc.createProcess (cproc env)
+      return (Right (inp, out, err, pid), [])
+    cproc env = Proc.CreateProcess {
+        Proc.cmdspec      = Proc.RawCommand p args,
+        Proc.cwd          = Nothing,
+        Proc.env          = Just env,
+        Proc.std_in       = Proc.CreatePipe,
+        Proc.std_out      = Proc.CreatePipe,
+        Proc.std_err      = Proc.CreatePipe,
+        Proc.close_fds    = False,
+        Proc.create_group = False
+      }
+
+
 -- | Like @run@, but echoes the command's text output to the screen instead of
 --   returning it.
 run_ :: FilePath -> [String] -> String -> Shell ()
@@ -249,6 +287,35 @@
           liftIO $ Dir.createDirectoryIfMissing False to
           ls from >>= mapM_ (\f -> cpDir (from </> f) (to </> f))
 
+-- | Recursively copy a directory, but omit all files that do not match the
+--   give predicate.
+cpFiltered :: (FilePath -> Bool) -> FilePath -> FilePath -> Shell ()
+cpFiltered pred from to = do
+  isdir <- isDirectory to
+  -- Lazily create directories only when needed!
+  let to' = unsafePerformIO $ do
+        when (not isdir) $ Dir.createDirectory to
+        return to
+  files <- ls from
+  mapM_ ((`cp` to') . (from </>)) (filter pred files)
+  fromdirs <- filterM (\d -> isDirectory (from </> d)) files
+  forM_ fromdirs $ \dir -> do
+    cpFilter pred (from </> dir) (to </> dir)
+
+-- | Perform an action on each file in the given directory.
+--   This function will traverse any subdirectories of the given as well.
+--   File paths are given relative to the given directory; the current working
+--   directory is not affected.
+forEachFile :: FilePath -> (FilePath -> Shell a) -> Shell [a]
+forEachFile dir f = do
+  echo $ "forEachFile in dir " ++ dir
+  files <- map (dir </>) <$> ls dir
+  xs <- filterM isFile files >>= mapM f
+  fromdirs <- filterM isDirectory files
+  xss <- forM fromdirs $ \d -> do
+    forEachFile d f
+  return $ concat (xs:xss)
+
 -- | Copy a file. Fails if the source is a directory. If the target is a
 --   directory, the source file is copied into that directory using its current
 --   name.
@@ -360,3 +427,20 @@
 -- | @putStrLn@ lifted into Shell for convenience.
 echo :: String -> Shell ()
 echo = liftIO . putStrLn
+
+class Guard guard a | guard -> a where
+  -- | Perform a Shell computation; if the computation succeeds but returns
+  --   a false-ish value, the outer Shell computation fails with the given
+  --   error message.
+  guard :: String -> guard -> Shell a
+
+instance Guard (Maybe a) a where
+  guard _ (Just x) = return x
+  guard desc _     = fail $ "Guard failed: " ++ desc
+
+instance Guard Bool () where
+  guard _ True = return ()
+  guard desc _ = fail $ "Guard failed: " ++ desc
+
+instance Guard a b => Guard (Shell a) b where
+  guard desc m = m >>= \x -> guard desc x
diff --git a/shellmate.cabal b/shellmate.cabal
--- a/shellmate.cabal
+++ b/shellmate.cabal
@@ -1,5 +1,5 @@
 name:                shellmate
-version:             0.1.1
+version:             0.1.2
 synopsis:            Simple interface for shell scripting in Haskell.
 -- description:         
 homepage:            http://github.com/valderman/shellmate
