[Черновик] Переменные окружения в ОС Windows и командной строке

Dragokas

Angry & Scary Developer
Команда форума
Супер-Модератор
Разработчик
Клуб переводчиков
Сообщения
7,814
Реакции
6,593
5 лет назад я начинал писать статью по этой теме, но постоянно сдвигал сроки из-за новых вводных.
В итоге, большая часть статьи готова, но не имеет логического завершения в виде практических примеров.

Недавно один человек на киберфоруме задал очень близкий к тематике вопрос
и даже предельно грамотно расписал примеры с пояснениями:

Так что в той теме я всё же решился выложить статью как есть, а здесь продублирую в развёрнутом виде.
+ будут приложены все исходники и материалы.
Возможно, кто-то когда-нибудь захочет закончить эту статью в со-авторстве, т.к. я уже давно ушел из этой темы.
Не хочется, чтобы труды пропадали и пылились на полке.
 
Последнее редактирование:
Переменные окружения в ОС Windows и командной строке

vision_title_jpg.jpg



// TODO:
описать что вообще такое переменные окружения.
Описать функции для работы с переменными, как они храняться в памяти процесса, как работать с блоком переменных.
Проверить как система передаёт процессу блок переменных – из реестра, или из родительского.
Оглавление...

Здравствуйте, уважаемые читатели !!!

Сегодня мы поговорим о различных аспектах переменных окружения с точки зрения:
  • их классификации;
  • методики расчета их значения;
  • области видимости, действия и времени жизни;
  • особенностях их раскрытия под Wow64 и в реестре;
  • принципа наследования в среде командного интерпретатора CMD.
Также мы рассмотрим, как вызывать дочерние пакетные файлы и программы:
  • без или с передачей окружения;
  • с передачей окружения, его возвратом (или не возвратом) в экземпляр родительской среды.
Получим знания о:
  • локализации переменных с помощью команд SetLocal и EndLocal;
  • перебрасывании локализованной переменной за пределы локали.

Также я дам в конце статьи все это пощупать :)

Что это такое?

Переменная окружения (далее – EV (environment variable) – это ...


Зачем нужны переменные окружения?

...

Вместо предисловия:

Ко мне пришло такое письмо:
У нас на работе есть большой сценарий, построенный на разных тулах - jscript,
perl, msbuild и еще много чего, бат-файлы там тоже есть в достаточном количестве.
Время от времени приходится там кое-что подкручивать, добавлять новые операции.
Иногда получается так: задействовал какую-то переменную в бат-файле, а потом
вдруг узнаешь, что переменная с таким именем уже была задействована где-то
"выше по стеку", т.е. например, в другом бат-файле, из которого был вызван этот.

Так вот, суть вопроса: как в бат-файлах грамотно управлять областью видимости
переменных и реально ли это вообще ? Я бы, к примеру, хотел иметь возможность в
своем бат-файле объявить переменную "counter", чтобы она "замаскировала"
другую, созданную кем-то ранее переменную с таким же именем. И если я вызываю
другой батник, чтобы она не передавалась в него. Короче, подчистить хвосты
после завершения работы своего батника. Такое вообще реально ? Если да, то как ?

Для больших проектов с перекрестными вызовами скриптов проблема конфликтов
имен переменных действительно очень актуальна.

Итак, у нас есть пакетный файл (.bat или .cmd), который вызывает:
1) другие пакетные файлы
2) jscript / аналогично vbscript
3) perl
4) msbuild

Из всего этого, только пакетные файлы не имеют своих локальных переменных в привычном понимании языка высокого уровня, а хранят их в переменных окружения процесса cmd.exe со всеми вытекающими последствиями (иными словами: переменная в CMD = переменная окружения).

[article="Переменные в других языках"]
Напомню, что ЯВУ (например, C++, C#, VB6) также как и скриптовые JScript / VBscript оперируют своими внутренними переменными, которые не влияют ни на среду выполнения*, ни на вызываемые ними другие программы.

* если этого не делать специально. А такие возможность есть во всех названных языках.
[/article]Поэтому, читая эту статью, забудьте все, что знали о философии переменных в ЯВУ таких, как:
  • глобальные
  • локальные
  • статические
  • регистровые
    и прочне…
Здесь всё работает иначе.


Виды переменных окружения в операционной системе

Существуют такие (в порядке приоритета):

1) Уровня ядра (CORE) ………. «вшиты» в файл NTDLL.dll
2) системные (SYSTEM) ............ из ключа HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment
3) пользовательские (USER)* ... из ключа HKCU\Environment
4) летучие (VOLATILE) .............. из ключа HKCU\Volatile Environment
5) процесса (PROCESS), например, в процессе cmd.exe.

* Учитывайте, что каждый пользователь имеет свои собственные пользовательские (USER) переменные,
т.к. улей реестра HKCU монтируется в момент входа пользователя в систему из подраздела HKEY_USERS\<SID пользователя>.

Чтобы изменить переменную:
- уровня SYSTEM, необходимо обладать привилегиями Администратора;
- уровня VOLATILE, достаточно обладать правами обычного пользователя;
- уровня чужого процесса, необходимо иметь права не ниже тех, которые предоставлены чужому процессу.

Примечание: в системах версий Windows 9x, переменные окружения также можно было устанавливать через файл Autoexec.bat, расположенный в корне системного диска.


Как формируется значение переменной окружения в командной строке ?

Сначала берётся системная ветка и создаются переменные. Затем пользовательские...
При этом в одноимённых переменных пользовательские значения затирают системные.
Летучие затирают пользовательские и системные.
А значения переменных, созданные процессом затирают все остальные значения переменных.

Формула замены такая: SYSTEM <= USER <= VOLATILE <= PROCESS
Результат получим в виде значения переменной в командной строке командой echo %имя переменной%

Из этого правила есть исключение для переменной PATH.
Вероятно, в виду ее важности для системы алгоритм ее расчета другой:
Для ее формирования берётся значение из HKCU, которое объединяется со значением из HKLM (Smitis, 2013).
Это легко проверить. Откройте реестр и добавьте в конец значения параметра PATH системной ветки дописку ;c:\HKLM, а пользовательской ;c:\HKCU,
затем запустите командную строку от имени администратора и введите команду set.
В вывод попадут обе части.

Кроме этого, для конкретных процессов, запущенных функцией из библиотеки Shell32.dll (ShellExecute, ShellExecuteEx - например, Win + R)
к переменной окружения PATH добавляется еще и значение из веток реестра:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\{app.exe}\
HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\{app.exe}\
из параметра под именем "Path",
где {app.exe} - алиас (псевдоним) приложения, которое было запущено,
а значение по-умолчанию для этой ветки указывает на путь к приложению.

acro.jpg


Также, подобными свойствами (в Windows 9x) для консольных приложений
обладает файл %SystemRoot%\System32\autoexec.nt.

В целом, поиск исполняемого файла, для которого не указан полный путь
происходит в таком порядке:
  1. Текущая рабочая директория.
  2. Директория системы (%SystemRoot%).
  3. Директория %SystemRoot%\System32.
  4. Директории, перечисленные в переменной окружения PATH.
  5. Если в ветке реестра App Paths есть подраздел с именем исполняемого файла, то значение по-умолчанию данной ветки используется для поиска этого файла.
В отличие от ShellExecute, функция CreateProcess ищет исполняемый файл в другом порядке:
  1. Директория, из которой запущено приложение.
  2. Текущая директория родительского процесса.
  3. Cистемная директория (%SystemRoot%\System32)
  4. 16-битная системная директория (%SystemRoot%\System)
  5. Директория Windows (%SystemRoot%)
  6. Директории, перечисленные в переменной окружения PATH. Ветка реестра App Paths не задействуется.

Принцип наследования переменных процессами

Как мы ранее говорили, есть несколько уровней переменных окружения:
SYSTEM (1), USER (2), VOLATILE (3), PROCESS (4)

Когда загружается операционная система, читаются ключи реестра, где хранятся переменные. Затем один процесс создает другой процесс, передавая переменные.
Родительский процесс может передавать дочернему любой блок переменных (Environment Block), если он подготовит его заранее, иначе дочерний процесс унаследует блок переменных родительского процесса.

1) Если Вы изменили переменную окружения (1,2,3), то также должны предупредить об этом другие уже запущенные процессы, чтобы они заново прочитали ключи реестра.
C++:
BOOL result = SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM) "Environment", SMTO_ABORTIFHUNG, 5000, &dwReturnValue);

Примечание. В отличие от API SendMessage, функция SendMessageTimeout с флагом SMTO_ABORTIFHUNG заставляет проверить, не завис ли поток-приемник, и, если да, немедленно вернуть управление вызывающему потоку. По-умолчанию, операционная система считает поток зависшим, если он прекращает обработку сообщений более чем на 5 секунд (Рихтер, 2001).

Для консоли есть специальная команда setx (Vista и выше), которая одновременно делает и изменение переменных, и оповещение об этом других процессов:
Код:
:: изменить системную переменную окружения MACHINE
SETX MACHINE "COMPAQ COMPUTER" /M
:: изменить пользовательскую переменную окружения MACHINE
SETX MACHINE "COMPAQ COMPUTER"
В этом случае процессы, создаваемые explorer.exe, будут гарантированно получать новые значения переменных.

При этом другие программы не обязаны отзываться на такое оповещение и можно только надеятся, что они подхватят новые переменные.
Например, процесс cmd.exe (как и большинство консольных приложений) не обновит свои переменные без явного перезапуска.
Правда, можно сделать трюк, создав файл env.cmd:
Код:
for /f "tokens=1* delims==" %%a in ('set') do set "%%a=%%b"
и вызвав его из текущего cmd.exe командой:
Код:
call env.cmd

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

2) Если изменилась переменная процесса (4) CMD.exe,
то она может повлиять только на процессы, запущенные из-под этого конкретного CMD.exe.

Подчеркну слово "может". Т.к. можно отключить наследование через ключи команды Start, тогда создаваемый процесс унаследует переменные родительского процесса, а не текущего.
Примечание: при запуске процесса через меню ПУСК, Выполнить (Win + R), он наследует переменные процесса explorer.exe.


Управление наследованием переменных окружения

Есть как минимум 3 способа управлять наследованием:
1) командой SetLocal и EndLocal (это локализация переменной)
2) командой Call
3) командой Start

Разберем на примерах.

1) Локализация переменной в CMD не имеет ничего общего с локальными переменными ЯВУ.

Действует это так:



Максимальная глубина вложенности SetLocal друга в друга = 31 (первая + 31 вложенных).



1) Передача своего окружения в новый пакетный файл
и возврат уже нового окружения назад в исходный (родительский) пакетный файл.


Call "Пакетный файл.cmd"
Вызовет новый пакетный файл в этой же среде.​
Поэтому переменные окружения для обеих скриптов являются общими.​
[fieldset=Level1.cmd]
Код:
[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]@echo off[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]set temp=L1[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]call Level2.cmd[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]echo Батник 1 temp (after) = %temp%[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]pause[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]
[/fieldset][fieldset=Level2.cmd]
Код:
[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]@echo off[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]echo Батник 2 temp (before) = %temp%[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]set temp=L2[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]
[/fieldset]Level2.cmd будет выполнен в том же окне и в той же среде, что и Level.cmd
затем передаст управление дальше по коду файла Level1.cmd
Результат в окне консоли:​
Код:
Батник 2 temp (before) = L1[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]Батник 1 temp (after) = L2[/INDENT][/INDENT][/INDENT][/INDENT][/INDENT][/INDENT]
[INDENT][INDENT][INDENT][INDENT][INDENT][INDENT]
Как видим батник № 2 наследовал переменную батника № 1.​
Затем наоборот батник № 1 наследовал переменную батника № 2.​


2) Передача своего окружения в новый пакетный файл
без возврата окружения обратно в родительский.


Call + SetLocal*


* Дочерний процесс будет наследовать среду родительского в зависимости от того,
как построена команда вызова нового процесса.


Например, такой командой можно вызвать пакетный файл в изолированной среде:​
[fieldset=Level1.cmd]
Код:
start "" /i cmd /c Isolated.cmd
[/fieldset]​
Новой средой для Isolated.cmd будет среда, переданная текущей cmd.exe​
перед началом выполнения в ней пакетного файла Level1.cmd
(по умолчанию, от процесса explorer.exe).​
Эта команда создает новое окно и выполняет в нем Isolated.cmd без ожидания его завершения*.​
Чтобы не создавать новое окно + ждать завершения работы Isolated.cmd,​
команда должна выглядеть так:​
Код:
start "" /i /b /wait cmd /c Isolated.cmd
/i - запуск в "изолированной" среде (авт.)​
/b - не создавать новое окно​
/w или /wait - ожидать, пока не завершится процесс​

** Среда CMD также может наследовать переменные, измененные в дочернем пакетном файле (среда останется той же).

Для этого выполним команду:​


Использована литература:
Smitis (cyberforum.ru). Заметки о переменной окружения PATH.
Lih Wern Wong. Forensic Analysis of the Windows Registry.
Рихтер Дж. Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной версии Windows, 2001.
MSDN. Application Registration.
MSDN. CreateProcess function.
MSDN. Changing Environment Variables.
MSDN. Как переносить переменных среды в систему.

Инструментарий: CMD, Sysinternals Process Monitor, редактор реестра Windows, Visual Basic 6 IDE.

_______________________________________________________________________
Во 2-й части статьи, которую я готовлю, Вы узнаете о том:
  • какие свойства имеет переменная окружения;
  • как хранятся переменные окружения в памяти процесса;
  • как создавать процессы с особыми настройками слоев совместимости;
  • ознакомитесь с правилами именования;
  • научитесь просматривать переменные чужого процесса;
  • авто-протоколирование переменных, которые изменились;
  • поиск и решение конфликтов при использовании одинаковых переменных;
_______________________________________________________________________
Спасибо. Надеюсь, Вам понравилось :)

Дополнения.

Переменные окружения в реестре Windows


Подсистема реестра способна на лету разыменовывать переменную окружения, если какая-либо программа считывает параметр.

Для этого нужно установить тип этого параметра в REG_EXPAND_SZ.

reg.png


Здесь, HKCU\Environment => Temp будет раскрыта сразу в, например:
C:\Users\Alex\AppData\Local\Temp

Примечание: консольная утилита REG.exe никогда не разыменовывает значение.

Если Ваша задача – наоборот, прочитать оригинальное значение параметра, имеющего тип REG_EXPAND_SZ, вы можете передать флаг RRF_NOEXPAND в функцию RegGetValue (поддерживается, начиная с Windows XP x64).

%Temp% и REG_EXPAND_SZ

Я встречал случаи, когда сторонняя программа перезаписала тип данных, изменив REG_EXPAND_SZ на REG_SZ. Это нарушило работу целого ряда программ. Вот пример.
Если вы столкнётесь с ошибками отсутствия доступа на записи временных файлов, проверьте тип параметра:
Не допускайте подобных ошибок в своей програмне. Если Вы пишите в параметр, который заранее существует, проверьте его тип. Если он - REG_EXPAND_SZ, не меняйте его без надобности.

Переменные окружения в среде Wow64

Следует быть осторожным при раскрытии переменных под Wow64 (когда вы работаете в 32-битном процессе на 64-битной ОС).

При раскрытии некоторых переменных будет ощущаться разница:

var.png


Вы можете оказаться в затруднительном положении, если к примеру, прочитаете из реестра путь к файлу: “%ProgramFiles%\file.txt” и затем попытаетесь его открыть из-под Wow64, не учтя поправку. Полученный путь «C:\Program Files (x86)\file.txt» окажется неверным.

Учтите также, что папка Program Files не подпадает под действие механизма File System Redirector.

Другие черновые материалы
Набор стандартных переменных окружения процесса

Category:Hidden variables - Environment Variables
Windows Environment Variables | Windows CMD | SS64.com
https://support.microsoft.com/en-us/kb/100843
https://support.microsoft.com/en-us/kb/286705
Не понимаю, зачем нужны переменные среды и командная строка - C++ и WinAPI - CyberForum.ru
[Undocumented] Variables __CD__ & __APPDIR__ - DosTips.com
Batch files - The SET command: Windows NT 4..Windows 7 Syntax
Why can't I access a variable named __CD__ on Windows 7?
Windows Environment Variables
[Undocumented] Input redirection check DPATH (Page 1) / Windows CMD Shell / SS64 Forum
Windows Environment Variables | Windows CMD | SS64.com


//TODO:
Исследовать проблему с незапуском
X64 Debuggers And Tools-10.0.10240.9_en-us
из-под Total Commander Kit-PPP
и оценить влияние на это фактора стороннего блока переменных окружения, переданного от процесса Total Commander-a.
(при успехе - упомянуть об этом в конце статьи)

PATH variable different when cmd was run through the context menu

Упомянуть про "переадресацию" переменных на х64 ОС для х32-битных процессов:
Как узнать объект ярлыка в 64-битной ОС - Страница 2 - Visual Basic - CyberForum.ru
File System Redirector (Windows)
(учесть также что для функций shell другой механизм. Они оперируют PIDL)
Упомянуть про раскрытие переменных в реестре, REG_EXPAND_SZ, а так то что, есть флаг для API который может из нее вытянуть путь без раскрытия переменной.

src
Код (C++):
if (g_fRunningOnNT)
{
// NT has a control panel applet that allows users to change the
// environment with-which to spawn new applications. On NT, we need
// to pick up that environment change so that anything we spawn in
// the future will pick up those updated environment values.
//
if (lParam && (lstrcmpi((LPTSTR)lParam, TEXT("Environment")) == 0))
{
void *pv;
RegenerateUserEnvironment(&pv, TRUE);
}
}
SystemRoot vs WinDir

Пример простейшего драйвера.
Как подгрузить драйвер.

RtlQueryEnvironmentVariable_U

Newly created with $env: environment variables in PowerShell are of Process type. For variable to display in the Advanced System Settings it has be the Machine or User level variable. To create variable with specific level use Environment.SetEnvironmentVariable method:
[Environment]::SetEnvironmentVariable('MyPath', 'c:\mypath', 'User')

[Environment]::SetEnvironmentVariable('MyPath', 'c:\mypath', 'Machine')

Что-то про алфавитный порядок:
Reuse of environment variables in Path

Двойное раскрытие переменных в реестре.

Net. Working with Env.variabels: https://msdn.microsoft.com/en-us/library/system.environmentvariabletarget.aspx

Проверить раскрывается ли %ProgramFiles% в Path: 1. Динамически, 2. После перезагрузки.
1. Для User 2. для System.

Разница между TMP и TEMP

Предупредить, что хранить один REG_EXPAND_SZ внутри другого – плохо.


setx VAR1 "CONTENT OF VAR1"
Later I've found those limitations:
1. If the %PATH% is too long then I get following warning and the %PATH% variable is truncated:
WARNING: The data being saved is truncated to 1024 characters.
2. As the help for setx command says:
When you use Setx.exe to clear an environment variable value, the environment variable name is not affected
In other words when I run setx -m OCVLIBDIR "" then the OCVLIBDIR will not be deleted but rather empty.


Как удалить прееменную окружения.

О слоях совместимости

Как получить * на PEB

Какие есть фнукции для работы с EV

Сказать что %EV% можно указывать в разных окнах shell32

https://technet.microsoft.com/en-us/library/cc951699.aspx

https://msdn.microsoft.com/en-us/library/windows/desktop/aa373347(v=vs.85).aspx - см. в комментариях

gentilkiwi/mimikatz
http://www.nosuchcon.org/talks/2014/D3_05_Alex_ionescu_Breaking_protected_processes.pdf

Добавить про CMDEXTVERSION (т.е. внутренние переменные CMD)

Семейство Windows NT[править | править вики-текст]
В операционных системах семейства Windows NT AUTOEXEC.BAT обрабатывается при входе пользователя в систему, и, как и в Windows ME, в нём игнорируются все команды, кроме команд установки переменных окружения (PATH, PROMPT и SET).[17] После обработки переменные из AUTOEXEC.BAT добавляются к переменным, заданным в реестре (в том числе, содержимое переменной PATH дописывается к содержимому, сформированному Windows). Обработку AUTOEXEC.BAT можно отменить, установив в 0 значение ключа реестра HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\ParseAutoexec.[18]


Другие автостартующие пакетные файлы системы[править | править вики-текст]
Файл autoexec.nt[править | править вики-текст]
В операционных системах семейства Windows NT файл AUTOEXEC.BAT используется только для чтения переменных окружения. При старте DOS-сессий (для запуска в режиме эмуляции приложений, написанных для DOS) вместо него исполняется файл autoexec.nt, расположенный в %systemroot%\System32. Синтаксис этого файла похож на синтаксис AUTOEXEC.BAT, но исполняется он без вывода сообщений о программах и командах на консоль (если только в файле config.nt не дана команда echoconfig[20]). Помимо этого, в свойствах ярлыка (pif-файла) для DOS-приложения можно задать собственные файлы config.nt и autoexec.nt (англ. Custom MS-DOS initialization files).

 

Вложения

  • Переменные окружения.zip
    125.6 KB · Просмотры: 0
Последнее редактирование:
Выкладываю черновой вариант 2 части статьи, который пылился на полке и затерялся.

==================================================================================

Разрешение конфликтов при совпадении имен переменных в командной строке​


Итак, прежде чем пользоваться переменной давайте посмотрим,
какими свойствами она обладает.


Свойства переменных в командной строке
  • не объявляются;
  • заранее инициализировать значением не обязательно(*1);
  • пустого значения не бывает(*2);
  • регистр символов имени переменной не имеет значения;
  • для именования переменной можно использовать некоторые спецсимволы, цифры и даже знак пробела (например: set a =1& echo %a % - будьте внимательны !!!);
  • для каждой конкретной переменной область ее видимости = области действия = времени жизни(*3);
  • имеют 1 тип данных(*4) - символьная строка длиной, соответствующей максимальной длине значения параметра реестра типа REG_SZ(*5).
(*1) Тем не менее, для уверенности и надежности рекомендуется всегда инициализировать перед использованием.
По-умолчанию, переменные инициализируются такими значениями:
  • при использовании в качестве строки - пустым значением;
  • при использовании в математических операциях - нулем или пустым значением (в зависимости от вида операции). При этом, пустое значение вызовет ошибку, если записать выражение в виде set /a a=%b% + %c%. Этого можно избечь, записав выражение в виде: set /a a=b + c. Здесь переменные b и c раскроются автоматически. Такое раскрытие будет отложенным (как !b! + !c!), т.е. актуализация значения произойдет под циклами или в однострочных командах сразу: set a=1& set /a b=a+1. Результат = 2.
(*2) Присвоение пустого значения считается освобождением переменной: Set a=
Однако, Вы можете создать такую переменную через реестр. Тем не менее, команда If Defined не укажет на существование этой переменной.

(*3) Иначе: говорить о данных понятиях вообще некорректно. Т.к. эти области зависят целиком от ветвления кода и от директив SetLocal / Endocal, которые можно выполнить в любом месте кода (см. далее).

(*4) Остальные ограничения, например, для математики - это на совести соответствующих операторов, умеющих работать только с типом signed long int, а для строк - максимальная длина составит от 8180 (гарантированно) до 8185 символов (по-разному для каждого оператора - также сугубо их ограничение).

(*5) По словам MS - это вся доступная память. По факту - 524285 символов, иначе получим ERROR_NO_SYSTEM_RESOURCES (Error # 1450 (0x5AA)


Правила именования

Чтобы не получать конфликтов при вызове других пакетных файлов:
  • Старайтесь инициализировать переменную перед использованием и освобождать, когда в ней больше нет необходимости.
  • Пользуйтесь локализацией SetLocal. Переменные в области локализации автоматически очищаются, как только завершается пакетный файл.
  • Пользуйтесь вложенной локализацией для псевдо-массивов. Если массив состоит из множества элементов, Вы можете легко его очистить командой EndLocal, заодно выиграв в скорости работы CMD.
  • Грамотно именуйте переменные. Важным переменным давайте длинные, но понятные имена.
  • Не забывайте о кавычках !!! Распечатывайте переменные, значение которых может содержать спецсимволы, только в кавычках: echo "%variable%", а еще безопаснее - через отложенное раскрытие SetLocal EnableDelayedExpansion& echo !variable! или cmd /v:ON /c echo !variable! или как вариант без отложенного раскрытия - с помощью вот такого цикла: for /f "delims=" %%a in ('call echo %%variable%%') do echo %%a. Присвоение делайте только в кавычках: set "var=%var2%" или set var="%var2%" (прим. - результат будет разным).
  • Если есть сомнение, что переменная уже используется в этом же файле, пройдитесь поиском по нему %переменная% и !переменная!. Обратите внимание: в математических операциях можно раскрывать переменную без использования знаков "%" и "!".
  • не передавайте вызываемым пакетным файлам и программам все свое окружение (см. 1-ю часть статьи). Используйте другие средства для обмена данными, предпочтительно - аргументы, или как вариант - файлы / реестр, если это действительно необходимо.

Виды конфликтов при вызове других программ

1) изменена переменная процесса
, которую наследует вызываемая программа.
Все переменные в CMD являются переменными процесса.
Мы меняем, к примеру, переменную GPU
и вызываем майнер биткоинов (есть такая злобная штука :)) :
Код:
set GPU=1
cgminer -user ME -pass GOOD e.t.c.
Он умеет читать такие переменные и использует для своей настройки.
А ведь выше по "стеку вызовов" можно случайно поменять другую важную для него переменную.
Для отладки подобных случаев Вы можете распечатать командой set& pause в любой части пакетного файла все переменные, чтобы проанализировать их.

2) изменена переменная окружения.
Управлять системными или пользовательскими можно командой setx (поддерживается начиная с Windows Vista).

3) изменена переменная процесса PATH
Результат: не сможет запустится приложение, для которого не задан полный путь.
Еще как вариант: 2 программы с одинаковым именем могут находится по расположениям PATH и система вызовет не ту из них, которая Вам нужна.

4) изменен рабочий каталог
Результат тот же. Разработчик должен учесть, что текущая папка может быть задана где угодно, а не в папке со скриптом или вызванной программы.
Внутри своих скриптов всегда в самом начале вызывайте команду cd /d "%~dp0"
Произойдет переход в папку с исполняемым скриптом.

// TODO: а для родительского тогда ведь тоже изменится каталог?


Средства автоматического поиска использованных переменных









Доступ к переменным окружения из jscript / vbscript


Давайте теперь посмотрим, как из других языков можно получить доступ к переменным процесса.

В отличие от CMD, скрипты JS / VBS позволяют разграничить доступ к каждому из видов переменных (PROCESS, SYSTEM, USER, VOLATILE)
[fieldset="env_temp.js"]
JavaScript:
var oShell = new ActiveXObject( 'WScript.Shell' )

// массив
arrEnvTypes = [ "PROCESS", "SYSTEM", "USER", "VOLATILE" ];

// Получаем значение переменной TEMP для каждого типа переменных окружения
for (var i = 0; i < arrEnvTypes.length; i++) {
    // <--- здесь получаем весь блок переменных соответствующего уровня ("PROCESS", "SYSTEM", "USER" или "VOLATILE").
    var oEnvType = oShell.Environment( arrEnvTypes[i] );
    // получаем из всего блока только переменную TEMP
    var EnvData = oEnvType.item("TEMP");
    // вывод на экран с выравниванием
    WScript.Echo (new String (arrEnvTypes[i] + '    ').substr(0,13) + EnvData);
}

// Переменная TEMP суммарно "SYSTEM" + "USER" + "VOLATILE" + "PROCESS" (тоже самое увидим из командной строки)
WScript.Echo (oShell.ExpandEnvironmentStrings( "TEMP=%TEMP%" ));
[/fieldset]Код проще всего запускать из командной строки:
Код:
cscript //nologo env_temp.js


Изменение переменных системного и пользовательского окружения


_______________________________________________________________
Post Scriptum. В процессе написания статьи ни одна кошка не пострадала
мой CMD перестал раскрывать переменные, которые вносились в реестр
после момента экспериментов с максимальной длиной значения параметра REG_SZ.
Спасло только удаление этого длинного ключа с обязательной перезагрузкой системы.
Ну..., еще упал Windows Scripting Host в виде Application Crash, но это уже так, по мелочи :Biggrin:
Вывод: хранить огромные массивы данных в пользовательских и системных переменных окружения опасно.

Использована литература:
MSDN. Сведения о реестре Windows для опытных пользователей.
 
Последнее редактирование:
Назад
Сверху Снизу