diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,10 @@
 # Revision history for shh
 
+## 0.7.1.0 -- 2020-09-05
+
+* Assume UTF-8 encoding in places. This is a temporary workaroun until we
+  can remove all encoding assumptions.
+
 ## 0.7.0.8 -- 2020-05-28
 
 * Flush std buffers before executing a process to make interaction
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -191,15 +191,20 @@
     {-# LANGUAGE ExtendedDefaultRules #-}
     $(loadEnv SearchPath)
 
-You now have all your executables available as simple to read Haskell
-functions. This may render the namespace unwieldy. Executables may be loaded
-explicitly using instead `load ShellPath`, such as
+You now have all your executables available as simple to Haskell
+functions. If you don't want to load your entire environment you
+can load specific commands directly:
 
     load SearchPath ["echo", "grep", "cat", "ls"]
 
 If you want to check that all the dependencies still exist, you can use
 `missingExecutables :: IO [String]`, which will tell you if anything is
 missing.
+
+For use in a project, it makes sense to have a dedicated module for your
+project which does the template Haskell above. This will prevent recompilation
+of all the executables, and also allow you to easily namespace them to
+avoid collisions with normal Haskell functions.
 
 ### Usage in GHCi
 
diff --git a/shh.cabal b/shh.cabal
--- a/shh.cabal
+++ b/shh.cabal
@@ -1,5 +1,5 @@
 name:                shh
-version:             0.7.0.8
+version:             0.7.1.0
 synopsis:            Simple shell scripting from Haskell
 description:         Provides a shell scripting environment for Haskell. It
                      helps you use external binaries, and allows you to
@@ -33,7 +33,7 @@
     process          >= 1.6.3 && < 1.7,
     split            >= 0.2.3 && < 0.3,
     stringsearch     >= 0.3.6.6 && < 0.4,
-    template-haskell >= 2.13.0 && < 2.17,
+    template-haskell >= 2.13.0 && < 2.18,
     containers       >= 0.5.11 && < 0.7,
     unix             >= 2.7.2 && < 2.8,
     utf8-string
diff --git a/src/Shh.hs b/src/Shh.hs
--- a/src/Shh.hs
+++ b/src/Shh.hs
@@ -75,9 +75,15 @@
     -- to how a shell normally works (even with @-o pipefail@!).
     , Failure(..)
     , ignoreFailure
+    , ignoreCode
     , tryFailure
+    , tryFailureJust
+    , catchFailure
+    , catchFailureJust
     , failWithStdErr
     , exitCode
+    , translateCode
+    , translateCode'
     -- | == Constructing Arguments
     , Cmd
     , ExecArg(..)
diff --git a/src/Shh/Internal.hs b/src/Shh/Internal.hs
--- a/src/Shh/Internal.hs
+++ b/src/Shh/Internal.hs
@@ -101,7 +101,7 @@
     show f = concat $
         [ "Command `"
         ]
-        ++ [intercalate " " (BC8.unpack (failureProg f) : map show (failureArgs f))]
+        ++ [intercalate " " (toString (failureProg f) : map show (failureArgs f))]
         ++
         [ "` failed [exit "
         , show (failureCode f)
@@ -109,7 +109,7 @@
         , prettyCallStack (failureStack f)
         ]
         ++ flip (maybe []) (failureStdErr f) (\s ->
-           ["\n-- stderr --\n" ++ BC8.unpack s])
+           ["\n-- stderr --\n" ++ toString s])
 
 instance Exception Failure
 
@@ -195,9 +195,9 @@
 p &> StdOut = runProc p
 (Proc f) &> StdErr = buildProc $ \i _ e -> f i e e
 (Proc f) &> (Truncate path) = buildProc $ \i _ e ->
-    withBinaryFile (BC8.unpack path) WriteMode $ \h -> f i h e
+    withBinaryFile (toString path) WriteMode $ \h -> f i h e
 (Proc f) &> (Append path) = buildProc $ \i _ e ->
-    withBinaryFile (BC8.unpack path) AppendMode $ \h -> f i h e
+    withBinaryFile (toString path) AppendMode $ \h -> f i h e
 infixl 9 &>
 
 -- | Redirect stderr of this process to another location
@@ -208,9 +208,9 @@
 p &!> StdErr = runProc $ p
 (Proc f) &!> StdOut = buildProc $ \i o _ -> f i o o
 (Proc f) &!> (Truncate path) = buildProc $ \i o _ ->
-    withBinaryFile (BC8.unpack path) WriteMode $ \h -> f i o h
+    withBinaryFile (toString path) WriteMode $ \h -> f i o h
 (Proc f) &!> (Append path) = buildProc $ \i o _ ->
-    withBinaryFile (BC8.unpack path) AppendMode $ \h -> f i o h
+    withBinaryFile (toString path) AppendMode $ \h -> f i o h
 infixl 9 &!>
 
 -- | Lift a Haskell function into a @`Proc`@. The handles are the @stdin@
@@ -434,8 +434,8 @@
 mkProc' :: HasCallStack => Bool -> ByteString -> [ByteString] -> Proc ()
 mkProc' delegate cmd args = Proc $ \i o e -> do
     let
-        cmd' = BC8.unpack cmd
-        args' = BC8.unpack <$> args
+        cmd' = toString cmd
+        args' = toString <$> args
     bracket
         (createProcess_ cmd' (proc cmd' args')
             { std_in = UseHandle i
@@ -543,6 +543,32 @@
 tryFailure :: Shell m => Proc a -> m (Either Failure a)
 tryFailure (Proc f) = buildProc $ \i o e -> try $ f i o e
 
+-- | Like @`tryFailure`@ except that it takes an exception predicate which
+-- selects which exceptions to catch. Any exception not matching the predicate
+-- (returning @Nothing@) is re-thrown.
+tryFailureJust :: Shell m => (Failure -> Maybe b) -> Proc a -> m (Either b a)
+tryFailureJust pr (Proc f) = buildProc $ \i o e -> tryJust pr (f i o e)
+
+-- | Run a `Proc` with an action to take if an exception is thrown.
+catchFailure :: Shell m => Proc a -> (Failure -> Proc a) -> m a
+catchFailure (Proc f) pr = buildProc $ \i o e -> catch (f i o e) (runProc . pr)
+
+-- | Like @`catchFailureJust`@ except that it takes an exception predicate
+-- which selects which exceptions to catch. Any exceptions not matching the
+-- predicate (returning @Nothing@) are re-thrown.
+catchFailureJust :: Shell m => (Failure -> Maybe b) -> Proc a -> (b -> Proc a) -> m a
+catchFailureJust pr (Proc f) h = buildProc $ \i o e -> catchJust pr (f i o e) (runProc . h)
+
+-- | Apply a function that translates non-0 exit codes to results. Any code
+-- that returns a @Nothing@ will be thrown as a @`Failure`@.
+translateCode' :: Shell m => (Int -> Maybe b) -> Proc a -> m (Either b a)
+translateCode' f p = tryFailureJust (f . failureCode) p
+
+-- | Apply a function to non-0 exit codes to extract a result. If @Nothing@
+-- is produced, the @`Failure`@ is thrown.
+translateCode :: Shell m => (Int -> Maybe a) -> Proc a -> m a
+translateCode f p = catchFailureJust (f . failureCode) p pure
+
 -- | Capture the stderr of the proc, and attach it to any @`Failure`@
 -- exceptions that are thrown. The stderr is also forwarded to downstream
 -- processes, or the inherited stderr handle. Note that capturing stderr
@@ -580,6 +606,17 @@
     where
         getCode (Right _) = 0
         getCode (Left  f) = failureCode f
+
+-- | Run the @`Proc`@, but don't throw an exception if it exits with the
+-- given code. Note, that from this point on, if the proc did fail with the
+-- code, everything else now sees it as having exited with 0. If you need
+-- to know the code, you have to use `exitCode`.
+ignoreCode :: (Monad m, Shell m) => Int -> Proc a -> m ()
+ignoreCode code p = catchFailureJust pr (void p) pure
+    where
+        pr f
+            | failureCode f == code = Just ()
+            | otherwise             = Nothing
 
 -- | A class for things that can be converted to arguments on the command
 -- line. The default implementation is to use `show`.
diff --git a/test/Readme.lhs b/test/Readme.lhs
--- a/test/Readme.lhs
+++ b/test/Readme.lhs
@@ -191,15 +191,20 @@
     {-# LANGUAGE ExtendedDefaultRules #-}
     $(loadEnv SearchPath)
 
-You now have all your executables available as simple to read Haskell
-functions. This may render the namespace unwieldy. Executables may be loaded
-explicitly using instead `load ShellPath`, such as
+You now have all your executables available as simple to Haskell
+functions. If you don't want to load your entire environment you
+can load specific commands directly:
 
     load SearchPath ["echo", "grep", "cat", "ls"]
 
 If you want to check that all the dependencies still exist, you can use
 `missingExecutables :: IO [String]`, which will tell you if anything is
 missing.
+
+For use in a project, it makes sense to have a dedicated module for your
+project which does the template Haskell above. This will prevent recompilation
+of all the executables, and also allow you to easily namespace them to
+avoid collisions with normal Haskell functions.
 
 ### Usage in GHCi
 
