diff --git a/fsl/utils/fslsub.py b/fsl/utils/fslsub.py
index 79e91a79cdf0ea020ea604ea7a489428eebe843a..30ffc0b72d901f774dd722649dc495f45d5668d8 100644
--- a/fsl/utils/fslsub.py
+++ b/fsl/utils/fslsub.py
@@ -33,6 +33,7 @@ Example usage, building a short pipeline::
    info
    output
    func_to_cmd
+   hold
 """
 
 
@@ -426,7 +427,7 @@ def hold(job_ids, hold_filename=None):
     elif not op.isdir(op.split(op.abspath(hold_filename))[0]):
         raise IOError(f"Hold file ({hold_filename}) can not be created in non-existent directory")
 
-    submit(f'touch {hold_filename}', wait_for=job_ids, minutes=1, job_name='.hold')
+    submit(('touch', hold_filename), wait_for=job_ids, minutes=1, job_name='.hold')
 
     while not op.exists(hold_filename):
         time.sleep(10)
diff --git a/fsl/utils/run.py b/fsl/utils/run.py
index 739d89ed2e6fb0e5c89356ef45f2176fd48c3228..3ed799ca8330f2c17585d8959da448c3b9ccf6eb 100644
--- a/fsl/utils/run.py
+++ b/fsl/utils/run.py
@@ -16,6 +16,7 @@
    run
    runfsl
    dryrun
+   hold
 """
 
 
@@ -422,3 +423,16 @@ def wslcmd(cmdpath, *args):
     else:
         # Command was not found in WSL with this path
         return None
+
+
+def hold(job_ids, hold_filename=None):
+    """
+    Waits until all jobs have finished
+
+    :param job_ids: possibly nested sequence of job ids. The job ids themselves should be strings.
+    :param hold_filename: filename to use as a hold file.
+        The containing directory should exist, but the file itself should not.
+        Defaults to a ./.<random characters>.hold in the current directory.
+    :return: only returns when all the jobs have finished
+    """
+    fslsub.hold(job_ids, hold_filename)