Skip to content

Instantly share code, notes, and snippets.

@dlenski
Last active September 26, 2025 19:38
Show Gist options
  • Save dlenski/234e2fcf5eaa16687b7ce2c04ed01ad4 to your computer and use it in GitHub Desktop.
Save dlenski/234e2fcf5eaa16687b7ce2c04ed01ad4 to your computer and use it in GitHub Desktop.

Revisions

  1. dlenski revised this gist Sep 26, 2025. 1 changed file with 3 additions and 4 deletions.
    7 changes: 3 additions & 4 deletions winenv.py
    Original file line number Diff line number Diff line change
    @@ -126,15 +126,14 @@ def _convert_paths(winenv: Mapping, paths: Union[Container, bool] = True) -> Map

    if args.associative:
    print(f'declare -A {args.associative}')
    print('; '.join(f'{args.associative}[{shlex.quote(var)}]={shlex.quote(val)}'))
    for var, val in winenv.items():
    print(f'{args.associative}[{shlex.quote(var)}]={shlex.quote(val)}')
    elif args.format == 'json':
    json.dump(winenv, sys.stdout)
    else:
    kw = 'setenv' if args.format == 'csh' else 'export'
    delim = ''
    for var, val in winenv.items():
    if not var.isidentifier():
    warn(f'Windows environment variable {var!r} is not a valid POSIX shell identifier, skipping.')
    else:
    print(f'{delim}{kw} {args.prefix}{var}={shlex.quote(val)}', end='')
    delim='; '
    print(f'{kw} {args.prefix}{var}={shlex.quote(val)}')
  2. dlenski revised this gist Sep 26, 2025. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions winenv.py
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,6 @@

    import subprocess as sp
    import json
    import os
    from typing import Union, Container, Mapping
    from pathlib import PureWindowsPath, PurePosixPath
    import shlex
    @@ -35,7 +34,6 @@

    BACKSLASH = '\\'
    DOUBLE_BACKSLASH = '\\\\'
    DETECT_WSL_PATH = '/proc/sys/fs/binfmt_misc/WSLInterop'
    POWERSHELL_EXE = '/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'


    @@ -139,4 +137,4 @@ def _convert_paths(winenv: Mapping, paths: Union[Container, bool] = True) -> Map
    warn(f'Windows environment variable {var!r} is not a valid POSIX shell identifier, skipping.')
    else:
    print(f'{delim}{kw} {args.prefix}{var}={shlex.quote(val)}', end='')
    delim='; '
    delim='; '
  3. dlenski revised this gist Sep 26, 2025. 1 changed file with 8 additions and 4 deletions.
    12 changes: 8 additions & 4 deletions winenv.py
    Original file line number Diff line number Diff line change
    @@ -23,6 +23,7 @@

    import subprocess as sp
    import json
    import os
    from typing import Union, Container, Mapping
    from pathlib import PureWindowsPath, PurePosixPath
    import shlex
    @@ -34,6 +35,7 @@

    BACKSLASH = '\\'
    DOUBLE_BACKSLASH = '\\\\'
    DETECT_WSL_PATH = '/proc/sys/fs/binfmt_misc/WSLInterop'
    POWERSHELL_EXE = '/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'


    @@ -126,13 +128,15 @@ def _convert_paths(winenv: Mapping, paths: Union[Container, bool] = True) -> Map

    if args.associative:
    print(f'declare -A {args.associative}')
    for var, val in winenv.items():
    print(f'{args.associative}[{shlex.quote(var)}]={shlex.quote(val)}')
    print('; '.join(f'{args.associative}[{shlex.quote(var)}]={shlex.quote(val)}'))
    elif args.format == 'json':
    json.dump(winenv, sys.stdout)
    else:
    kw = 'setenv' if args.format == 'csh' else 'export'
    delim = ''
    for var, val in winenv.items():
    if not var.isidentifier():
    warn(f'Windows environment variable {var!r} is not a valid POSIX shell identifier, commenting out.')
    print(("" if var.isidentifier() else "# ") + f'{kw} {args.prefix}{var}={shlex.quote(val)}')
    warn(f'Windows environment variable {var!r} is not a valid POSIX shell identifier, skipping.')
    else:
    print(f'{delim}{kw} {args.prefix}{var}={shlex.quote(val)}', end='')
    delim='; '
  4. dlenski revised this gist Sep 26, 2025. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions winenv.py
    Original file line number Diff line number Diff line change
    @@ -23,7 +23,6 @@

    import subprocess as sp
    import json
    import os
    from typing import Union, Container, Mapping
    from pathlib import PureWindowsPath, PurePosixPath
    import shlex
    @@ -35,7 +34,6 @@

    BACKSLASH = '\\'
    DOUBLE_BACKSLASH = '\\\\'
    DETECT_WSL_PATH = '/proc/sys/fs/binfmt_misc/WSLInterop'
    POWERSHELL_EXE = '/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'


  5. dlenski revised this gist Sep 26, 2025. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion winenv.py
    Original file line number Diff line number Diff line change
    @@ -23,6 +23,7 @@

    import subprocess as sp
    import json
    import os
    from typing import Union, Container, Mapping
    from pathlib import PureWindowsPath, PurePosixPath
    import shlex
    @@ -34,14 +35,16 @@

    BACKSLASH = '\\'
    DOUBLE_BACKSLASH = '\\\\'
    DETECT_WSL_PATH = '/proc/sys/fs/binfmt_misc/WSLInterop'
    POWERSHELL_EXE = '/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe'


    def _load_winenv() -> dict:
    env2json = '[System.Environment]::GetEnvironmentVariables() | ConvertTo-Json'
    # Robustness fixes based on https://github.com/wslutilities/wslu/blob/1bb1b53c0532394a1433b2eaa041b1c1e6ce0b69/src/wslu-header#L290,
    # with the addition of ErrorActionPreference from https://stackoverflow.com/a/9949909/20789.
    cli = [
    '/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe',
    POWERSHELL_EXE,
    '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command',
    '$ErrorActionPreference = "Stop"; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ' + env2json
    ]
  6. dlenski revised this gist Sep 26, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion winenv.py
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@
    Access Windows environment variables from Python under WSL
    The :py:data:`winenv` and py:data:`winenv_wslpath` objects are
    immutable mappings, lazy-loaded upon initial usage.
    immutable mappings, loaded at module import time.
    py:data:`winenv` contains the unmodified Windows environment variables,
    while py:data:`winenv_wslpath` attempts to convert all ``PATH``-like
  7. dlenski revised this gist Sep 26, 2025. 1 changed file with 16 additions and 5 deletions.
    21 changes: 16 additions & 5 deletions winenv.py
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,5 @@
    #!/usr/bin/env python3

    r'''
    Access Windows environment variables from Python under WSL
    @@ -25,10 +27,14 @@
    from pathlib import PureWindowsPath, PurePosixPath
    import shlex
    import sys

    from warnings import warn

    __all__ = ['winenv', 'winenv_wslpath']

    BACKSLASH = '\\'
    DOUBLE_BACKSLASH = '\\\\'


    def _load_winenv() -> dict:
    env2json = '[System.Environment]::GetEnvironmentVariables() | ConvertTo-Json'
    @@ -52,21 +58,21 @@ def _convert_paths(winenv: Mapping, paths: Union[Container, bool] = True) -> Map
    return winenv
    j = {}
    for var, vals in winenv.items():
    convert = vals.startswith('\\') or vals[1:2] == ':' if paths == True else var in paths
    convert = vals.startswith(BACKSLASH) or (vals[0:1].isalpha() and vals[1:2] == ':') if paths == True else var in paths
    if convert:
    winvals = map(PureWindowsPath, vals.split(';'))
    outvals = []
    for val in winvals:
    if val.anchor.startswith('\\\\'):
    if val.anchor.startswith(DOUBLE_BACKSLASH):
    warn(f'Windows environment variable {var!r} contains UNC path {shlex.quote(str(val))}, not converting')
    elif val.anchor == '\\':
    elif val.anchor == BACKSLASH:
    anchor = winenv.get('SystemDrive', 'C:')
    warn(f'Windows environment variable {var!r} contains driveless path {shlex.quote(str(val))}, assuming {anchor!r}')
    val = PurePosixPath(f'/mnt/{anchor.rstrip("\\").removesuffix(":").lower()}') / val.relative_to(val.anchor)
    val = PurePosixPath(f'/mnt/{anchor.rstrip(BACKSLASH).removesuffix(":").lower()}') / val.relative_to(val.anchor)
    elif not val.is_absolute():
    warn(f'Windows environment variable {var!r} contains relative path {shlex.quote(str(val))}, not converting')
    else:
    val = PurePosixPath(f'/mnt/{val.anchor.rstrip("\\").removesuffix(":").lower()}') / val.relative_to(val.anchor)
    val = PurePosixPath(f'/mnt/{val.anchor.rstrip(BACKSLASH).removesuffix(":").lower()}') / val.relative_to(val.anchor)
    outvals.append(str(val))
    else:
    j[var] = ':'.join(outvals)
    @@ -81,6 +87,11 @@ def _convert_paths(winenv: Mapping, paths: Union[Container, bool] = True) -> Map
    winenv_wslpath = MappingProxyType(_convert_paths(winenv))

    else:
    # Monkey-patch reduced warning verbosity (see https://stackoverflow.com/a/2187390)
    import warnings
    _fw = warnings.formatwarning
    warnings.formatwarning = lambda *args, **kwargs: _fw(*args, **kwargs).splitlines(keepends=True)[0]

    import argparse
    p = argparse.ArgumentParser(
    description='''Capture Windows environment variables in a format that can be used under Windows Subsystem for Linux.
  8. dlenski created this gist Jun 14, 2025.
    126 changes: 126 additions & 0 deletions winenv.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,126 @@
    r'''
    Access Windows environment variables from Python under WSL
    The :py:data:`winenv` and py:data:`winenv_wslpath` objects are
    immutable mappings, lazy-loaded upon initial usage.
    py:data:`winenv` contains the unmodified Windows environment variables,
    while py:data:`winenv_wslpath` attempts to convert all ``PATH``-like
    variables to WSL paths.
    Example:
    from wslenv import winenv, winenv_wslpath
    print("Windows username is", winenv['USERNAME'])
    print("WSL-ified Windows PATH is", winenv_wslpath['PATH'])
    This requires WSL and PowerShell 3.0 installed in the standard location of
    ``C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe``.
    It grew out of https://stackoverflow.com/a/79662726
    '''

    import subprocess as sp
    import json
    from typing import Union, Container, Mapping
    from pathlib import PureWindowsPath, PurePosixPath
    import shlex
    import sys
    from warnings import warn

    __all__ = ['winenv', 'winenv_wslpath']


    def _load_winenv() -> dict:
    env2json = '[System.Environment]::GetEnvironmentVariables() | ConvertTo-Json'
    # Robustness fixes based on https://github.com/wslutilities/wslu/blob/1bb1b53c0532394a1433b2eaa041b1c1e6ce0b69/src/wslu-header#L290,
    # with the addition of ErrorActionPreference from https://stackoverflow.com/a/9949909/20789.
    cli = [
    '/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe',
    '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command',
    '$ErrorActionPreference = "Stop"; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ' + env2json
    ]
    try:
    return json.loads(sp.check_output(cli, stdin=sp.DEVNULL))
    except sp.CalledProcessError as exc:
    raise NotImplementedError("Could not get environment variables from PowerShell in JSON format; PowerShell 3.0+ is required") from exc
    except FileNotFoundError as exc:
    raise NotImplementedError(f"Could not invoke {cli[0]}; may not be running under WSL") from exc


    def _convert_paths(winenv: Mapping, paths: Union[Container, bool] = True) -> Mapping:
    if not paths:
    return winenv
    j = {}
    for var, vals in winenv.items():
    convert = vals.startswith('\\') or vals[1:2] == ':' if paths == True else var in paths
    if convert:
    winvals = map(PureWindowsPath, vals.split(';'))
    outvals = []
    for val in winvals:
    if val.anchor.startswith('\\\\'):
    warn(f'Windows environment variable {var!r} contains UNC path {shlex.quote(str(val))}, not converting')
    elif val.anchor == '\\':
    anchor = winenv.get('SystemDrive', 'C:')
    warn(f'Windows environment variable {var!r} contains driveless path {shlex.quote(str(val))}, assuming {anchor!r}')
    val = PurePosixPath(f'/mnt/{anchor.rstrip("\\").removesuffix(":").lower()}') / val.relative_to(val.anchor)
    elif not val.is_absolute():
    warn(f'Windows environment variable {var!r} contains relative path {shlex.quote(str(val))}, not converting')
    else:
    val = PurePosixPath(f'/mnt/{val.anchor.rstrip("\\").removesuffix(":").lower()}') / val.relative_to(val.anchor)
    outvals.append(str(val))
    else:
    j[var] = ':'.join(outvals)
    else:
    j[var] = vals
    return j


    if __name__ != '__main__':
    from types import MappingProxyType
    winenv = MappingProxyType(_load_winenv())
    winenv_wslpath = MappingProxyType(_convert_paths(winenv))

    else:
    import argparse
    p = argparse.ArgumentParser(
    description='''Capture Windows environment variables in a format that can be used under Windows Subsystem for Linux.
    Requires PowerShell 3.0+ (Windows 8+).
    Under bash or other POSIX Bourne shells, use: eval "$(%(prog)s)"''')
    g = p.add_argument_group('Path conversion')
    x = g.add_mutually_exclusive_group()
    x.add_argument('-w', '--wslpath', metavar='COMMA_SEPARATED_LIST', default=True,
    help="Only try to convert these specific Windows environment variables to WSL paths (default is to try to convert all)")
    x.add_argument('-W', '--no-wslpath', dest='wslpath', action='store_false',
    help='Do not try to convert any Windows environment variables containing paths to WSL paths (default is to try to convert all)')
    g = p.add_argument_group('Output format')
    x = g.add_mutually_exclusive_group()
    x.add_argument('-A', '--associative', metavar='ARRAY_NAME',
    help='Output environment variables in named associative array (Bash extension)')
    x.add_argument('-b', '--sh', '--bash', dest='format', action='store_const', const='sh', default='sh',
    help='Output environment variables as POSIX Bourne shell "export" commands (the default)')
    x.add_argument('-c', '--csh', dest='format', action='store_const', const='csh',
    help='Output environment variables as C Shell "setenv" commands')
    x.add_argument('-j', '--json', dest='format', action='store_const', const='json',
    help='Output environment variables as JSON')
    p.add_argument('-p', '--prefix', default='WIN_', help='Prefix for variable names in sh/csh output (default %(default)r)')
    args = p.parse_args()

    try:
    winenv = _load_winenv()
    except Exception as exc:
    p.error(f'Error loading Windows environment variables: {exc.args}')

    winenv = _convert_paths(winenv, paths=args.wslpath)

    if args.associative:
    print(f'declare -A {args.associative}')
    for var, val in winenv.items():
    print(f'{args.associative}[{shlex.quote(var)}]={shlex.quote(val)}')
    elif args.format == 'json':
    json.dump(winenv, sys.stdout)
    else:
    kw = 'setenv' if args.format == 'csh' else 'export'
    for var, val in winenv.items():
    if not var.isidentifier():
    warn(f'Windows environment variable {var!r} is not a valid POSIX shell identifier, commenting out.')
    print(("" if var.isidentifier() else "# ") + f'{kw} {args.prefix}{var}={shlex.quote(val)}')