Любопытное поведение в sys.path при изменении томов в Windows CMD

#python #windows #cmd

#python #Windows #cmd

Вопрос:

Я только что заметил некоторое любопытное поведение sys.path и Windows CMD и хотел бы знать, что происходит и почему.

В следующих двух примерах я распечатываю sys.path , изменяю volume на D: , cd в каталог, снова изменяю volume на C: и, наконец, распечатываю sys.path снова. Я делаю это с помощью двух очень похожих PYTHONPATH файлов.

С PYTHONPATH=D: :

 C:Usersz003w3we>python -c "import sys; print(sys.path)"
['', 'D:\', <others>]

C:Usersz003w3we>D:

D:>cd UserData

D:UserData>C:

C:Usersz003w3we>python -c "import sys; print(sys.path)"
['', 'D:\', <others>]
  

Все так, как ожидалось.

С помощью PYTHONPATH=D: (обратите внимание на отсутствующий конец ):

 C:Usersz003w3we>python -c "import sys; print(sys.path)"
['', 'D:\', <others>]

C:Usersz003w3we>D:

D:>cd UserData

D:UserData>C:

C:Usersz003w3we>python -c "import sys; print(sys.path)"
['', 'D:\UserData', <others>]

C:Usersz003w3we>echo %PYTHONPATH%
D:
  

Теперь вместо D: мы внезапно имеем D:UserData in sys.path . Обратите внимание, что PYTHONPATH не изменилось.

Я не смог воспроизвести то же поведение с PowerShell; оба PYTHONPATH s демонстрируют первое поведение.

Как я уже сказал, мне было бы очень интересно узнать, что здесь происходит.

Ответ №1:

CMD устанавливает традиционно скрытую переменную среды с именем «=D:» для рабочего каталога на диске D:. Процесс Python наследует эту переменную среды, и Windows API использует ее при GetFullPathNameW вызове для разрешения относительного к диску пути «D:» как полного пути.

В CMD мы можем перечислить все переменные среды, включая «скрытые», через set «». Эта команда зависит от ошибки, но она существовала так долго, что теперь стала функцией. Чтобы перечислить только скрытые тома, используйте set "" | findstr /r "^=" . Например:

 C:>set "" | findstr /r "^="
=C:=C:

C:>cd /d E:UserData
E:UserData>c:

C:>set "" | findstr /r "^="
=C:=C:
=E:=E:UserData

C:>set PYTHONPATH=E:
C:>python -c "import sys; print(sys.path[1]); sys.exit(0xFF)"
E:UserData

C:>set "" | findstr /r "^="
=C:=C:
=E:=E:UserData
=ExitCode=000000FF
  

Комментарии:

1. «D:» — это путь, относящийся к диску (т. Е. без обратной косой черты после двоеточия), который разрешается относительно рабочего каталога на томе. Поскольку процесс NT имеет только один рабочий каталог, классическое поведение DOS, в котором каждый диск имеет рабочий каталог, эмулируется путем установки «скрытых» переменных среды. Если «=D:» не задано, по умолчанию используется корневой каталог. Вот еще один пример. Если рабочий каталог в «D:» является «D:UserData «, затем «D:spam.txt «разрешается до»D:UserDataspam.txt «.

2. Я вижу. Что PowerShell делает по-другому?

3. Я проверю, но я предполагаю, что PowerShell не устанавливает переменную «=D:». Windows API использует эти переменные с буквой диска, но никогда не устанавливает их. Их настройка зависит от отдельных приложений и библиотек. Библиотека времени выполнения C выполняет. Python также делает это, когда мы вызываем os.chdir .

4. Я могу подтвердить, что PowerShell не устанавливает эти переменные с буквой диска, которые я тестировал с помощью win32api.GetEnvironmentVariable('=C:') из Python, запускаемого из PowerShell, запускаемого из проводника. (Эти «скрытые» переменные не добавляются в os.environ отображение Python, поэтому нам приходится использовать Windows API напрямую.) PowerShell даже не устанавливает рабочий каталог своего процесса. Оно оставляет его с начальным значением, и все рабочие каталоги диска поддерживаются как состояние приложения.