Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Скриптовые языки администрирования Windows (http://forum.oszone.net/forumdisplay.php?f=102)
-   -   [решено] Изменение глобальной переменной в дочернем процессе. (http://forum.oszone.net/showthread.php?t=295220)

Michael 09-02-2015 11:24 2468786

Изменение глобальной переменной в дочернем процессе.
 
Здравствуйте.
Помогите разобраться. Есть два исполняемых фала со следующим содержимым:
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 2469124

Цитата:

Цитата Michael
При выполнении видно, что значение переменной MY_ERRORLEVEL1 в процесс yyy.cmd передается и им можно воспользоваться. »

Не так. Создаётся копия окружения родительского процесса «cmd.exe» (в котором исполняется пакетный файл «xxx.cmd»), и порождённый процесс «cmd.exe» (в котором исполняется пакетный файл «yyy.cmd») работает именно с ней.

Цитата:

Цитата Michael
Затем происходит изменение переменной MY_ERRORLEVEL1 и выход из файла. При этом новое значение переменной MY_ERRORLEVEL1 в процессе xxx.cmd (в родительском) не доступно, оно осталось старым и равно 1. »

Оно и не будет доступно, поскольку изменение происходит в окружении порождённого процесса «cmd.exe», являющемся копией окружения родительского процесса «cmd.exe».

Цитата:

Цитата Michael
Вопрос - как в дочернем процессе изменить значение переменной, определенной в родительском процессе? Причем изменить так, чтобы новое значение было доступно в родительском процессе? »

Ответ: в рамках заданной категории и выбранного механизма исполнения — никак.

Цитата:

Цитата Michael
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 2469219

Дочерний батник можно завернуть в 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 2469233

Foreigner, как это будет выглядеть со «start 2.cmd»?!

Michael 10-02-2015 08:32 2469350

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

Iska 10-02-2015 09:39 2469364

Цитата:

Цитата Michael
зачем в начале используется :: »

Вместо команды комментария REM используется псевдометка.

Цитата:

Цитата Michael
и зачем нужны кавычки в строке set "var=1"? »

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

Цитата:

Цитата Michael
upd: :: просто комментарий с именем файла? »

Фактически — да.

Foreigner 10-02-2015 10:12 2469378

Цитата:

Цитата Michael
Меня такой вариант не устраивает - слишком долго придется ждать. »

Просто не вник в суть вопроса. Сделал акцент на возвращение переменной из дочернего батника. В любом случае можно запустить несколько копий родительского командного файла с разными аргументами (название компьютера, папки). Вопрос реализации.

Michael 10-02-2015 10:42 2469389

Цитата:

Цитата Iska
Создаётся копия окружения родительского процесса «cmd.exe» »

Т.е. исходя из этого, в каждом вновь запущенном процессе будет своя переменная ERRORLEVEL со своим значением. Правильно?

Iska 10-02-2015 11:33 2469410

Цитата:

Цитата Michael
Т.е. исходя из этого, в каждом вновь запущенном процессе будет своя переменная ERRORLEVEL со своим значением. »

Своя переменная окружения «MY_ERRORLEVEL1».

Michael 10-02-2015 11:44 2469422

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

Foreigner 10-02-2015 11:47 2469424

Цитата:

Цитата Michael
будет своя переменная 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 2469610

Цитата:

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

Michael, как бы Вам сказать… Ваш вопрос лишён смысла в озвученном контексте. Такой системной переменной окружения нет.

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

sov44 16-10-2016 18:38 2678696

Правильно ли я понял, что переменную дочернего процесса не получить в родительском?
Код:

@echo off

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

pause
exit

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

Iska 16-10-2016 20:03 2678725

Цитата:

Цитата sov44
Правильно ли я понял, что переменную дочернего процесса не получить в родительском? »

Вы правильно поняли. Потому что родительский и дочерний процесс имеют каждый по своей независимой копии области переменных окружения.

Цитата:

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

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

sov44 16-10-2016 20:37 2678743

Задача простая - получить "рапорт" об исполнении задания дочернего процесса. Хотел через переменную, сделал через временный файл.
Под спойлером скрипт удаления не верных ярлыков. Вот только 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 2678753

Цитата:

Цитата sov44
Задача простая - получить "рапорт" об исполнении задания дочернего процесса. »

Более развёрнутый, нежели простой код возврата, так?

Цитата:

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

Что такое QL?

sov44 16-10-2016 22:14 2678771

Цитата:

Цитата Iska
Более развёрнутый, нежели простой код возврата, так? »

хм... интересны примеры реализации обеих вариантов. :)

Цитата:

Цитата Iska
Что такое QL? »

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

Iska 16-10-2016 22:43 2678780

Цитата:

Цитата sov44
хм... интересны примеры реализации обеих вариантов. »

Первый — call в родительском, exit /b … — в дочернем, и последующий анализ errorlevel в родительском же.

Второй — например, через тот же указанный Вами вариант со временным файлом. Единственно — в родительском использовать, скажем, процедуру получения имени временного файла: CMD/BAT: генерация пути для временного файла или папки — Коллекция скриптов и идей — Серый форум, передача этого полученного имени в дочерний через параметр, после возврата в родительский — анализ содержимого временного файла и удаление этого временного файла. Можно комбинировать с первым способом — например, можно условиться, что нулевой код возврата будет означать корректную работу дочернего пакетного файла (а разные не нулевые — ту или иную ошибку), а развёрнутые детали могут передаваться посредством временного файла.


Время: 10:40.

Время: 10:40.
© OSzone.net 2001-