diff --git a/shelltestrunner.cabal b/shelltestrunner.cabal
--- a/shelltestrunner.cabal
+++ b/shelltestrunner.cabal
@@ -1,10 +1,10 @@
 name:           shelltestrunner
-version:        0.4
+version:        0.5
 category:       Testing
 synopsis:       A tool for testing command-line programs.
 description:
  .
- Run a given program through "shell" tests specifed by one or more test
+ Run a given program through \"shell\" tests specifed by one or more test
  files, each of which can specify: command-line arguments, input, expected
  output, expected stderr output, and expected exit code.  This was
  extracted from the hledger project, inspired by the tests in John
@@ -31,27 +31,24 @@
  > >>>= [/regexp/]
  > [..or expected numeric exit code]
  .
- The expected fields can also have a regular expression match expression
- following the delimiter on the same line, with no other data lines, in
- which case the test passes if the output is matched by the regexp. The
- regexp is enclosed in forward slashess. A ! preceding the expression
- negates the match. For example, to check that stdout does not contain
- "axe":
- .
- > >>> !/axe/
+ Each expected field can have either a regular expression match
+ expression, in which case the test passes if the output is matched, or 0
+ or more data lines, in which case the output must match these exactly.  A
+ ! preceding a /regexp/ negates the match. The regular expression syntax
+ is that supported by the regexpr library.
  .
  Apart from the command line, all fields are optional. Only fields
  specified in the test will be tested, unless you use the
  -i/--implicit-tests flag, which will test for empty stdout, empty stderr,
- or 0 exit code whenever fields are omitted.
+ or 0 exit code where fields are omitted.
  .
  Issues:
  .
- - output order is mixed up
- .
- - option processing is weak
+ - can't put / in a regexp
  .
  - can't test input/output which does not end with newline
+ .
+ - option processing is weak
  .
  Wishlist:
  .
diff --git a/shelltestrunner.hs b/shelltestrunner.hs
--- a/shelltestrunner.hs
+++ b/shelltestrunner.hs
@@ -14,13 +14,13 @@
 import Control.Concurrent (forkIO)
 import Control.Concurrent.MVar (newEmptyMVar, putMVar, takeMVar)
 import Control.Monad (liftM,when)
-import Data.List (partition)
+import Data.List (partition, intercalate)
 import Data.Maybe (fromJust,isJust,maybe)
 import qualified Test.HUnit (Test)
 import System.Console.ParseArgs hiding (args)
 import System.Environment (withArgs)
 import System.Exit (ExitCode(..),exitWith)
-import System.IO (Handle, hGetContents, hPutStr, stderr)
+import System.IO (Handle, hGetContents, hPutStr)
 import System.Process (runInteractiveCommand, waitForProcess)
 import Test.Framework (defaultMain)
 import Test.Framework.Providers.HUnit (hUnitTestToTests)
@@ -34,7 +34,7 @@
 
 
 version :: String
-version = "0.4" -- keep synced with .cabal
+version = "0.5" -- keep synced with .cabal
 
 data ArgId = HelpFlag
            | VersionFlag
@@ -201,11 +201,9 @@
 -- running
 
 shellTestToHUnitTest :: Args ArgId -> ShellTest -> Test.HUnit.Test
-shellTestToHUnitTest args t = testname t ~: do {r <- runShellTest args t; assertBool "" r}
-
-runShellTest :: Args ArgId -> ShellTest -> IO Bool
-runShellTest args ShellTest{testname=n,commandargs=c,stdin=i,stdoutExpected=o_expected,
-                            stderrExpected=e_expected,exitCodeExpected=x_expected} = do
+shellTestToHUnitTest args ShellTest{testname=n,commandargs=c,stdin=i,stdoutExpected=o_expected,
+                                    stderrExpected=e_expected,exitCodeExpected=x_expected} = 
+ n ~: do
   let exe = fromJust $ getArgString args ExecutableArg
       cmd = unwords [exe,c]
       (o_expected',e_expected',x_expected') =
@@ -234,17 +232,19 @@
   o_actual <- takeMVar o
   e_actual <- takeMVar e
 
-  let o_ok = maybe True (o_actual `matches`) o_expected'
-  let e_ok = maybe True (e_actual `matches`) e_expected'
-  let x_ok = maybe True ((show $ fromExitCode x_actual) `matches`) x_expected'
-  if o_ok && e_ok && x_ok
-   then do
-     return True 
-   else do
-     when (not o_ok) $ printExpectedActual "stdout" (fromJust o_expected') o_actual
-     when (not e_ok) $ printExpectedActual "stderr" (fromJust e_expected') e_actual
-     when (not x_ok) $ printExpectedActual "exit code" (fromJust x_expected') (show $ fromExitCode x_actual)
-     return False
+  assertString $ addnewline $ intercalate "\n" $ filter (not . null)
+      [if (maybe True (o_actual `matches`) o_expected')
+        then "" 
+        else showExpectedActual "stdout"    (fromJust o_expected') o_actual
+      ,if (maybe True (e_actual `matches`) e_expected')
+        then "" 
+        else showExpectedActual "stderr"    (fromJust e_expected') e_actual
+      ,if (maybe True ((show $ fromExitCode x_actual) `matches`) x_expected')
+        then ""
+        else showExpectedActual "exit code" (fromJust x_expected') (show $ fromExitCode x_actual)
+      ]
+      where addnewline "" = ""
+            addnewline s  = "\n"++s
 
 hGetContentsStrictlyAnd :: Handle -> (String -> IO b) -> IO b
 hGetContentsStrictlyAnd h f = hGetContents h >>= \c -> length c `seq` f c
@@ -255,14 +255,15 @@
 matches s (Numeric p)       = s == p
 matches s (Exact p)         = s == p
 
+showExpectedActual :: String -> Matcher -> String -> String
+showExpectedActual field e a =
+    printf "**Expected %s:%s**Got %s:\n%s" field (showMatcher e) field a
+
 showMatcher :: Matcher -> String
 showMatcher (PositiveRegex r) = " /"++r++"/\n"
 showMatcher (NegativeRegex r) = " !/"++r++"/\n"
-showMatcher (Numeric s)       = "\n"++s
+showMatcher (Numeric s)       = "\n"++s++"\n"
 showMatcher (Exact s)         = "\n"++s
-
-printExpectedActual :: String -> Matcher -> String -> IO ()
-printExpectedActual f e a = hPutStr stderr $ printf "**Expected %s:%s**Got %s:\n%s" f (showMatcher e) f a
 
 
 -- utils
