Скопировать среду дочернего процесса с помощью сценариев Windows?

Я пытаюсь создать простой сценарий входа в систему Windows, который запускает vcvarsall.bat пакетный файл для настройки моей среды Visual C++. (Под управлением Windows 7 Домашняя расширенная).

При запуске в экземпляре cmd.exe vcvarsall.bat настраивает среду для этого экземпляра cmd.exe. Тем не менее, я хочу, чтобы эти переменные среды были установлены на протяжении всего моего текущего пользовательского сеанса.

Что я хотел бы сделать, так это запустить vcvarsall.bat в качестве сценария входа в систему. Но очевидно, что это само по себе не сохранит переменные среды на протяжении всего сеанса пользователя Windows. Поэтому я бы очень хотел запустить vcvarsall.bat как дочерний процесс и, когда он завершится, скопировать любые переменные среды из дочернего процесса, которые отличаются от текущих переменных среды USER.

Есть ли способ получить доступ к среде дочернего процесса с помощью сценариев Windows?

(Используя WScript.Exec(), WScript.Run(), WScript.CreateObject("WSHController").CreateScript().Execute() или любой другой метод???)


person 0xbe5077ed    schedule 31.03.2014    source источник


Ответы (2)


Вот решение, использующее комбинацию команды set и WSH/JavaScript. Идея состоит в том, чтобы процесс, выполняющий vcvarsall.bat, выводил свою среду на стандартный вывод (h/t @arx здесь), а затем скопируйте переменные среды в "изменчивую" среду, т.е. среду, которая существует до тех пор, пока пользователь не перезагрузится.

var shell = WScript.CreateObject("WScript.Shell")
var session_env = shell.Environment("Volatile")

// Run 'vcvarsall.bat' and then print its environment variables to standard out
// as described here: https://stackoverflow.com/a/21223991/1911388.
var vcvarsall_bat = "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat"
var script = shell.Exec("cmd /C \"" + vcvarsall_bat + "\" && set")
var stdout = script.StdOut

// Read the environment variables from the script's standard output with method
// described here: https://groups.google.com/d/msg/microsoft.public.scripting.wsh/cwg0QudGp4Y/-wunGEk6sw0J.
// For each line that is a valid environment variable assignment of value X, set
// the variable in the user environment to X if-and-only-if it isn't already set
// to X.
do
{
    WScript.Sleep(10)
    while (! stdout.AtEndOfStream)
    {
        var line = stdout.ReadLine()
        var match = /^([^=]+)=(.*)$/.exec(line)
        if (null != match)
        {
            var varname = match[1]
            var varval  = match[2]
            var prevval = session_env(varname)
            if (prevval.toLowerCase() != varval.toLowerCase())
            {
                session_env(varname) = varval
            }
        }
    }
}
while (0 == script.Status && ! stdout.AtEndOfStream)

Установив приведенное выше в качестве моего сценария входа в систему, как описано здесь, я могу запустить cl.exe или любой другой инструмент VC, который мне нужен, из любой оболочки или Makefile. непосредственно при входе в Windows.

person 0xbe5077ed    schedule 31.03.2014
comment
Проблема с вашим методом заключается в том, что он добавляет к типу Volatile все переменные, которых раньше не было, то есть действительно все переменные. То есть обычные переменные, такие как OS, PATH или TEMP, не относятся к типу Volatile, верно? Смотрите редактирование в моем ответе выше... - person Aacini; 31.03.2014
comment
@ Аачини, почему это проблема? Немного избыточности меня не беспокоит, и в некоторых случаях, таких как PATH, vcvarsall.bat изменяет его, поэтому я хочу, чтобы он был помещен в изменчивую среду... - person 0xbe5077ed; 31.03.2014
comment
В таком случае вы можете получить гораздо более простое решение с помощью команды setx следующим образом: for /F "tokens=1* delims==" %%a in ('set') do setx %%a "%%b" которые определяют переменные в HKEY_CURRENT_USER... - person Aacini; 31.03.2014
comment
@Aacini, сохраняются ли переменные setx между сеансами? (т. е. если я запускаю setx, затем выхожу из системы, а затем запускаю Изменить переменные среды для вашей учетной записи, они сохраняются?) - person 0xbe5077ed; 31.03.2014
comment
@Аачини. Прохладный. Это то, чего я также надеялся избежать (я не хочу, чтобы мои переменные среды редактирования для диалогового окна вашей учетной записи были загрязнены чем-либо, кроме значений, которые я намеренно установил сам). - person 0xbe5077ed; 31.03.2014

Невозможно скопировать дочернюю среду в родительскую. Однако вы можете использовать объект WshShell.Environment для создания постоянных переменных среды способами, выходящими за рамки возможностей команды setx.

Некоторое время назад я написал гибридный скрипт Batch-JScript под названием SetEnv.bat:

@if (@CodeSection == @Batch) @then


:: SetEnv.bat: Creation of persistent environment variables via a JScript subroutine
:: Antonio Perez Ayala

@echo off
setlocal EnableDelayedExpansion

if "%~1" neq "" if "%~1" neq "/?" goto begin
set start=
for /F "delims=:" %%a in ('findstr /N "^</*usage>" "%~F0"') do (
   if not defined start (set start=%%a) else set /A lines=%%a-start-1
)
set line=0
for /F "skip=%start% tokens=1* delims=:" %%a in ('findstr /N "^" "%~F0"') do (
   echo(%%b
   set /A line+=1
   if !line! equ %lines% goto :EOF
)

<usage>
Create persistent environment variables.

SETENV charVar=code strVar:=string ... [/T:envtype]

  charVar    Specifies the name of one-character environment-variable.
  code       Ascii (Unicode) code of the character assigned to charVar;
             any code is valid, excepting zero.

  strVar     Specifies the name of string environment-variable.
  string     String of characters assigned to strVar;
             if contain spaces or special characters, enclose it in quotes.

  /T         Specify the environment type for the variables.
  envtype     SYSTEM    Applies to all users of the computer and is saved
                        between logoffs and restarts in the registry.
              USER      Applies to the user currently logged on to the
                        computer and is saved between logoffs and restarts.
              VOLATILE  Applies to current logon session and is not saved
                        between logoffs and restarts (this is the default).
              PROCESS   Applies to current process and might be passed to
                        child processes.

Examples:

    [call] SetEnv BEL=7 BS=8 TAB=9 CR=13 LF=10
    [call] SetEnv LastLogoff:="%date% @ %time%" /T:USER
</usage>

:begin

rem Define the variables via JScript
Cscript /nologo /E:JScript "%~F0" %*
goto :EOF


@end


// JScript section: SetEnv subprogram

// Define environment variables given in command-line arguments with this format:
//     charVar=code strVar:="string" ... [/T:SYSTEM|USER|VOLATILE|PROCESS]
// Antonio Perez Ayala

// Reference: http://technet.microsoft.com/en-us/library/ee156595.aspx

var envType = WScript.Arguments.Named.Item("T");
if ( envType == undefined ) envType = "VOLATILE";
var WshShell = WScript.CreateObject("WScript.Shell"),
    colEnvVars = WshShell.Environment(envType),
    args = WScript.Arguments.Unnamed, name_Value, equalSignPos;
for ( var i = 0; i < args.Length; i++ ) {
   name_Value = args.Item(i);
   equalSignPos = name_Value.indexOf("=");
   if ( name_Value.charAt(equalSignPos-1) == ":" )
      colEnvVars(name_Value.slice(0,equalSignPos-1)) = name_Value.slice(equalSignPos+1);
   else {
      colEnvVars(name_Value.slice(0,equalSignPos)) = String.fromCharCode(parseInt(name_Value.slice(equalSignPos+1)));
   }
}

Таким образом, вам просто нужно изменить vcvarsall.bat и заменить set varname=value команды эквивалентными call SetEnv varname:="value"; просто обратите внимание на синтаксис SetEnv, который требует наличия двоеточия в строковых переменных.

EDIT: Новый метод в качестве ответа на комментарий

@if (@CodeSection == @Batch) @then


@echo off
setlocal EnableDelayedExpansion

rem Get the list of current environment variables
set "v="
for /F "delims==" %%a in ('set') do set v[%%a]=def

rem Call vcvarsall.bat to create new variables
call "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/vcvarsall.bat"

rem Create a file with just the newly created variables and values
(for /F "tokens=1* delims==" %%a in ('set') do (
   set "v=%%a"
   if "!v:~0,2!" neq "v[" if not defined v[%%a] echo %%a=%%b
)) > vcvars.txt

rem Call JScript code to define persistent variables
Cscript /nologo /E:JScript "%~F0" < vcvars.txt
del vcvars.txt
goto :EOF


@end


var WshShell = WScript.CreateObject("WScript.Shell"),
    colEnvVars = WshShell.Environment("Volatile");
while ( ! WScript.Stdin.AtEndOfStream ) {
   var line = WScript.Stdin.ReadLine();
   var pos = line.indexOf("=");
   colEnvVars(line.slice(0,pos)) = line.slice(pos+1);
}
person Aacini    schedule 31.03.2014
comment
Спасибо @Aacini. Проблема с этим решением заключается в том, что я не хочу изменять vcvarsall.bat, так как я хочу рассматривать его как черный ящик от Microsoft. Для новой версии ВК я просто хочу изменить путь к скрипту в одном месте. Смотрите мой опубликованный ответ, чтобы понять, что я нашел полезным. - person 0xbe5077ed; 31.03.2014