diff --git a/src/Turtle/Options.hs b/src/Turtle/Options.hs
--- a/src/Turtle/Options.hs
+++ b/src/Turtle/Options.hs
@@ -35,6 +35,7 @@
     ( -- * Types
       Parser
     , ArgName
+    , CommandName
     , ShortName
     , Description
     , HelpMessage
@@ -59,6 +60,7 @@
     , arg
 
       -- * Consume parsers
+    , subcommand
     , options
 
     ) where
@@ -97,6 +99,15 @@
 -- | The short one-character abbreviation for a flag (i.e. @-n@)
 type ShortName = Char
 
+{-| The name of a sub-command
+
+    This is lower-cased to create a sub-command.  For example, a `CommandName` of
+    @\"Name\"@ will parse `name` on the command line before parsing the
+    remaining arguments using the command's subparser.
+-}
+newtype CommandName = CommandName { getCommandName :: Text }
+    deriving (IsString)
+
 {-| A brief description of what your program does
 
     This description will appear in the header of the @--help@ output
@@ -206,3 +217,16 @@
     case f (Text.pack s) of
         Just a -> return a
         Nothing -> Opts.readerAbort Opts.ShowHelpText
+
+{-| Create a sub-command that parses `CommandName` and then parses the rest
+    of the command-line arguments
+
+    The sub-command will have its own `Description` and help text
+-}
+subcommand :: CommandName -> Description -> Parser a -> Parser a
+subcommand cmdName desc p =
+    Opts.subparser (Opts.command name info <> Opts.metavar name)
+  where
+    name = Text.unpack (getCommandName cmdName)
+
+    info = Opts.info p (Opts.header (Text.unpack (getDescription desc)))
diff --git a/src/Turtle/Prelude.hs b/src/Turtle/Prelude.hs
--- a/src/Turtle/Prelude.hs
+++ b/src/Turtle/Prelude.hs
@@ -132,6 +132,7 @@
     , rmtree
     , testfile
     , testdir
+    , testpath
     , date
     , datefile
     , touch
@@ -236,6 +237,7 @@
 import qualified Filesystem
 import Filesystem.Path.CurrentOS (FilePath, (</>))
 import qualified Filesystem.Path.CurrentOS as Filesystem
+import GHC.IO.Exception (IOErrorType(UnsupportedOperation))
 import Network.HostName (getHostName)
 import System.Clock (Clock(..), TimeSpec(..), getTime)
 import System.Environment (
@@ -254,6 +256,7 @@
 import System.IO (Handle, hClose)
 import qualified System.IO as IO
 import System.IO.Temp (withTempDirectory, withTempFile)
+import System.IO.Error (catchIOError, ioeGetErrorType)
 import qualified System.Process as Process
 #ifdef mingw32_HOST_OS
 import qualified System.Win32 as Win32
@@ -352,6 +355,7 @@
 
     let open = do
             (Just hIn, Nothing, Nothing, ph) <- Process.createProcess p'
+            IO.hSetBuffering hIn IO.LineBuffering
             return (hIn, ph)
 
     -- Prevent double close
@@ -386,6 +390,7 @@
 
     let open = do
             (Just hIn, Just hOut, Nothing, ph) <- liftIO (Process.createProcess p')
+            IO.hSetBuffering hIn IO.LineBuffering
             return (hIn, hOut, ph)
 
     -- Prevent double close
@@ -453,6 +458,7 @@
 
     let open = do
             (Just hIn, Just hOut, Nothing, ph) <- liftIO (Process.createProcess p')
+            IO.hSetBuffering hIn IO.LineBuffering
             return (hIn, hOut, ph)
 
     -- Prevent double close
@@ -623,9 +629,19 @@
                 else return child
         else return child
 
--- | Move a file or directory
+{-| Move a file or directory
+
+    Works if the two paths are on the same filesystem.
+    If not, @mv@ will still work when dealing with a regular file,
+    but the operation will not be atomic
+-}
 mv :: MonadIO io => FilePath -> FilePath -> io ()
-mv oldPath newPath = liftIO (Filesystem.rename oldPath newPath)
+mv oldPath newPath = liftIO $ catchIOError (Filesystem.rename oldPath newPath)
+   (\ioe -> if ioeGetErrorType ioe == UnsupportedOperation -- certainly EXDEV
+                then do
+                    Filesystem.copyFile oldPath newPath
+                    Filesystem.removeFile oldPath
+                else ioError ioe)
 
 {-| Create a directory
 
@@ -668,6 +684,14 @@
 testdir :: MonadIO io => FilePath -> io Bool
 testdir path = liftIO (Filesystem.isDirectory path)
 
+-- | Check if a path exists
+testpath :: MonadIO io => FilePath -> io Bool
+testpath path = do
+  exists <- testfile path
+  if exists
+    then return exists
+    else testdir path
+
 {-| Touch a file, updating the access and modification times to the current time
 
     Creates an empty file if it does not exist
@@ -840,7 +864,8 @@
 die :: MonadIO io => Text -> io a
 die txt = liftIO (throwIO (userError (unpack txt)))
 
-infixr 2 .&&., .||.
+infixr 2 .||.
+infixr 3 .&&.
 
 {-| Analogous to `&&` in Bash
 
diff --git a/src/Turtle/Tutorial.hs b/src/Turtle/Tutorial.hs
--- a/src/Turtle/Tutorial.hs
+++ b/src/Turtle/Tutorial.hs
@@ -24,15 +24,21 @@
     If you are already proficient with Haskell, then you can get quickly up to
     speed by reading the Quick Start guide at the top of "Turtle.Prelude".
 
-    The easiest way to follow along with the examples is to download the
-    `stack` package management tool by following the instructions here:
+    If you are on Windows, the easiest way to follow along is to install
+    [Git for Windows](https://git-scm.com/download/win) and use the Git Bash
+    program that it installs to get a fully featured Unix-like environment.
 
+    For all operating systems, the recommended way to compile and run the
+    following examples is to download the `stack` package management tool by
+    following the instructions here:
+
     <https://github.com/commercialhaskell/stack>
 
     ... and then run:
 
 > $ stack install turtle
 
+    This tutorial will mostly focus on using Haskell as a scripting language.
     The first two lines of each script below contain boilerplate instructions
     so that `stack` will load and run the script.  This helps ensure that a
     script will run on any computer that has a `stack` executable, as `stack`
@@ -41,6 +47,13 @@
 
     <https://github.com/commercialhaskell/stack/blob/master/doc/GUIDE.md#ghcrunghc>
 
+    If you want to make a Windows script independently executable outside of a
+    Git Bash environment, you can either (A) compile the script into an
+    executable or (B) run these two commands from a @cmd@ shell with
+    administrator privileges to make all @*.hs@ scripts executable:
+
+> assoc .hs=Haskell
+> ftype Haskell="C:\path\to\stack.exe" "%1" %*
 -}
 
 module Turtle.Tutorial (
@@ -97,6 +110,9 @@
 
     -- * Conclusion
     -- $conclusion
+
+    -- * FAQ
+    -- $faq
     ) where
 
 import Turtle
@@ -1614,3 +1630,30 @@
 -- This library provides an extended suite of Unix-like utilities, but would
 -- still benefit from adding more utilities for better parity with the Unix
 -- ecosystem.  Pull requests to add new utilities are highly welcome!
+
+-- $faq
+--
+-- These are the most frequently asked questions from new users:
+--
+-- /Question:/ How do I convert `FilePath` to `Text`?
+--
+-- /Answer:/ Use @(`format` `fp`)@
+--
+-- /Question:/ My program prints some extra output every time it starts.  How do
+-- I remove it?
+--
+-- /Answer:/ Compile your program and run the executable instead of interpreting-- the program.
+--
+-- /Question:/ What's the easiest way to fail with a descriptive error message
+-- if a subprocess command like `proc`/`shell` returns a non-zero exit code?
+-- code?
+--
+-- /Answer:/ Use @(`proc` cmd args input `.||.` `die` "Descriptive error message")@
+-- or @(`shell` cmdline input `.||.` `die` "Descriptive error message")@, very
+-- similar to Bash and Perl.
+--
+-- /Question:/ How do I close a resource that I acquired?
+--
+-- /Answer:/ Use `runManaged`, `sh`, or (`<|>`) (all resources acquired in the
+-- left stream will close before beginning the right stream).  Alternatively,
+-- use `with` to acquire a resource for a limited scope.
diff --git a/turtle.cabal b/turtle.cabal
--- a/turtle.cabal
+++ b/turtle.cabal
@@ -1,5 +1,5 @@
 Name: turtle
-Version: 1.2.2
+Version: 1.2.3
 Cabal-Version: >=1.10
 Build-Type: Simple
 License: BSD3
