Jak trwale zaktualizować zmienną PATH z wiersza poleceń systemu Windows?

122

Jeśli wykonuję set PATH=%PATH%;C:\\Something\\binz wiersza poleceń ( cmd.exe), a następnie wykonuję echo %PATH%, widzę ten ciąg dodany do PATH. Jeśli zamknę i otworzę wiersz poleceń, tego nowego ciągu nie ma w PATH.

Jak mogę trwale zaktualizować PATH z wiersza poleceń dla wszystkich procesów w przyszłości, a nie tylko dla bieżącego procesu?

Nie chcę tego robić, przechodząc do Właściwości systemu → Zaawansowane → Zmienne środowiskowe i aktualizując ścieżkę w tym miejscu.

To polecenie musi być wykonane z aplikacji Java (zobacz moje inne pytanie ).

vale4674
źródło
5
Korzystając z PowerShell, jest to dość proste stackoverflow.com/questions/714877/… . Używając cmd, nie jestem pewien. Może być konieczne zmodyfikowanie rejestru lub pobranie zestawu .net.
Austen Holmes
1
Jak powiedziałem, muszę to zrobić z poziomu aplikacji java. Pomyślałem tylko o wykonaniu polecenia cmd useng java'sRuntime.getRuntime().exec("my command");
vale4674

Odpowiedzi:

43

Dokumentację, jak to zrobić, można znaleźć w witrynie MSDN . Kluczowy fragment jest następujący:

Aby programowo dodać lub zmodyfikować zmienne środowiskowe systemu, dodaj je do klucza rejestru HKEY_LOCAL_MACHINE \ System \ CurrentControlSet \ Control \ Session Manager \ Environment , a następnie rozgłaszaj WM_SETTINGCHANGEkomunikat z lParam ustawionym na ciąg „Environment”. Dzięki temu aplikacje, takie jak powłoka, mogą pobierać aktualizacje.

Pamiętaj, że Twoja aplikacja będzie potrzebować podwyższonych uprawnień administratora, aby móc modyfikować ten klucz.

W komentarzach wskazujesz, że z przyjemnością zmodyfikowałbyś tylko środowisko dla każdego użytkownika. Zrób to, edytując wartości w HKEY_CURRENT_USER \ Environment . Tak jak poprzednio, upewnij się, że nadajesz WM_SETTINGCHANGEwiadomość.

Powinieneś być w stanie zrobić to z łatwością z poziomu aplikacji Java, używając klas rejestru JNI.

David Heffernan
źródło
1
Tak, używając klas rejestru JNI. Większy problem polega na tym, że Twoja aplikacja prawdopodobnie nie działa z podwyższonym poziomem uprawnień. Czy wiesz, jak to zrobić? Jeśli chcesz, aby tylko niewielka część Twojej aplikacji działała z podwyższonym poziomem uprawnień (tj. Tylko po to, aby dokonać tej zmiany), najprostszym rozwiązaniem jest bardzo prosta aplikacja C ++ do wykonania zadania, oznaczona manifestem aplikacji, a następnie wykonana jako oddzielny proces, który wywołuje okno dialogowe UAC.
David Heffernan
1
Możesz także edytować, HKEY_CURRENT_USER\Environmentaby uniknąć wymogu podniesienia.
kichik
@David Heffernan Tak, tylko to musi działać na podwyższonym poziomie. Więc twoja sugestia jest taka, aby napisać aplikację w C ++ i uruchomić ją z mojej aplikacji java? Czy możesz podać przykładowy kod lub link, jak to zrobić?
vale4674
Tak. Tak jak powiedział David. Tylko ty nie masz elewacji. Powinienem również wspomnieć, że zmodyfikuje to środowisko tylko dla bieżącego użytkownika.
kichik
Musisz podzielić to na osobny proces, aby wymusić okno dialogowe UAC tylko podczas modyfikowania ŚCIEŻKI systemowej. Wystarczy prosta aplikacja C ++ z kilkoma odczytami i zapisami rejestru, a następnie SendMessage. Ustaw requestedExecutionLevelsię requireAdministratorw pliku manifestu aplikacji.
David Heffernan
145

Możesz użyć:

setx PATH "%PATH%;C:\\Something\\bin"

Jednak setxspowoduje obcięcie przechowywanego ciągu do 1024 bajtów, potencjalnie uszkadzając PATH.

/Mzmieni PATHw HKEY_LOCAL_MACHINEzamiast HKEY_CURRENT_USER. Innymi słowy, zmienna systemowa zamiast użytkownika. Na przykład:

SETX /M PATH "%PATH%;C:\your path with spaces"

Musisz pamiętać, że nowa PATH nie jest widoczna w Twoim obecnym cmd.exe.

Ale jeśli spojrzeć w rejestrze lub na nowy cmd.exez "set p"was może zobaczyć nową wartość.

panny
źródło
2
Czy istnieje sposób setxzmiany ścieżki komputera zamiast ścieżki użytkownika?
Corey Ogburn,
4
Od tutaj można powiedzieć, może to być możliwe, aby ustawić zmienną nie tylko dla aktualnie zalogowanego użytkownika, ale na maszynie za pomocą /mna końcu polecenia, na Windows XP i 7. Nie próbowałem go jednak.
panny
1
setxWystąpił błąd podczas wykonywania polecenia „Domyślna opcja nie może być dłuższa niż„ 2 ”czas”. Jak to ominąć?
Nam G VU
12
Komentarze @KilgoreCod: Przestrzegam przed użyciem polecenia: W wielu (większości?) Instalacjach obecnie zmienna PATH będzie długa - setx obetnie zapisany ciąg do 1024 bajtów, potencjalnie uszkadzając PATH (zobacz dyskusję tutaj superuser.com/ q / 812754 ).
beresfordt
2
Próbuję odtworzyć ścieżkę, która ma już ponad 1200 bajtów. jakikolwiek inny sposób zamiast setx?
lawphotog
37

Przestrzegam przed użyciem polecenia

setx PATH "%PATH%;C:\Something\bin"

zmodyfikować zmienną PATH ze względu na „cechę” jej implementacji. W wielu (większości?) Instalacjach w dzisiejszych czasach zmienna będzie długa - setxskróci przechowywany ciąg do 1024 bajtów, potencjalnie uszkadzając PATH (patrz dyskusja tutaj ).

( Zapisałem się specjalnie, aby oznaczyć ten problem, więc brakuje mi reputacji witryny, aby bezpośrednio komentować odpowiedź opublikowaną 2 maja 2012 r. Dziękuję beresfordt za dodanie takiego komentarza )

KilgoreCod
źródło
9

Ten skrypt Pythona [*] robi dokładnie to:

"""
Show/Modify/Append registry env-vars (ie `PATH`) and notify Windows-applications to pickup changes.

First attempts to show/modify HKEY_LOCAL_MACHINE (all users), and 
if not accessible due to admin-rights missing, fails-back 
to HKEY_CURRENT_USER.
Write and Delete operations do not proceed to user-tree if all-users succeed.

Syntax: 
    {prog}                  : Print all env-vars. 
    {prog}  VARNAME         : Print value for VARNAME. 
    {prog}  VARNAME   VALUE : Set VALUE for VARNAME. 
    {prog}  +VARNAME  VALUE : Append VALUE in VARNAME delimeted with ';' (i.e. used for `PATH`). 
    {prog}  -VARNAME        : Delete env-var value. 

Note that the current command-window will not be affected, 
changes would apply only for new command-windows.
"""

import winreg
import os, sys, win32gui, win32con

def reg_key(tree, path, varname):
    return '%s\%s:%s' % (tree, path, varname) 

def reg_entry(tree, path, varname, value):
    return '%s=%s' % (reg_key(tree, path, varname), value)

def query_value(key, varname):
    value, type_id = winreg.QueryValueEx(key, varname)
    return value

def yield_all_entries(tree, path, key):
    i = 0
    while True:
        try:
            n,v,t = winreg.EnumValue(key, i)
            yield reg_entry(tree, path, n, v)
            i += 1
        except OSError:
            break ## Expected, this is how iteration ends.

def notify_windows(action, tree, path, varname, value):
    win32gui.SendMessage(win32con.HWND_BROADCAST, win32con.WM_SETTINGCHANGE, 0, 'Environment')
    print("---%s %s" % (action, reg_entry(tree, path, varname, value)), file=sys.stderr)

def manage_registry_env_vars(varname=None, value=None):
    reg_keys = [
        ('HKEY_LOCAL_MACHINE', r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment'),
        ('HKEY_CURRENT_USER', r'Environment'),
    ]
    for (tree_name, path) in reg_keys:
        tree = eval('winreg.%s'%tree_name)
        try:
            with winreg.ConnectRegistry(None, tree) as reg:
                with winreg.OpenKey(reg, path, 0, winreg.KEY_ALL_ACCESS) as key:
                    if not varname:
                        for regent in yield_all_entries(tree_name, path, key):
                            print(regent)
                    else:
                        if not value:
                            if varname.startswith('-'):
                                varname = varname[1:]
                                value = query_value(key, varname)
                                winreg.DeleteValue(key, varname)
                                notify_windows("Deleted", tree_name, path, varname, value)
                                break  ## Don't propagate into user-tree.
                            else:
                                value = query_value(key, varname)
                                print(reg_entry(tree_name, path, varname, value))
                        else:
                            if varname.startswith('+'):
                                varname = varname[1:]
                                value = query_value(key, varname) + ';' + value
                            winreg.SetValueEx(key, varname, 0, winreg.REG_EXPAND_SZ, value)
                            notify_windows("Updated", tree_name, path, varname, value)
                            break  ## Don't propagate into user-tree.
        except PermissionError as ex:
            print("!!!Cannot access %s due to: %s" % 
                    (reg_key(tree_name, path, varname), ex), file=sys.stderr)
        except FileNotFoundError as ex:
            print("!!!Cannot find %s due to: %s" % 
                    (reg_key(tree_name, path, varname), ex), file=sys.stderr)

if __name__=='__main__':
    args = sys.argv
    argc = len(args)
    if argc > 3:
        print(__doc__.format(prog=args[0]), file=sys.stderr)
        sys.exit()

    manage_registry_env_vars(*args[1:])

Poniżej znajduje się kilka przykładów użycia, zakładając, że został on zapisany w pliku o nazwie setenv.pygdzieś w bieżącej ścieżce. Zauważ, że w tych przykładach nie miałem uprawnień administratora , więc zmiany dotyczyły tylko drzewa rejestru mojego lokalnego użytkownika:

> REM ## Print all env-vars
> setenv.py
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session   Manager\Environment:PATH due to: [WinError 5] Access is denied
HKEY_CURRENT_USER\Environment:PATH=...
...

> REM ## Query env-var:
> setenv.py PATH C:\foo
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session   Manager\Environment:PATH due to: [WinError 5] Access is denied
!!!Cannot find HKEY_CURRENT_USER\Environment:PATH due to: [WinError 2] The system cannot find the file specified

> REM ## Set env-var:
> setenv.py PATH C:\foo
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session   Manager\Environment:PATH due to: [WinError 5] Access is denied
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo

> REM ## Append env-var:
> setenv.py +PATH D:\Bar
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session   Manager\Environment:PATH due to: [WinError 5] Access is denied
---Set HKEY_CURRENT_USER\Environment:PATH=C:\foo;D:\Bar

> REM ## Delete env-var:
> setenv.py -PATH
!!!Cannot access HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session   Manager\Environment:PATH due to: [WinError 5] Access is denied
---Deleted HKEY_CURRENT_USER\Environment:PATH

[*] Na podstawie: http://code.activestate.com/recipes/416087-persistent-environment-variables-on-windows/

ankostis
źródło
4

Dla celów informacyjnych, dla każdego, kto szuka sposobu zmiany ścieżki za pomocą kodu, cytuję przydatny post napisany przez programistę Delphi z tej strony internetowej: http://www.tek-tips.com/viewthread.cfm?qid=686382

TonHu (programista) 22 października 03 17:57 Znalazłem miejsce, w którym przeczytałem oryginalny post, jest tutaj: http://news.jrsoftware.org/news/innosetup.isx/msg02129 ....

Fragment tego, czego potrzebujesz, jest następujący:

W LParam należy określić ciąg „Środowisko”. W Delphi zrobiłbyś to w ten sposób:

 SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, 0, Integer(PChar('Environment')));

Zasugerował go Jordan Russell, http://www.jrsoftware.org , autor (ao) InnoSetup („Inno Setup to darmowy instalator programów dla systemu Windows. Wprowadzony po raz pierwszy w 1997 roku, dzisiaj Inno Setup rywalizuje, a nawet przewyższa komercyjne instalatory w zestawie funkcji i stabilności. ”) (Chciałbym tylko, aby więcej osób korzystało z InnoSetup)

HTH

Steve F.
źródło
Musisz zmodyfikować rejestr. Również obsada na Integer jest kiepska. Zamiast tego przesyłaj do LPARAM w celu zapewnienia zgodności z 64-bitową.
David Heffernan
4

W sieci firmowej, w której użytkownik ma ograniczony dostęp i korzysta z aplikacji przenośnych, istnieją następujące sztuczki wiersza poleceń:

  1. Kwerendy zmienne env użytkownik: reg query "HKEY_CURRENT_USER\Environment". Użyj "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment"dla LOCAL_MACHINE.
  2. Dodaj nową zmienną env użytkownik: reg add "HKEY_CURRENT_USER\Environment" /v shared_dir /d "c:\shared" /t REG_SZ. Użyj REG_EXPAND_SZdla ścieżek zawierających inne zmienne %%.
  3. Usuń istniejącą zmienną env: reg delete "HKEY_CURRENT_USER\Environment" /v shared_dir.
razvanone
źródło
3

Ten skrypt http://www.autohotkey.com/board/topic/63210-modify-system-path-gui/

zawiera wszystkie niezbędne wywołania interfejsu API systemu Windows, które można ponownie dostosować do swoich potrzeb. W rzeczywistości jest to GUI AutoHotkey do łatwej zmiany ŚCIEŻKI systemu. Musi być uruchomiony jako administrator.

Evgeni Sergeev
źródło
Przeczytaj pytanie. Jeszcze raz.
jiggunjer,
Świetny scenariusz. Używam HotKey, ale nie wiem, jak i co muszę zrobić, aby dodać do niego skrypt. Czy możesz zaoferować pomoc, podać link lub wyjaśnić, co należy zrobić?
jwzumwalt