Skip to content

Instantly share code, notes, and snippets.

@hashchange
Last active November 5, 2025 21:28
Show Gist options
  • Select an option

  • Save hashchange/f4cd619def08def6e90704e9905ce3d0 to your computer and use it in GitHub Desktop.

Select an option

Save hashchange/f4cd619def08def6e90704e9905ce3d0 to your computer and use it in GitHub Desktop.

Revisions

  1. hashchange revised this gist Nov 5, 2025. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -77,7 +77,8 @@ fi
    fatal_error() { echo "$PROGNAME: $1" >&2; exit 1; }

    # Checks if a path is absolute and in Windows format. Ie, it begins with
    # [Drive letter]:\ or \\[UNC host name].
    # [Drive letter]:\ or \\[UNC host name]. A path consisting solely of
    # [Drive letter]: is also accepted.
    is_abs_path_in_windows_format() { [[ "$1" =~ ^[a-zA-Z]:(\\|$)|^\\\\[a-zA-Z] ]]; }

    # Checks if a path is in Windows format.
  2. hashchange revised this gist Nov 5, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -6,7 +6,7 @@ PROGNAME="$(basename "$BASH_SOURCE")"
    if [[ "$1" == '--version' || "$1" == '-v' ]]; then
    fmt -s <<- VERSION_TEXT
    $PROGNAME 1.1.2
    (c) 2022 Michael Heim
    (c) 2022-2025 Michael Heim
    License: MIT
    VERSION_TEXT
  3. hashchange revised this gist May 10, 2022. No changes.
  4. hashchange revised this gist May 10, 2022. No changes.
  5. hashchange revised this gist May 10, 2022. 1 changed file with 8 additions and 6 deletions.
    14 changes: 8 additions & 6 deletions wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ PROGNAME="$(basename "$BASH_SOURCE")"

    if [[ "$1" == '--version' || "$1" == '-v' ]]; then
    fmt -s <<- VERSION_TEXT
    $PROGNAME 1.1.1
    $PROGNAME 1.1.2
    (c) 2022 Michael Heim
    License: MIT
    @@ -115,10 +115,10 @@ while getopts ":ef" option; do
    force_unc=true
    ;;
    \?)
    fatal_error "Option '-$OPTARG' is invalid. Script aborted."
    fatal_error "Option '-$OPTARG' is invalid."
    ;;
    :)
    fatal_error "The argument for option '-$OPTARG' is missing. Script aborted."
    fatal_error "The argument for option '-$OPTARG' is missing."
    ;;
    esac
    done
    @@ -148,11 +148,13 @@ if $force_unc; then
    wslpath -w / | tr '\\' '/'
    [ $? -ne 0 ] && exit 1; set +o pipefail
    )" || fatal_error "Can't determine the UNC path prefix to WSL. \`wslpath\` call failed. Script aborted."
    )" || fatal_error "Can't determine the UNC path prefix to WSL. \`wslpath\` call failed."
    fi

    while IFS= read -r path; do

    [ -z "$path" ] && fatal_error "Missing argument in input from pipe/stdin. No path provided."

    if is_abs_path_in_windows_format "$path"; then
    # Absolute path in Windows format (conventional or UNC). Normalize
    # accidental forward slashes to backslashes, otherwise input is left
    @@ -177,14 +179,14 @@ while IFS= read -r path; do
    realpath -m "$convert" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'
    [ $? -ne 0 ] && exit 1; set +o pipefail
    )" || fatal_error "Error while processing the path \"$path\". Script aborted."
    )" || fatal_error "Error while processing the path \"${path//\\/\\\\}\"."
    elif is_in_windows_format "$path"; then
    # Relative path in Windows format. Normalize accidental forward slashes
    # to backslashes, otherwise input is left as-is.
    converted="$(tr '/' '\\' <<<"$path")" || exit $?
    else
    # Path in Linux format
    abs_path="$(realpath -m "$path")"
    abs_path="$(realpath -m "$path")" || fatal_error "Error while processing the path \"${path//\\/\\\\}\"."
    is_in_windows_filesystem "$abs_path" && convert="$abs_path" || convert="$path"
    # - sed expression #1: if line begins with /mnt/, convert each "/" to "\"
    # See https://unix.stackexchange.com/a/337255/297737
  6. hashchange revised this gist May 10, 2022. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -5,7 +5,7 @@ PROGNAME="$(basename "$BASH_SOURCE")"

    if [[ "$1" == '--version' || "$1" == '-v' ]]; then
    fmt -s <<- VERSION_TEXT
    $PROGNAME 1.1.0
    $PROGNAME 1.1.1
    (c) 2022 Michael Heim
    License: MIT
    @@ -78,7 +78,7 @@ fatal_error() { echo "$PROGNAME: $1" >&2; exit 1; }

    # Checks if a path is absolute and in Windows format. Ie, it begins with
    # [Drive letter]:\ or \\[UNC host name].
    is_abs_path_in_windows_format() { [[ "$1" =~ ^[a-zA-Z]:\\|^\\\\[a-zA-Z] ]]; }
    is_abs_path_in_windows_format() { [[ "$1" =~ ^[a-zA-Z]:(\\|$)|^\\\\[a-zA-Z] ]]; }

    # Checks if a path is in Windows format.
    #
  7. hashchange revised this gist May 10, 2022. 1 changed file with 45 additions and 16 deletions.
    61 changes: 45 additions & 16 deletions wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -5,8 +5,8 @@ PROGNAME="$(basename "$BASH_SOURCE")"

    if [[ "$1" == '--version' || "$1" == '-v' ]]; then
    fmt -s <<- VERSION_TEXT
    $PROGNAME 1.0.0
    (c) 2021 Michael Heim
    $PROGNAME 1.1.0
    (c) 2022 Michael Heim
    License: MIT
    VERSION_TEXT
    @@ -20,11 +20,11 @@ elif [[ "$1" == '--help' || "$1" == '-h' ]]; then
    Converts a path if it points to a location in the Windows file system (/mnt/[drive letter]). If the path is WSL-specific, ie within the Linux file system, it is returned unchanged by default. To force conversion to a UNC path (\\\\wsl$\\Ubuntu\\...), use the -f flag.
    Windows paths (e.g. C:\\Users\\foo) are returned unchanged. However, accidental forward slashes in the path are corrected to backslashes. The -e option (escape backslashes) is honoured as well.
    A Windows path (e.g. C:\\Users\\foo) is returned unchanged. However, accidental forward slashes in the path are corrected to backslashes. The -f option converts a relative path into an absolute path. The -e option (escape backslashes) is honoured as well.
    The conversion is entirely string-based, the path does not have to exist.
    During conversion from Linux to Windows format, trailing backslashes are removed. Trailing slashes in unconverted Linux paths are returned as they are passed in, never added or removed.
    During conversion from Linux to Windows format, trailing backslashes are removed. They are also removed during conversion from a relative to an absolute path (-f option). Trailing slashes in unconverted Linux or Windows paths are returned as they are passed in, never added or removed.
    Usage:
    $PROGNAME [options] path
    @@ -33,7 +33,8 @@ elif [[ "$1" == '--help' || "$1" == '-h' ]]; then
    Options:
    -e Escape backslashes.
    -f Convert Linux-specific paths to UNC paths.
    -f Convert Linux-specific paths to UNC paths, and relative
    paths to absolute paths.
    -v, --version Show version and license.
    -h, --help Show help.
    @@ -75,9 +76,26 @@ fi

    fatal_error() { echo "$PROGNAME: $1" >&2; exit 1; }

    # Checks if a path is in Windows format. Ie, it begins with [Drive letter]:\
    # or \\[UNC host name].
    is_in_windows_format() { [[ "$1" =~ ^[a-zA-Z]:\\|^\\\\[a-zA-Z] ]]; }
    # Checks if a path is absolute and in Windows format. Ie, it begins with
    # [Drive letter]:\ or \\[UNC host name].
    is_abs_path_in_windows_format() { [[ "$1" =~ ^[a-zA-Z]:\\|^\\\\[a-zA-Z] ]]; }

    # Checks if a path is in Windows format.
    #
    # In ambiguous cases, the following rules apply:
    #
    # - If the path contains a backslash, it is treated as a Windows path (even if
    # forward slashes are present, too).
    # - But there is an exception: If the path begins with a single forward slash,
    # it is considered to be a Linux path, even if backslashes are present
    # (because a path beginning with a directory separator does not make sense in
    # Windows).
    # - If the path does not contain any path separator, it is considered to be a
    # Linux path.
    #
    # NB These rules are somewhat different from the `is_in_windows_format()`
    # function in `wsl-linux-path`.
    is_in_windows_format() { { [[ "$1" =~ \\ ]] && [[ ! "$1" =~ ^/[^/\\]+ ]]; }; }

    # Checks if a WSL path points to a location in the Windows file system. Ie, it
    # begins with /mnt/[drive letter]. Expects an absolute path. If necessary,
    @@ -111,7 +129,7 @@ done
    # - https://stackoverflow.com/a/36432966/508355
    # - https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
    #
    # NB Multi-line input may come from stdin (or a redirected file). Therfore,
    # NB Multi-line input may come from stdin (or a redirected file). Therefore,
    # multiple paths are handled from here on out, separated by newlines - one path
    # per line.
    shift $(($OPTIND - 1))
    @@ -135,29 +153,40 @@ fi

    while IFS= read -r path; do

    if is_in_windows_format "$path"; then
    # Normalizing accidental forward slashes to backslashes, otherwise
    # input is left as-is
    if is_abs_path_in_windows_format "$path"; then
    # Absolute path in Windows format (conventional or UNC). Normalize
    # accidental forward slashes to backslashes, otherwise input is left
    # as-is.
    converted="$(tr '/' '\\' <<<"$path")" || exit $?
    elif $force_unc; then
    # - realpath: ensure an absolute path, so we can distinguish between
    # - Temporarily replace backslashes with forward slashes, so that
    # realpath resolves a relative path correctly even if it contains a
    # backslash (Windows format).
    convert="$(tr '\\' '/' <<<"$path")" || exit $?

    # - realpath: ensure an absolute path, so we can distinguish between
    # the Windows FS (/mnt/..) and the Linux FS (everything else)
    # - sed expression #1: if line begins with /mnt/, remove it and convert
    # the next letter to upper case, followed by ":"
    # the next letter to upper case, followed by ":"
    # - sed expression #2: if line begins with / (still), it's the Unix fs
    # root. Replace it with the UNC path prefix (\\wsl$\Ubuntu\)
    # - tr: replace forward slashes with backslashes
    converted="$(
    set -o pipefail # See https://stackoverflow.com/a/19804002/508355
    realpath -m "$path" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'
    realpath -m "$convert" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'
    [ $? -ne 0 ] && exit 1; set +o pipefail
    )" || fatal_error "Error while processing the path \"$path\". Script aborted."
    elif is_in_windows_format "$path"; then
    # Relative path in Windows format. Normalize accidental forward slashes
    # to backslashes, otherwise input is left as-is.
    converted="$(tr '/' '\\' <<<"$path")" || exit $?
    else
    # Path in Linux format
    abs_path="$(realpath -m "$path")"
    is_in_windows_filesystem "$abs_path" && convert="$abs_path" || convert="$path"
    # - sed expression #1: if line begins with /mnt/, convert each "/" to "\"
    # - sed expression #1: if line begins with /mnt/, convert each "/" to "\"
    # See https://unix.stackexchange.com/a/337255/297737
    # - sed expression #2: remove \mnt\, convert next letter to upper case, add ":"
    converted="$(sed -r -e '/^\/mnt\// y_/_\\_' -e 's_^\\mnt\\([a-zA-Z])_\U\1:_' <<<"$convert")" || exit $?
  8. hashchange revised this gist Dec 21, 2021. 1 changed file with 32 additions and 21 deletions.
    53 changes: 32 additions & 21 deletions wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -3,50 +3,61 @@
    # Script name
    PROGNAME="$(basename "$BASH_SOURCE")"

    if [[ "$1" == '--help' || "$1" == '-h' ]]; then
    if [[ "$1" == '--version' || "$1" == '-v' ]]; then
    fmt -s <<- VERSION_TEXT
    $PROGNAME 1.0.0
    (c) 2021 Michael Heim
    License: MIT
    VERSION_TEXT
    exit 0
    elif [[ "$1" == '--help' || "$1" == '-h' ]]; then
    fmt -s <<- HELP_TEXT
    Safely converts a WSL (Linux) path to a Windows path.
    If a path argument is not provided, input is read from standard input instead (so the command can be used in a pipe).
    Converts a path if it points to a location in the Windows file system (/mnt/[drive letter]). If the path is WSL-specific, ie within the Linux file system, it is returned unchanged by default. To force conversion to an UNC path (\\\\wsl$\Ubuntu\...), use the -f flag.
    Windows paths (e.g. C:\Users\foo) are returned unchanged.
    Converts a path if it points to a location in the Windows file system (/mnt/[drive letter]). If the path is WSL-specific, ie within the Linux file system, it is returned unchanged by default. To force conversion to a UNC path (\\\\wsl$\\Ubuntu\\...), use the -f flag.
    The conversion is entirely string-based, the path does not have to exist. Trailing slashes are returned/converted as they are passed in, never added or removed.
    Windows paths (e.g. C:\\Users\\foo) are returned unchanged. However, accidental forward slashes in the path are corrected to backslashes. The -e option (escape backslashes) is honoured as well.
    The conversion is entirely string-based, the path does not have to exist.
    During conversion from Linux to Windows format, trailing backslashes are removed. Trailing slashes in unconverted Linux paths are returned as they are passed in, never added or removed.
    Usage:
    $PROGNAME [options] path
    ... | $PROGNAME [options]
    Options:
    -e Escape backslashes.
    -f Convert Linux-specific paths to UNC paths.
    -h, --help Show help.
    -e Escape backslashes.
    -f Convert Linux-specific paths to UNC paths.
    -v, --version Show version and license.
    -h, --help Show help.
    Conversion examples:
    /mnt/d/Foo/Bar Baz/.quux/file.txt => D:\Foo\Bar Baz\.quux\file.txt
    /mnt/d/Foo/Bar Baz/.quux/ => D:\Foo\Bar Baz\.quux\
    /mnt/d/Foo/Bar Baz/.quux => D:\Foo\Bar Baz\.quux
    /mnt/d/Foo/Bar Baz/.quux/file.txt => D:\\Foo\\Bar Baz\\.quux\\file.txt
    /mnt/d/Foo/Bar Baz/.quux/ => D:\\Foo\\Bar Baz\\.quux
    /mnt/d/Foo/Bar Baz/.quux => D:\\Foo\\Bar Baz\\.quux
    /usr/local/bin/ => /usr/local/bin/
    /usr/local/bin/command.sh => /usr/local/bin/command.sh
    ~ => /home/[user] (*)
    ./bar (assuming cwd /home/m/foo) => ./bar
    ./bar (assuming cwd /mnt/d/Foo) => D:\Foo\bar
    ./bar (assuming cwd /mnt/d/Foo) => D:\\Foo\\bar
    (*) as a result of shell expansion
    With the -f flag:
    ~ => \\\\wsl$\Ubuntu\home\[user]
    /usr/local/bin/ => \\\\wsl$\Ubuntu\usr\local\bin\
    /usr/local/bin/command.sh => \\\\wsl$\Ubuntu\usr\local\bin\command.sh
    ~ => \\\\wsl$\\Ubuntu\\home\\[user]
    /usr/local/bin/ => \\\\wsl$\\Ubuntu\\usr\\local\\bin
    /usr/local/bin/command.sh => \\\\wsl$\\Ubuntu\\usr\\local\\bin\\command.sh
    ./bar (assuming cwd /home/m/foo) => \\\\wsl$\Ubuntu\home\m\foo\bar
    ./bar (assuming cwd /mnt/d/Foo) => D:\Foo\bar
    ./bar (assuming cwd /home/m/foo) => \\\\wsl$\\Ubuntu\\home\\m\\foo\\bar
    ./bar (assuming cwd /mnt/d/Foo) => D:\\Foo\\bar
    $PROGNAME differs from the built-in wslpath utility in several respects:
    @@ -142,7 +153,7 @@ while IFS= read -r path; do
    realpath -m "$path" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'
    [ $? -ne 0 ] && exit 1; set +o pipefail
    )" || fatal_error "Error while processing the path "$path". Script aborted."
    )" || fatal_error "Error while processing the path \"$path\". Script aborted."
    else
    abs_path="$(realpath -m "$path")"
    is_in_windows_filesystem "$abs_path" && convert="$abs_path" || convert="$path"
  9. hashchange revised this gist Dec 11, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    #!/usr/bin/env bash

    # Script name
    PROGNAME=$(basename "$0")
    PROGNAME="$(basename "$BASH_SOURCE")"

    if [[ "$1" == '--help' || "$1" == '-h' ]]; then
    fmt -s <<- HELP_TEXT
  10. hashchange revised this gist Dec 4, 2021. 1 changed file with 26 additions and 9 deletions.
    35 changes: 26 additions & 9 deletions wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -22,6 +22,7 @@ if [[ "$1" == '--help' || "$1" == '-h' ]]; then
    Options:
    -e Escape backslashes.
    -f Convert Linux-specific paths to UNC paths.
    -h, --help Show help.
    @@ -73,10 +74,14 @@ is_in_windows_format() { [[ "$1" =~ ^[a-zA-Z]:\\|^\\\\[a-zA-Z] ]]; }
    is_in_windows_filesystem() { [[ "$1" =~ ^/mnt/[a-zA-Z] ]]; }

    # Option default values
    escape_backslash=false
    force_unc=false

    while getopts ":f" option; do
    while getopts ":ef" option; do
    case $option in
    e)
    escape_backslash=true
    ;;
    f)
    force_unc=true
    ;;
    @@ -108,16 +113,21 @@ paths="$(tr -d '\r' <<<"$paths")"
    [ -z "$paths" ] && fatal_error "Missing argument. No path provided."

    if $force_unc; then
    unc_prefix="$(wslpath -w / | tr '\\' '/')"
    [ $? -ne 0 ] && fatal_error "Can't determine the UNC path prefix to WSL. \`wslpath\` call failed."
    unc_prefix="$(
    set -o pipefail # See https://stackoverflow.com/a/19804002/508355
    wslpath -w / | tr '\\' '/'
    [ $? -ne 0 ] && exit 1; set +o pipefail
    )" || fatal_error "Can't determine the UNC path prefix to WSL. \`wslpath\` call failed. Script aborted."
    fi

    while IFS= read -r path; do

    if is_in_windows_format "$path"; then
    # Normalizing accidental forward slashes to backslashes, otherwise
    # input is left as-is
    tr '/' '\\' <<<"$path"
    converted="$(tr '/' '\\' <<<"$path")" || exit $?
    elif $force_unc; then
    # - realpath: ensure an absolute path, so we can distinguish between
    # the Windows FS (/mnt/..) and the Linux FS (everything else)
    @@ -126,19 +136,26 @@ while IFS= read -r path; do
    # - sed expression #2: if line begins with / (still), it's the Unix fs
    # root. Replace it with the UNC path prefix (\\wsl$\Ubuntu\)
    # - tr: replace forward slashes with backslashes
    set -o pipefail # See https://stackoverflow.com/a/19804002/508355
    converted="$(
    set -o pipefail # See https://stackoverflow.com/a/19804002/508355
    realpath -m "$path" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'
    realpath -m "$path" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'
    [ $? -ne 0 ] && fatal_error "Error while processing the path "$path". Script aborted."
    set +o pipefail
    [ $? -ne 0 ] && exit 1; set +o pipefail
    )" || fatal_error "Error while processing the path "$path". Script aborted."
    else
    abs_path="$(realpath -m "$path")"
    is_in_windows_filesystem "$abs_path" && convert="$abs_path" || convert="$path"
    # - sed expression #1: if line begins with /mnt/, convert each "/" to "\"
    # See https://unix.stackexchange.com/a/337255/297737
    # - sed expression #2: remove \mnt\, convert next letter to upper case, add ":"
    sed -r -e '/^\/mnt\// y_/_\\_' -e 's_^\\mnt\\([a-zA-Z])_\U\1:_' <<<"$convert"
    converted="$(sed -r -e '/^\/mnt\// y_/_\\_' -e 's_^\\mnt\\([a-zA-Z])_\U\1:_' <<<"$convert")" || exit $?
    fi

    if $escape_backslash; then
    sed 's_\\_\\\\_g' <<<"$converted"
    else
    echo "$converted"
    fi

    done <<<"$paths"
  11. hashchange revised this gist Dec 3, 2021. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -81,10 +81,10 @@ while getopts ":f" option; do
    force_unc=true
    ;;
    \?)
    fatal_error "Option '-$OPTARG' is invalid. Skript aborted."
    fatal_error "Option '-$OPTARG' is invalid. Script aborted."
    ;;
    :)
    fatal_error "The argument for option '-$OPTARG' is missing. Skript aborted."
    fatal_error "The argument for option '-$OPTARG' is missing. Script aborted."
    ;;
    esac
    done
    @@ -130,7 +130,7 @@ while IFS= read -r path; do

    realpath -m "$path" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'

    [ $? -ne 0 ] && fatal_error "Error while processing the path "$path". Skript aborted."
    [ $? -ne 0 ] && fatal_error "Error while processing the path "$path". Script aborted."
    set +o pipefail
    else
    abs_path="$(realpath -m "$path")"
  12. hashchange created this gist Dec 3, 2021.
    144 changes: 144 additions & 0 deletions wsl-windows-path
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,144 @@
    #!/usr/bin/env bash

    # Script name
    PROGNAME=$(basename "$0")

    if [[ "$1" == '--help' || "$1" == '-h' ]]; then
    fmt -s <<- HELP_TEXT
    Safely converts a WSL (Linux) path to a Windows path.
    If a path argument is not provided, input is read from standard input instead (so the command can be used in a pipe).
    Converts a path if it points to a location in the Windows file system (/mnt/[drive letter]). If the path is WSL-specific, ie within the Linux file system, it is returned unchanged by default. To force conversion to an UNC path (\\\\wsl$\Ubuntu\...), use the -f flag.
    Windows paths (e.g. C:\Users\foo) are returned unchanged.
    The conversion is entirely string-based, the path does not have to exist. Trailing slashes are returned/converted as they are passed in, never added or removed.
    Usage:
    $PROGNAME [options] path
    ... | $PROGNAME [options]
    Options:
    -f Convert Linux-specific paths to UNC paths.
    -h, --help Show help.
    Conversion examples:
    /mnt/d/Foo/Bar Baz/.quux/file.txt => D:\Foo\Bar Baz\.quux\file.txt
    /mnt/d/Foo/Bar Baz/.quux/ => D:\Foo\Bar Baz\.quux\
    /mnt/d/Foo/Bar Baz/.quux => D:\Foo\Bar Baz\.quux
    /usr/local/bin/ => /usr/local/bin/
    /usr/local/bin/command.sh => /usr/local/bin/command.sh
    ~ => /home/[user] (*)
    ./bar (assuming cwd /home/m/foo) => ./bar
    ./bar (assuming cwd /mnt/d/Foo) => D:\Foo\bar
    (*) as a result of shell expansion
    With the -f flag:
    ~ => \\\\wsl$\Ubuntu\home\[user]
    /usr/local/bin/ => \\\\wsl$\Ubuntu\usr\local\bin\
    /usr/local/bin/command.sh => \\\\wsl$\Ubuntu\usr\local\bin\command.sh
    ./bar (assuming cwd /home/m/foo) => \\\\wsl$\Ubuntu\home\m\foo\bar
    ./bar (assuming cwd /mnt/d/Foo) => D:\Foo\bar
    $PROGNAME differs from the built-in wslpath utility in several respects:
    - \`wslpath -w\` throws an error if the input path does not exist.
    - \`wslpath -w\` always converts WSL-specific paths to UNC paths.
    - \`wslpath -w\` throws an error if the input path is in Windows format.
    Limitations:
    Input paths do not have to exist, but they are expected to be valid paths and do not pass an additional sanity check. Invalid paths may lead to unexpected output, rather than an error.
    HELP_TEXT
    exit 0
    fi

    fatal_error() { echo "$PROGNAME: $1" >&2; exit 1; }

    # Checks if a path is in Windows format. Ie, it begins with [Drive letter]:\
    # or \\[UNC host name].
    is_in_windows_format() { [[ "$1" =~ ^[a-zA-Z]:\\|^\\\\[a-zA-Z] ]]; }

    # Checks if a WSL path points to a location in the Windows file system. Ie, it
    # begins with /mnt/[drive letter]. Expects an absolute path. If necessary,
    # resolve it with `realpath -m` first.
    is_in_windows_filesystem() { [[ "$1" =~ ^/mnt/[a-zA-Z] ]]; }

    # Option default values
    force_unc=false

    while getopts ":f" option; do
    case $option in
    f)
    force_unc=true
    ;;
    \?)
    fatal_error "Option '-$OPTARG' is invalid. Skript aborted."
    ;;
    :)
    fatal_error "The argument for option '-$OPTARG' is missing. Skript aborted."
    ;;
    esac
    done

    # After removing options from the arguments, get the input path from the
    # remaining argument or, if there is none, from stdin (ie, from a pipe). See
    # - https://stackoverflow.com/a/35512655/508355
    # - https://stackoverflow.com/a/36432966/508355
    # - https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
    #
    # NB Multi-line input may come from stdin (or a redirected file). Therfore,
    # multiple paths are handled from here on out, separated by newlines - one path
    # per line.
    shift $(($OPTIND - 1))
    paths="${1:-$(</dev/stdin)}"

    # Clean-up: Removing \r characters which may be left over from calls to Windows
    # utilities.
    paths="$(tr -d '\r' <<<"$paths")"

    [ -z "$paths" ] && fatal_error "Missing argument. No path provided."

    if $force_unc; then
    unc_prefix="$(wslpath -w / | tr '\\' '/')"
    [ $? -ne 0 ] && fatal_error "Can't determine the UNC path prefix to WSL. \`wslpath\` call failed."
    fi

    while IFS= read -r path; do

    if is_in_windows_format "$path"; then
    # Normalizing accidental forward slashes to backslashes, otherwise
    # input is left as-is
    tr '/' '\\' <<<"$path"
    elif $force_unc; then
    # - realpath: ensure an absolute path, so we can distinguish between
    # the Windows FS (/mnt/..) and the Linux FS (everything else)
    # - sed expression #1: if line begins with /mnt/, remove it and convert
    # the next letter to upper case, followed by ":"
    # - sed expression #2: if line begins with / (still), it's the Unix fs
    # root. Replace it with the UNC path prefix (\\wsl$\Ubuntu\)
    # - tr: replace forward slashes with backslashes
    set -o pipefail # See https://stackoverflow.com/a/19804002/508355

    realpath -m "$path" | sed -r -e 's_^/mnt/([a-zA-Z])_\U\1:_' -e "s_^/_${unc_prefix}_" | tr '/' '\\'

    [ $? -ne 0 ] && fatal_error "Error while processing the path "$path". Skript aborted."
    set +o pipefail
    else
    abs_path="$(realpath -m "$path")"
    is_in_windows_filesystem "$abs_path" && convert="$abs_path" || convert="$path"
    # - sed expression #1: if line begins with /mnt/, convert each "/" to "\"
    # See https://unix.stackexchange.com/a/337255/297737
    # - sed expression #2: remove \mnt\, convert next letter to upper case, add ":"
    sed -r -e '/^\/mnt\// y_/_\\_' -e 's_^\\mnt\\([a-zA-Z])_\U\1:_' <<<"$convert"
    fi

    done <<<"$paths"