diff --git a/fsl/utils/run.py b/fsl/utils/run.py
index b818503600d2d86b5e028cacf4a271921b37c008..af52da2bf34160a9e7a82a9817fde7aed2652e8c 100644
--- a/fsl/utils/run.py
+++ b/fsl/utils/run.py
@@ -34,6 +34,13 @@ execute them.
 """
 
 
+class FSLNotPresent(Exception):
+    """Error raised by the :func:`runfsl` function when ``$FSLDIR`` cannot
+    be found.
+    """
+    pass
+
+
 @contextlib.contextmanager
 def dryrun(*args):
     """Context manager which causes all calls to :func:`run` to be logged but
@@ -68,11 +75,29 @@ def _prepareArgs(args):
     return list(args)
 
 
-def run(*args):
+def run(*args, **kwargs):
     """Call a command and return its output. You can pass the command and
     arguments as a single string, or as a regular or unpacked sequence.
+
+    An exception is raised if the command returns a non-zero exit code, unless
+    the ``returnCode`` option is set to ``True``.
+
+    :arg err: Must be passed as a keyword argument. Defaults to
+              ``False``. If ``True``, standard error is captured and
+              returned.
+
+    :arg ret: Must be passed as a keyword argument. Defaults to
+              ``False``. If ``True``, and the command's return code
+              is non-0, an exception is not raised.
+
+    :returns: A string containing the command's standard output. Or,
+              if ``err is True`` and/or ``returnCode is True``, a tuple
+              containing the standard output, standard error (if
+              ``err``), and return code (if ``returnCode``).
     """
 
+    err  = kwargs.get('err', False)
+    ret  = kwargs.get('ret', False)
     args = _prepareArgs(args)
 
     if DRY_RUN:
@@ -81,13 +106,30 @@ def run(*args):
         log.debug('run: {}'.format(' '.join(args)))
 
     if DRY_RUN:
-        result = '<dryrun>'
+        stdout = '<dryrun>'
+        stderr = ''
     else:
-        result = sp.check_output(args).decode('utf-8').strip()
+        proc           = sp.Popen(args, stdout=sp.PIPE, stderr=sp.PIPE)
+        stdout, stderr = proc.communicate()
+        retcode        = proc.returncode
+
+        stdout = stdout.decode('utf-8').strip()
+        stderr = stderr.decode('utf-8').strip()
+
+        log.debug('stdout: {}'.format(stdout))
+        log.debug('stderr: {}'.format(stderr))
+
+        if not ret and (retcode != 0):
+            raise RuntimeError('{} returned non-zero exit code: {}'.format(
+                args[0], retcode))
+
+    results = [stdout]
 
-    log.debug('result: {}'.format(result))
+    if err: results.append(stderr)
+    if ret: results.append(retcode)
 
-    return result
+    if len(results) == 1: return results[0]
+    else:                 return tuple(results)
 
 
 def runfsl(*args):
@@ -96,7 +138,7 @@ def runfsl(*args):
     """
 
     if fslplatform.fsldir is None:
-        raise RuntimeError('$FSLDIR is not set - FSL cannot be found!')
+        raise FSLNotPresent('$FSLDIR is not set - FSL cannot be found!')
 
     args    = _prepareArgs(args)
     args[0] = op.join(fslplatform.fsldir, 'bin', args[0])