diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index c55323973b075f6026b9ceb1904c22be9907bab6..ddf56a5b72b4449b4079c1c63c8ebbb5f00f9d38 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,18 @@ order.
 -------------------------
 
 
+Added
+^^^^^
+
+
+* New :func:`.winpath` and :func:`wslpath` functions for working with paths
+  when using FSL in a Windows Subsystem for Linux (WSL) environment.
+* New :func:`.wslcmd` function for generating a path to a FSL command installed
+  in a WSL environment.
+* New :meth:`.Platform.fslwsl` attribute for detecting whether FSL is installed
+  in a WSL environment.
+
+
 Fixed
 ^^^^^
 
diff --git a/fsl/utils/path.py b/fsl/utils/path.py
index 8d312bef5f7e427debeb6fde27ce30501220c3f6..0a0859528945a7b944482442b0ffc658d0dc05f9 100644
--- a/fsl/utils/path.py
+++ b/fsl/utils/path.py
@@ -23,6 +23,8 @@ paths.
    removeDuplicates
    uniquePrefix
    commonBase
+   wslpath
+   winpath
 """
 
 
@@ -30,6 +32,9 @@ import os.path as op
 import            os
 import            glob
 import            operator
+import            re
+
+from fsl.utils.platform import platform
 
 
 class PathError(Exception):
@@ -524,3 +529,58 @@ def commonBase(paths):
             return base
 
     raise PathError('No common base')
+
+
+def wslpath(winpath):
+    """
+    Convert Windows path (or a command line argument containing a Windows path)
+    to the equivalent WSL path (e.g. ``c:\\Users`` -> ``/mnt/c/Users``). Also supports
+    paths in the form ``\\wsl$\\(distro)\\users\\...``
+
+    :param winpath: Command line argument which may (or may not) contain a Windows path. It is assumed to be
+                    either of the form <windows path> or --<arg>=<windows path>. Note that we don't need to
+                    handle --arg <windows path> or -a <windows path> since in these cases the argument
+                    and the path will be parsed as separate entities.
+    :return: If ``winpath`` matches a Windows path, the converted argument (including the --<arg>= portion).
+                Otherwise returns ``winpath`` unchanged.
+    """
+    match = re.match(r"^(--[\w-]+=)?\\\\wsl\$[\\\/][^\\^\/]+(.*)$", winpath)
+    if match:
+        arg, path = match.group(1, 2)
+        if arg is None:
+            arg = ""
+        return arg + path.replace("\\", "/")
+
+    match = re.match(r"^(--[\w-]+=)?([a-zA-z]):(.+)$", winpath)
+    if match:
+        arg, drive, path = match.group(1, 2, 3)
+        if arg is None:
+            arg = ""
+        return arg + "/mnt/" + drive.lower() + path.replace("\\", "/")
+
+    return winpath
+
+
+def winpath(wslpath):
+    """
+    Convert a WSL-local filepath (for example ``/usr/local/fsl/``) into a path that can be used from
+    Windows.
+
+    If ``self.fslwsl`` is ``False``, simply returns ``wslpath`` unmodified
+    Otherwise, uses ``FSLDIR`` to deduce the WSL distro in use for FSL.
+    This requires WSL2 which supports the ``\\wsl$\`` network path.
+    wslpath is assumed to be an absolute path.
+    """
+    if not platform.fslwsl:
+        return wslpath
+    else:
+        match = re.match(r"^\\\\wsl\$\\([^\\]+).*$", platform.fsldir)
+        if match:
+            distro = match.group(1)
+        else:
+            distro = None
+
+        if not distro:
+            raise RuntimeError("Could not identify WSL installation from FSLDIR (%s)" % platform.fsldir)
+
+        return "\\\\wsl$\\" + distro + wslpath.replace("/", "\\")
diff --git a/fsl/utils/platform.py b/fsl/utils/platform.py
index 4a97325bc1515017ca93bc1883bea66d8f205420..ebd9478cb79f3d11d5cda0d4bf8a3eeb00c534f7 100644
--- a/fsl/utils/platform.py
+++ b/fsl/utils/platform.py
@@ -292,6 +292,12 @@ class Platform(notifier.Notifier):
         return os.environ.get('FSLDEVDIR', None)
 
 
+    @property
+    def fslwsl(self):
+        """Boolean flag indicating whether FSL is installed in Windows Subsystem for Linux """
+        return self.fsldir is not None and self.fsldir.startswith("\\\\wsl$")
+
+
     @fsldir.setter
     def fsldir(self, value):
         """Changes the value of the :attr:`fsldir` property, and notifies any
diff --git a/fsl/utils/run.py b/fsl/utils/run.py
index caacbf9761bc7c9da7e198449915ee848ce6c04b..e2adbeb8de7d39006b28eac3497573fd281cd8d9 100644
--- a/fsl/utils/run.py
+++ b/fsl/utils/run.py
@@ -20,21 +20,22 @@
 """
 
 
-import               sys
-import               shlex
-import               logging
-import               threading
-import               contextlib
-import               collections
-import subprocess as sp
-import os.path    as op
-
-import               six
+import                    sys
+import                    shlex
+import                    logging
+import                    threading
+import                    contextlib
+import collections.abc as abc
+import subprocess      as sp
+import os.path         as op
+import                    os
+
+import                    six
 
 from   fsl.utils.platform import platform as fslplatform
 import fsl.utils.fslsub                   as fslsub
 import fsl.utils.tempdir                  as tempdir
-
+import fsl.utils.path                     as fslpath
 
 log = logging.getLogger(__name__)
 
@@ -202,7 +203,7 @@ def run(*args, **kwargs):
         if submit is True:
             submit = dict()
 
-    if submit is not None and not isinstance(submit, collections.Mapping):
+    if submit is not None and not isinstance(submit, abc.Mapping):
         raise ValueError('submit must be a mapping containing '
                          'options for fsl.utils.fslsub.submit')
 
@@ -359,7 +360,12 @@ def runfsl(*args, **kwargs):
     args = prepareArgs(args)
     for prefix in prefixes:
         cmdpath = op.join(prefix, args[0])
-        if op.isfile(cmdpath):
+        if fslplatform.fslwsl:
+            wslargs = wslcmd(cmdpath, *args)
+            if wslargs is not None:
+                args = wslargs
+                break
+        elif op.isfile(cmdpath):
             args[0] = cmdpath
             break
 
@@ -372,6 +378,53 @@ def runfsl(*args, **kwargs):
     return run(*args, **kwargs)
 
 
+def wslcmd(cmdpath, *args):
+    """
+    Convert a command + arguments into an equivalent set of arguments that will run the command
+    under Windows Subsystem for Linux
+
+    :param cmdpath: Fully qualified path to the command. This is essentially a WSL path not a Windows
+                    one since FSLDIR is specified as a WSL path, however it may have backslashes
+                    as path separators due to previous use of ``os.path.join``
+    :param args: Sequence of command arguments (the first of which is the unqualified command name)
+
+    :return: If ``cmdpath`` exists and is executable in WSL, return a sequence of command arguments
+             which when executed will run the command in WSL. Windows paths in the argument list will
+             be converted to WSL paths. If ``cmdpath`` was not executable in WSL, returns None
+    """
+    # Check if command exists in WSL (remembering that the command path may include FSLDIR which
+    # is a Windows path)
+    cmdpath = fslpath.wslpath(cmdpath)
+    retcode = sp.call(["wsl", "test", "-x", cmdpath])
+    if retcode == 0:
+        # Form a new argument list and convert any Windows paths in it into WSL paths
+        wslargs = [fslpath.wslpath(arg) for arg in args]
+        wslargs[0] = cmdpath
+        local_fsldir = fslpath.wslpath(fslplatform.fsldir)
+        if fslplatform.fsldevdir:
+            local_fsldevdir = fslpath.wslpath(fslplatform.fsldevdir)
+        else:
+            local_fsldevdir = None
+        # Prepend important environment variables - note that it seems we cannot
+        # use WSLENV for this due to its insistance on path mapping. FIXME FSLDEVDIR?
+        local_path = "$PATH"
+        if local_fsldevdir:
+            local_path += ":%s/bin" % local_fsldevdir
+        local_path += ":%s/bin" % local_fsldir
+        prepargs = [
+            "wsl",
+            "PATH=%s" % local_path,
+            "FSLDIR=%s" % local_fsldir,
+            "FSLOUTPUTTYPE=%s" % os.environ.get("FSLOUTPUTTYPE", "NIFTI_GZ")
+        ]
+        if local_fsldevdir:
+            prepargs.append("FSLDEVDIR=%s" % local_fsldevdir)
+        return prepargs + wslargs
+    else:
+        # Command was not found in WSL with this path
+        return None
+
+
 def wait(job_ids):
     """Proxy for :func:`.fslsub.wait`. """
     return fslsub.wait(job_ids)
diff --git a/tests/test_fsl_utils_path.py b/tests/test_fsl_utils_path.py
index d4cc1bec5ede307776f8f22d5857b692e67c01e6..0361ced5ee2843f4c6827e4bbcabd705251d6cdf 100644
--- a/tests/test_fsl_utils_path.py
+++ b/tests/test_fsl_utils_path.py
@@ -13,6 +13,7 @@ import            shutil
 import            tempfile
 
 import pytest
+import mock
 
 import fsl.utils.path as fslpath
 import fsl.data.image as fslimage
@@ -1395,3 +1396,19 @@ def test_commonBase():
     for ft in failtests:
         with pytest.raises(fslpath.PathError):
             fslpath.commonBase(ft)
+
+def test_wslpath():
+    assert fslpath.wslpath('c:\\Users\\Fishcake\\image.nii.gz') == '/mnt/c/Users/Fishcake/image.nii.gz'
+    assert fslpath.wslpath('--input=x:\\transfers\\scratch\\image_2.nii') == '--input=/mnt/x/transfers/scratch/image_2.nii'
+    assert fslpath.wslpath('\\\\wsl$\\centos 7\\users\\fsl\\file.nii') == '/users/fsl/file.nii'
+    assert fslpath.wslpath('--file=\\\\wsl$\\centos 7\\home\\fsl\\img.nii.gz') == '--file=/home/fsl/img.nii.gz'
+    assert fslpath.wslpath('\\\\wsl$/centos 7/users\\fsl\\file.nii') == '/users/fsl/file.nii'
+
+def test_winpath():
+    """
+    See comment for ``test_fslwsl`` for why we are overwriting FSLDIR
+    """
+    with mock.patch.dict('os.environ', **{ 'FSLDIR' : '\\\\wsl$\\my cool linux distro v2.0\\usr\\local\\fsl'}):
+        assert fslpath.winpath("/home/fsl/myfile.dat") == '\\\\wsl$\\my cool linux distro v2.0\\home\\fsl\\myfile.dat'
+    with mock.patch.dict('os.environ', **{ 'FSLDIR' : '/opt/fsl'}):
+        assert fslpath.winpath("/home/fsl/myfile.dat") == '/home/fsl/myfile.dat'
diff --git a/tests/test_platform.py b/tests/test_platform.py
index 05321fb43bf9578fd1e59714d118f335bb6f1640..9024bec8262658dd419c48ef288f2d4af2a75c8c 100644
--- a/tests/test_platform.py
+++ b/tests/test_platform.py
@@ -212,3 +212,17 @@ def test_detect_ssh():
         p = fslplatform.Platform()
         assert not p.inSSHSession
         assert not p.inVNCSession
+
+def test_fslwsl():
+    """
+    Note that ``Platform.fsldir`` requires the directory in ``FSLDIR`` to exist and
+    sets ``FSLDIR`` to ``None`` if it doesn't. So we create a ``Platform`` first 
+    and then overwrite ``FSLDIR``. This is a bit of a hack but the logic we are testing
+    here is whether ``Platform.fslwsl`` recognizes a WSL ``FSLDIR`` string
+    """
+    p = fslplatform.Platform()
+    with mock.patch.dict('os.environ', **{ 'FSLDIR' : '\\\\wsl$\\my cool linux distro v1.0\\usr\\local\\fsl'}):
+        assert p.fslwsl
+
+    with mock.patch.dict('os.environ', **{ 'FSLDIR' : '/usr/local/fsl'}):
+        assert not p.fslwsl