Войти

Показать полную графическую версию : [решено] Изменение глобальной переменной в дочернем процессе.


Michael
09-02-2015, 11:24
Здравствуйте.
Помогите разобраться. Есть два исполняемых фала со следующим содержимым:
xxx.cmd

echo off
SET MY_ERRORLEVEL1=1
echo xxx1 MY_ERRORLEVEL1=%MY_ERRORLEVEL1%
pause
start yyy.cmd
echo xxx2 MY_ERRORLEVEL1=%MY_ERRORLEVEL1%
pause
pause

yyy.cmd

echo off
echo yyy1 MY_ERRORLEVEL1=%MY_ERRORLEVEL1%
SET MY_ERRORLEVEL1=2
echo yyy2 MY_ERRORLEVEL1=%MY_ERRORLEVEL1%
pause
exit


Пользователь запускает файл xxx.cmd. В нем определяется переменная MY_ERRORLEVEL1. Затем из этого файла следует вызов дочернего файла yyy.cmd. При выполнении видно, что значение переменной MY_ERRORLEVEL1 в процесс yyy.cmd передается и им можно воспользоваться. Затем происходит изменение переменной MY_ERRORLEVEL1 и выход из файла. При этом новое значение переменной MY_ERRORLEVEL1 в процессе xxx.cmd (в родительском) не доступно, оно осталось старым и равно 1.
Вопрос - как в дочернем процессе изменить значение переменной, определенной в родительском процессе? Причем изменить так, чтобы новое значение было доступно в родительском процессе?
Спасибо.
P.S. общая задача такова - необходимо сделать бэкап 9 папок с разных компов. При этом, для экономии времени, копирование запускается в 9 процессов. При этом, на время выполнения дочерних процессов копирования, родительский процесс встает на паузу (ping -n 500 localhost > nul). Копирование может закончится ошибкой доступа - поэтому хочу возвращать из дочернего процесса значение переменной %ERRORLEVEL%, значение которой присвою переменным MY_ERRORLEVEL1..MY_ERRORLEVEL9

Iska
09-02-2015, 18:49
При выполнении видно, что значение переменной MY_ERRORLEVEL1 в процесс yyy.cmd передается и им можно воспользоваться. »
Не так. Создаётся копия окружения родительского процесса «cmd.exe» (в котором исполняется пакетный файл «xxx.cmd»), и порождённый процесс «cmd.exe» (в котором исполняется пакетный файл «yyy.cmd») работает именно с ней.

Затем происходит изменение переменной MY_ERRORLEVEL1 и выход из файла. При этом новое значение переменной MY_ERRORLEVEL1 в процессе xxx.cmd (в родительском) не доступно, оно осталось старым и равно 1. »
Оно и не будет доступно, поскольку изменение происходит в окружении порождённого процесса «cmd.exe», являющемся копией окружения родительского процесса «cmd.exe».

Вопрос - как в дочернем процессе изменить значение переменной, определенной в родительском процессе? Причем изменить так, чтобы новое значение было доступно в родительском процессе? »
Ответ: в рамках заданной категории и выбранного механизма исполнения — никак.

P.S. общая задача такова - необходимо сделать бэкап 9 папок с разных компов. При этом, для экономии времени, копирование запускается в 9 процессов. При этом, на время выполнения дочерних процессов копирования, родительский процесс встает на паузу (ping -n 500 localhost > nul). Копирование может закончится ошибкой доступа - поэтому хочу возвращать из дочернего процесса значение переменной %ERRORLEVEL%, значение которой присвою переменным MY_ERRORLEVEL1..MY_ERRORLEVEL9 »
Задача понятна.

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

Я бы подумал о переходе на WSH («WshRemote»), либо PowerShell («WinRM»). Если никак — попробуйте, например, писать результат из дочерних пакетных файлов в единый текстовый файл, по строчке на машину. Родительский пакетный файл очищает/удаляет его, затем запускает по «start» N порождённых пакетных файлов отдельно. Каждый из них по исполнении дописывает свой результат в единый текстовый файл. Всё это время, родительский пакетный файл в бесконечном цикле ожидания раз, допустим, в пять секунд проверяет этот текстовый файл на предмет наличия в нём записей от всех порождённых дочерних файлов. Как только все записи окажутся в наличии — происходит выход из цикла ожидания, разбор этого текстового файла и вывод результатов.

Foreigner
09-02-2015, 21:45
Дочерний батник можно завернуть в for /f:

:: 1.cmd

@echo off
setlocal

set "var=1"
echo %var% 1.cmd

for /f %%i in ('2.cmd') do set "var=%%i"

echo %var% 1.cmd


:: 2.cmd

@echo off
setlocal

set /a var+=1
echo %var%

Iska
09-02-2015, 22:09
Foreigner, как это будет выглядеть со «start 2.cmd»?!

Michael
10-02-2015, 08:32
Iska, спасибо. Пока попробую остановиться на использовании временных файлов, создаваемых в дочерних процессах и анализируемых в родительском.
Foreigner, если я правильно понял ваш код, то выполнение файла 1.cmd остановится на время выполнения файла 2.cmd. Правильно? Меня такой вариант не устраивает - слишком долго придется ждать. И еще. Не могли бы вы объяснить 2 момента - зачем в начале используется :: и зачем нужны кавычки в строке set "var=1"? Спасибо.
upd: :: просто комментарий с именем файла?

Iska
10-02-2015, 09:39
зачем в начале используется :: »
Вместо команды комментария REM используется псевдометка.

и зачем нужны кавычки в строке set "var=1"? »
Дабы ограничить правую часть в присваивании. В данном случае нет разницы по сравнению с вариантом без кавычек.

upd: :: просто комментарий с именем файла? »
Фактически — да.

Foreigner
10-02-2015, 10:12
Меня такой вариант не устраивает - слишком долго придется ждать. »
Просто не вник в суть вопроса. Сделал акцент на возвращение переменной из дочернего батника. В любом случае можно запустить несколько копий родительского командного файла с разными аргументами (название компьютера, папки). Вопрос реализации.

Michael
10-02-2015, 10:42
Создаётся копия окружения родительского процесса «cmd.exe» »
Т.е. исходя из этого, в каждом вновь запущенном процессе будет своя переменная ERRORLEVEL со своим значением. Правильно?

Iska
10-02-2015, 11:33
Т.е. исходя из этого, в каждом вновь запущенном процессе будет своя переменная ERRORLEVEL со своим значением. »
Своя переменная окружения «MY_ERRORLEVEL1».

Michael
10-02-2015, 11:44
Iska, нет, меня интересует именно системная переменная ERRORLEVEL. Она будет своя в каждом процессе?
P.S. своя - в смысле независимая от других процессов.

Foreigner
10-02-2015, 11:47
будет своя переменная ERRORLEVEL со своим значением »
Если его определить принудительно, то нет:

@echo off
setlocal

set errorlevel=100
start /b 2.cmd


:: 2.cmd

@echo off
setlocal

echo %errorlevel%
help > nul

:: Errorlevel должен бы стать равным 1, но:

echo %errorlevel%
exit /b

Iska
10-02-2015, 19:08
Iska, нет, меня интересует именно системная переменная ERRORLEVEL. Она будет своя в каждом процессе?
P.S. своя - в смысле независимая от других процессов. »
Michael, как бы Вам сказать… Ваш вопрос лишён смысла в озвученном контексте. Такой системной переменной окружения нет.

Псевдопеременная же окружения ERRORLEVEL доступна только при включённой расширенной обработке команд (которая, впрочем, включена по умолчанию). Она вычисляется динамически, в момент раскрытия (т.е., на неё действуют все правила раскрытия обычных переменных окружения) и принимает значение кода ошибки (кода возврата), если тот был установлен одной из предыдущих внутренних или внешних команд. Если Вы принудительно определите её по «set» — получите обычную переменную окружения с тем же именем.

sov44
16-10-2016, 18:38
Правильно ли я понял, что переменную дочернего процесса не получить в родительском?
@echo off

cmd /v/c "set n=5& echo !n!& pause"
if defined n echo %n%

pause
exit


п.с. как вариант вижу решение через запись в текстовый файл и чтение из него, но может есть и иные варианты?

Iska
16-10-2016, 20:03
Правильно ли я понял, что переменную дочернего процесса не получить в родительском? »
Вы правильно поняли. Потому что родительский и дочерний процесс имеют каждый по своей независимой копии области переменных окружения.

п.с. как вариант вижу решение через запись в текстовый файл и чтение из него, но может есть и иные варианты? »
А что Вам нужно сделать? Интересует сама задача, а не выбранное Вами решение.

sov44
16-10-2016, 20:37
Задача простая - получить "рапорт" об исполнении задания дочернего процесса. Хотел через переменную, сделал через временный файл.
Под спойлером скрипт удаления не верных ярлыков. Вот только QL не смог победить ((


@Echo Off
SetLocal enabledelayedexpansion

(
echo Set oShell = CreateObject^("WScript.Shell"^)
echo Set oShortcut = oShell.CreateShortcut^(Wscript.Arguments.Item^(0^)^)
echo WScript.Echo oShortcut.TargetPath
)>$.vbs

set dir1=HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders
set dir2=HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders

Ver | Find "5." >Nul && call :L_XP
Ver | Find "6." >Nul && call :L_7

if exist f_lnk (echo Очистка от не верных ярлыков ^(без QL^) - OK)
if not exist f_lnk (echo Очистка от не верных ярлыков ^(без QL^) - --)
del $.vbs
del f_lnk
pause
exit

:L_XP
chcp 1251>nul
for %%a in ("%dir1%","%dir2%") do (
for /f "tokens=1-2* delims=:" %%b in ('reg query "%%~a" ^|find ":"') do (
chcp 866>nul& set r1=%%b
cmd /v /c "for %%x in ("!r1:~-1!:%%c\*.lnk") do @for /f "delims=" %%y in ('cscript //nologo "$.vbs" "%%~x"') do @if not exist "%%~y" (del /f /q "%%~x"& >f_lnk echo.1)"
))
chcp 866>nul
exit /b

:L_7
for %%a in ("%dir1%","%dir2%") do (
for /f "tokens=1-2* delims=:" %%b in ('reg query "%%~a" ^|find ":"') do (
set r1=%%b
cmd /v /c "for %%x in ("!r1:~-1!:%%c\*.lnk") do @for /f "delims=" %%y in ('cscript //nologo "$.vbs" "%%~x"') do @if not exist "%%~y" (del /f /q "%%~x"& >f_lnk echo.1)"
))
exit /b

Iska
16-10-2016, 21:21
Задача простая - получить "рапорт" об исполнении задания дочернего процесса. »
Более развёрнутый, нежели простой код возврата, так?

Вот только QL не смог победить (( »
Что такое QL?

sov44
16-10-2016, 22:14
Более развёрнутый, нежели простой код возврата, так? »
хм... интересны примеры реализации обеих вариантов. :)

Что такое QL? »
Quick Launch, панель быстрого запуска в WinXP. В Windows 7 и выше её можно добавить в панель задач. Удаление не рабочих ярлыков с панели задач и панели быстрого запуска командой del не корректно, т.к. ярлыки "привязаны" к реестру, гибрид на vbs возможно с этим мог бы помочь.

Iska
16-10-2016, 22:43
хм... интересны примеры реализации обеих вариантов. »
Первый — call в родительском, exit /b … — в дочернем, и последующий анализ errorlevel в родительском же.

Второй — например, через тот же указанный Вами вариант со временным файлом. Единственно — в родительском использовать, скажем, процедуру получения имени временного файла: CMD/BAT: генерация пути для временного файла или папки — Коллекция скриптов и идей — Серый форум (http://forum.script-coding.com/viewtopic.php?id=6259), передача этого полученного имени в дочерний через параметр, после возврата в родительский — анализ содержимого временного файла и удаление этого временного файла. Можно комбинировать с первым способом — например, можно условиться, что нулевой код возврата будет означать корректную работу дочернего пакетного файла (а разные не нулевые — ту или иную ошибку), а развёрнутые детали могут передаваться посредством временного файла.




© OSzone.net 2001-2012