1

Тема: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Столкнулся с проблемой создания уникальных значений (имен файлов) в скриптах, запускаемых в конвейерах. Простой пример:

Скрипт, назовем его z.bat.

@echo %TIME% %RANDOM%>&2

и запустим его несколько раз, например

for /l %1 in ( 1, 1, 10 ) do @( z | z )

Результат:

17:28:32,10 32319
17:28:32,11 32319
17:28:32,15 32319
17:28:32,17 32319
17:28:32,21 32319
17:28:32,22 32319
17:28:32,25 32319
17:28:32,26 32319
17:28:32,28 32319
17:28:32,29 32319
17:28:32,31 32319
17:28:32,32 32319
17:28:32,33 32319
17:28:32,34 32319
17:28:32,35 32319
17:28:32,36 32319
17:28:32,38 32319
17:28:32,38 32319
17:28:32,39 32319
17:28:32,40 32319

При конкатенации команд (z & z) подобной проблемы не возникает.

( 2 * b ) || ! ( 2 * b )

2

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata, я по тому же принципу сделал для себя такое:

@echo off
setlocal enableextensions enabledelayedexpansion

call :GetTemporaryName
echo Temporary name: [%TemporaryName%]

endlocal
exit /b 0

rem ==========================================================================
rem Функция GetTemporaryName()
rem ==========================================================================
:GetTemporaryName
    setlocal enableextensions enabledelayedexpansion

:NextName
    set sTempName=%temp%\temp%random%.tmp

    if exist "%sTempName%" goto :NextName

    set sProcName=%~0

    endlocal & set %sProcName:~4%=%sTempName%
    exit /b 0
rem ==========================================================================

Дело в том, что генерация %RANDOM% вроде как и зависит от времени. Так что, самое простое — просто проверять, нет ли уже такого файла/папки, и, если есть — генерировать другое имя.

3 (изменено: alexii, 2011-09-30 19:16:38)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Серый форум / CMD/BAT: Почему ANDOM% выдает близкие друг к другу числа?

Я конечно далек от мысли... (с)

4

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

smaharbA,
Проблема та же, только описание другое. Но и там решения нет. Проблема в том, что значения переменных %TIME% и %RANDOM% могут совпадать с разных скриптах.

( 2 * b ) || ! ( 2 * b )

5 (изменено: DnsIs, 2011-10-01 11:17:26)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Часа 3 убил на обдумку:

@GOTO Run
i 42
i 42
i 42
i 42
i 42
i 42
i 42
i 42
q

:Run
@echo off
setlocal ENABLEDELAYEDEXPANSION
for /F "eol=- skip=2" %%i in ('DEBUG ^< %~sf0') do @(call SET rnd=%%rnd%%%%i)
echo %rnd%
endlocal

Батник читает из 42h порта значение. Если запустить msinfo32.exe, то он скажет, что 0x00000040-0x00000043 - Порты системного таймера.
Вообще идея была, приплюсовать к полученному значению текущие время и дату, и посчитать с полученной строки MD5, тогда ИМХО было бы более случайное значение. Но тогда это придется раздувать код, да и как MD5 батником посчитать не знаю.

Зацените пожалуйста, прокомментируйте.

Rumata пишет:

...и запустим его несколько раз, например

for /l %1 in ( 1, 1, 10 ) do @( z | z )

В чем смысл записи do @( z | z ), почему просто не запустить do @( z  )?

Нас невозможно сбить с пути, нам пофигу куда идти.

6

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

DnsIs пишет:

В чем смысл записи do @( z | z ), почему просто не запустить do @( z  )?

Некий скрипт что-то выполняет, создавая и используя некий временный объект (например, файл), потом что-то выводит. Это что-то передается другому процессу на вход для обработки. Этот второй процесс тоже создает временный файл. Если в качестве шаблонов для имен файлов использовать %TIME% и %RANDOM%, то конвейерные процессы "захватывают" один и тот же файл. Возникает коллизия.

Я предполагаю, что ОС запускает все процессы единовременно. В этом есть смысл - все процессы запущены и готовы к приему/выдаче данных.

( 2 * b ) || ! ( 2 * b )

7

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Ради интереса запустил аналогичный скрипт на линуксе

#!/bin/sh

echo $(date +%T) $RANDOM>&2

Следующий запуск

./z | ./z

показал, что хоть время и одинаковое с точностью до секунды (!!!), но переменная $RANDOM всегда выдает разное значение:

sh-2.05b$ ./z | ./z
12:35:59 8427
12:35:59 24357
sh-2.05b$ ./z | ./z
12:36:00 27083
12:36:00 10245

( 2 * b ) || ! ( 2 * b )

8

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata, вот более-менее внятное толкование: As random as I wanna be: Why cmd.exe's %RANDOM% isn't so random - The Old New Thing - Site Home - MSDN Blogs.

9 (изменено: !k, 2011-10-01 20:53:46)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

попробуйте из батника, а не из ком.строки

for /l %%i in (1,1,30) do @(call echo %%TIME%% %%RANDOM%%>&2)
setlocal ENABLEDELAYEDEXPANSION
for /l %%i in (1,1,30) do @(echo !TIME! !RANDOM!>&2)

10

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

И так... Прошло какое-то время, я решил вернуться к этой проблеме. Я провел несколько экспериментов чтобы убедиться, что проблема не решаема средствами чистого скрипта, без привлечения сторонних средств.

Кратко суть проблемы. Есть некий пакетный файл, который создает случайный объект (число, произвольное слово или временный файл с уникальным именем). Можно разработать скрипт так, что он будет корректно генерировать случайный объект. Однако проблема возникает при использовании данного скрипта в конвейерах.

А теперь подробнее. Допустим, есть некий скрипт, который создает для своих нужд временный файл, что-то делает и выводит информацию на консоль. Данные поступают дальше по конвейеру в другой скрипт (или этот же, так как он умеет читать данные из консоли). И второй скрипт тоже для своих нужд создает временный файл по тому же самому алгоритму. Любые исследованные мной способы создать уникальный объект приводили к коллизии: два скрипта пытались создать один и тот же объект. Я нахожу этому единственное объяснение: скрипты запускаются "параллельно". Я не уверен в этом полностью, но выглядит это именно так.

Вот пример (взят отсюда и слегка изменен)

@echo off

call :GETTEMPNAME
echo %~1: "%TMPFILE%">&2
echo %~1: "%TMPFILE%">>"%TMPFILE%"

goto :EOF

:GETTEMPNAME
set TMPFILE=mytempfile-%RANDOM%-%TIME:~6,5%.tmp
if exist "%TMPFILE%" GOTO :GETTEMPNAME

:EOF

Запуская в связке

z 1 | z 2

можно получить вместо двух один файл, содержащий две строки - результат работы двух скриптов:

C:\Temp>type "mytempfile-29067-37,00.tmp"
1: "mytempfile-29067-37,00.tmp"
2: "mytempfile-29067-37,00.tmp"

Встречаются ситуации, когда второй скрипт выводит информацию раньше первого:

C:\Temp>z 1 | z 2
2: "mytempfile-29076-39,83.tmp"
1: "mytempfile-29076-39,84.tmp"

А иногда возникает коллизия при одновременном обращении к файлу:

C:\Temp>z 1 | z 2
1: "mytempfile-29076-39,98.tmp"
2: "mytempfile-29076-39,98.tmp"
The process cannot access the file because it is being used by another process.

Единственный пока (возможно есть и другие, но мне они не известны) способ получить действительно уникальный файл - использовать cscript.exe (как я и говорил - использовать внешнее средство).

@set @x=0/*!&&@set @x=
@echo off

call :GETTEMPNAME
echo %~1: "%TMPFILE%">&2
echo %~1: "%TMPFILE%">>"%TMPFILE%"

goto :EOF

:GETTEMPNAME
cscript //nologo //e:javascript "%~f0"
set TMPFILE=mytempfile-%ERRORLEVEL%-%TIME:~6,5%.tmp
if exist "%TMPFILE%" GOTO :GETTEMPNAME
goto :EOF

*/

var r = Math.floor(Math.random() * 32768);
WScript.Quit(r);

Как и почему это работает мне не понятно. Меня больше беспокоит как это безболезненно интегрировать в существующий скрипт, так как такая техника - два-в-одном - уже используется в данном скрипте.

( 2 * b ) || ! ( 2 * b )

11

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata, я не вижу здесь:

:GETTEMPNAME
set TMPFILE=mytempfile-%RANDOM%-%TIME:~6,5%.tmp
if exist "%TMPFILE%" GOTO :GETTEMPNAME

никакой проблемы. Проблемы есть в особенностях работы командного интерпретатора. В абсолютном большинстве случаев необходимости в этом нет, но, если потребно — достаточно добавить создание самого файла:

>nul copy nul "%TMPFILE%"

внутрь процедуры. И коллизий с одинаковыми именами файлов не будет.

12

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:

...Единственный пока (возможно есть и другие, но мне они не известны) способ получить действительно уникальный файл - использовать cscript.exe (как я и говорил - использовать внешнее средство)....

Неужели мой вариант совсем не в тему? Я опять написал х**нь?

Нас невозможно сбить с пути, нам пофигу куда идти.

13

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

alexii пишет:

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

Я проводил и такое исследование. Но в сообщение попал первый вариант. Согласен, что я рассматриваю редкий случай. Но все таки! Он показывает на существование проблемы, которая решается несколько криво (использование cscript.exe)

Измененный вариант:

@echo off

call :GETTEMPNAME %~1

goto :EOF

:GETTEMPNAME
set TMPFILE=mytempfile-%RANDOM%-%TIME:~6,5%.tmp
if exist "%TMPFILE%" GOTO :GETTEMPNAME
echo %~1: "%TMPFILE%">>"%TMPFILE%"
echo %~1: "%TMPFILE%">&2

:EOF

И несколько показательных примеров:

C:\Temp>z 1 | z 2
2: "mytempfile-23778-51,05.tmp"
1: "mytempfile-23778-51,05.tmp"

C:\Temp>z 1 | z 2
2: "mytempfile-23778-51,90.tmp"
The process cannot access the file because it is being used by another process.
1: "mytempfile-23778-51,90.tmp"

C:\Temp>type "mytempfile-23778-51,05.tmp"
2: "mytempfile-23778-51,05.tmp"
1: "mytempfile-23778-51,05.tmp"

C:\Temp>type "mytempfile-23778-51,90.tmp"
2: "mytempfile-23778-51,90.tmp"

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

( 2 * b ) || ! ( 2 * b )

14

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata, повторюсь: не перенаправление вывода, которое завязано и организуется самим интерпретатором команд, а именно создание файла внутри процедуры.

15

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

alexii, я понял Вас - файл создается внутри процедуры. Проблем нет с созданием файла в одиночном процессе. Проблема возникает когда команда вызывается в связке. Перенаправление вывода и создание файла - несвязанные друг с другом операции, но коллизии возникают.

( 2 * b ) || ! ( 2 * b )

16

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

DnsIs
Это кстати интересный вариант, но это тоже внешнее решение.

( 2 * b ) || ! ( 2 * b )

17

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:

Проблема возникает когда команда вызывается в связке.

Не вижу.

«0001.cmd»:

@echo off
setlocal enableextensions enabledelayedexpansion

call :GetTemporaryName
echo %~1: "%TemporaryName%">&2
echo %~1: "%TemporaryName%">>"%TemporaryName%"

endlocal
exit /b 0

rem ==========================================================================
rem Функция GetTemporaryName()
rem 
rem На основе Серый форум / CMD/BAT: генерация пути для временного файла или папки
rem (http://forum.script-coding.com/viewtopic.php?id=6259) с добавлением создания файла в процедуре
rem ==========================================================================
:GetTemporaryName
    setlocal enableextensions enabledelayedexpansion

:NextName
    set sTempName=%temp%\temp%random%.tmp

    if exist "%sTempName%" goto :NextName
    >nul copy nul "%sTempName%"
    set sProcName=%~0

    endlocal & set %sProcName:~4%=%sTempName%
    exit /b 0
rem ==========================================================================

и «0002.cmd», того же содержания.

Так же:

0001.cmd 1 | 0002.cmd 2

вызываю. Коллизий с именами временных файлов не увидел.

18

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

alexii пишет:

Не вижу.

Еще раз потестировал. Любая программа, выполненная скриптом в конвейере, может привести к коллизии. Проверить можно, запуская поток в цикле, например так:

for /l %%l ( 1, 1, 100 ) do @( z 1 | z 2 )

Надо посмотреть в сторону определения PID данного процесса.

( 2 * b ) || ! ( 2 * b )

19

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata, увидел.

20 (изменено: smaharbA, 2012-03-30 23:25:26)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

сид не отменяли

хотя бы

http://forum.script-coding.com/viewtopic.php?id=6085

Я конечно далек от мысли... (с)

21 (изменено: Rumata, 2012-03-31 00:28:43)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

smaharbA
Коллега, смею утверждать, что все это одно и тоже. Ваш скрипт, будучи запущенным дважды одновременно выдаст тот же самый результат.

Вы можете многократно запустить скрипт и убедиться в этом.

( 2 * b ) || ! ( 2 * b )

22

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Вы не до начала поняли

Я конечно далек от мысли... (с)

23 (изменено: Rumata, 2012-03-31 01:51:29)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

smaharbA пишет:

Вы не до начала поняли

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

А если серьезно, то Ваш пример ни чем не отличается от рассмотренных в этой теме. Не верите - проверьте.

( 2 * b ) || ! ( 2 * b )

24 (изменено: UNDYING, 2012-04-06 03:52:58)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:
alexii пишет:

Не вижу.

Еще раз потестировал. Любая программа, выполненная скриптом в конвейере, может привести к коллизии. Проверить можно, запуская поток в цикле, например так:

for /l %%l ( 1, 1, 100 ) do @( z 1 | z 2 )

Надо посмотреть в сторону определения PID данного процесса.

Скорее всего. Как я понимаю, вы на довольно-таки мощной машине запускаете процессы? Попробовав почти все (без "сторонних" решений) способы, которые перечислены на страничке на 8-ядерной "тачке" получил описываемую вами проблему, причём на тестовых скриптах коллизия всегда, а на виртуальной машине, запущенной на слабеньком одноядерном AthlonXP для %time% коллизий нет, всегда есть 5-10 и более миллисекунд разницы (в первом случае Windows Server 2008 R2 SP1, во втором Windows XP SP3).

Причина скорее всего в том, что cmd.exe при втрече конвейера "|" пытается запустить процессы единовременно; на одноядерной машине это физически невозможно (какая-никакая задержка, но будет, процессорные конвейеры в счёт не берём), многопроцесоррные и многоядерные машины в этом случае создают для интерпретатора такую возможность.
Интересно выглядит ситуация, если заменить содержимое z.bat на что-нибудь вроде:

@echo off
setlocal ENABLEEXTENSIONS
wmic os get LocalDateTime|Find "." >&2
endlocal & exit /b

После изучения вывода цикла возник другой вопрос: может ли WMI кэшировать запросы?

25 (изменено: Rumata, 2012-04-06 04:43:31)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

UNDYING
Я уже попытался поиграться с PID процессов - толку мало. Вы верно заметили: в конвейере процессор пытается запустить скрипты одновременно, соответственно, одновременно видны и процессы, чьи PID я пытаюсь определить. И как различить их для одноименных процессов - не ясно.

К слову сказать. Аналогичные тесты на 4-процессорной машине, работающей под linux, не дали ни одной коллизии. В то время как 2-ядерная машина под windows всегда, в каждой серии запусков, не обходилась коллизий.

UNDYING пишет:

многопроцесоррные и многоядерные машины в этом случае создают для интерпретатора такую возможность

У меня возникала такая мысль и я думал попробовать на своей машине запустить три процесса в конвейере, вроде z | z | z. Теоретически, я увижу коллизии 1 с 2, или 2 с 3, или 1 с 3. Но никогда - все одновременно.

Но ведь эта такая редкая и специфическая задача, стоит ли игра свеч?

( 2 * b ) || ! ( 2 * b )

26 (изменено: UNDYING, 2012-04-06 11:42:06)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata

Я уже попытался поиграться с PID процессов - толку мало. Вы верно заметили: в конвейере процессор пытается запустить скрипты одновременно, соответственно, одновременно видны и процессы, чьи PID я пытаюсь определить. И как различить их для одноименных процессов - не ясно.

Я решил данный момент так - передавал каждому скрипту разные аргументы в %1.
Если такой способ никоим образом не подходит - опишите поточнее задачу, каким именно способом запускаются 2 скрипта в конвейере, если их всегда 2 можно передавать "тупо" 1 и 2.

У меня возникала такая мысль и я думал попробовать на своей машине запустить три процесса в конвейере, вроде z | z | z. Теоретически, я увижу коллизии 1 с 2, или 2 с 3, или 1 с 3. Но никогда - все одновременно.

На вышеописанной мною машине (двухголовый "сервант" по 4 ядра на цп), z | z | z работает точно также (3 коллизии за раз) и даже z | z | z | z (4 коллизии) - я думаю, не выход из ситуации.

К слову сказать. Аналогичные тесты на 4-процессорной машине, работающей под linux, не дали ни одной коллизии. В то время как 2-ядерная машина под windows всегда, в каждой серии запусков, не обходилась коллизий.

Пожалуйста, не путайте "кислое с длинным" - в том же bash можно тупо $PPID - и вот вам PID самого скрипта, теже PID'ы у дочерних процессов, как правило, больше, чем у родительского; массивы "изкаропки" - это вам не файлик for'ом перебирать. Перечислять можно бесконечно, но это не показатель - cmd тоже на много способен, просто не все это знают, и не хватает какого-нибудь "Advanced Batch Scripting Guide" .

Это то что получилось:
т.н. z.bat

@echo off
setlocal ENABLEEXTENSIONS
setlocal ENABLEDELAYEDEXPANSION
set exenm=%0
set comstr=%1
for /f "skip=1" %%a in ('wmic Process Where "Name LIKE 'cmd%%' AND CommandLine LIKE '%%!exenm!%%!comstr!%%'" GET ProcessId') do (call :chk1 %%a)
if exist tmp\%ppid% (echo. > tmp\%ppid%_%1) else (echo. > tmp\%ppid%)
endlocal & exit /b
:chk1
if not "%1" == "" set ppid=%1
exit /b

и "стартер" для z.bat (назовём его x.bat)

@echo off
for /l %%a in (1,1,100) do (call :main "cmd /c z.bat %%a |cmd /c z.bat 02")
exit /b
:main
%~1
exit /b

Пишите, что не так - подумаю что ещё можно сделать.
И еще замечено - можно использовать объём свободной памяти из wmic os get FreePhysicalMemory - коллизии встречались, но реже, чем %time% и wmic os get localdatetime (каждый извращается по своему ).
А самый запасной вариант: "сторонние" решения выше и getpids.exe, ссылку на офф. сайт не помню, но "гуглится" быстро.

27

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

UNDYING пишет:

if exist tmp\%ppid% (echo. > tmp\%ppid%_%1) else (echo. > tmp\%ppid%_%1)

Объясните что значит этот код?! Что Вы хотели этим сказать?

UNDYING пишет:

На вышеописанной мною машине

три и более процессов лишь для проверки, что коллизии находятся в зависимости от процессоров, ядер. Ну а Ваши слова говорят о том, что дело не в количестве "исполнителей", а в самой ОС.

UNDYING пишет:

А самый запасной вариант: "сторонние" решения"

абсолютно не приемлемы, задача должна решаться средствами только ОС.

( 2 * b ) || ! ( 2 * b )

28

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

OFF: Если уж про внешние средства — то «GUIDGen.EXE» .

29 (изменено: UNDYING, 2012-04-06 18:41:12)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:
UNDYING пишет:

if exist tmp\%ppid%...

Объясните что значит этот код?! Что Вы хотели этим сказать?

Просто перенаправил вывод, код "технический" - можно смело убирать. Проверка на существование файла - название берётся из PID процесса - а они могут повторяться, когда тестировал - считал процессы, их и, соответственно, файлов должно было быть 200. Единовременно 2-х и более процессов с одинаковым PID существовать всё равно не может (ядро в расчёт не берём).

Rumata пишет:

три и более процессов лишь для проверки, что коллизии находятся в зависимости от процессоров, ядер. Ну а Ваши слова говорят о том, что дело не в количестве "исполнителей", а в самой ОС.

Ну собственно опыт это доказал, хотя, на более слабых машинах, скорее "проканают" любые способы, описанные выше.

Rumata пишет:

абсолютно не приемлемы, задача должна решаться средствами только ОС.

Собственно, как я думаю, я показал как это можно сделать, но каждый процесс придётся немного "индивидуализировать" - передать уникальное число для поиска, можно использовать тот же родительский PID делённый на № процесса в конвейере (напр: 1 и 2).

Попробую прокомментировать свой код:

:: тут просто - имя самого файла и "уникальный идентификатор" в 1-м аргументе, используются для поика PID
set exenm=%0
set comstr=%1
:: запрос к WMI: среди процессов выбрать тот
:: у которого имя начинается на cmd и далее любые символы (%% - любые символы)
:: И командная строка содержит в середине имя запускаемого файла и первый аргумент
:: и вывести его PID
for /f "skip=1" %%a in ('wmic Process Where "Name LIKE 'cmd%%' AND CommandLine LIKE '%%!exenm!%%!comstr!%%'" GET ProcessId') do (call :chk1 %%a)
...
:: данная "процедура" нужна лишь для того чтобы избавиться от пробелов
:: в "выводе" wmic, иначе %ppid% будет пустым
:chk1
if not "%1" == "" set ppid=%1

Т.е. если вас такое решение устраивает, то для "запиливания" под ваши нужды редактируйте
"Name LIKE 'cmd%%' AND CommandLine LIKE '%%!exenm!%%!comstr!%%'",
и попутно проверяйте - вместо PID батника по этой схеме хорошо ищется PID самого wmic!

alexii пишет:

OFF: Если уж про внешние средства — то «GUIDGen.EXE»

Я пытался найти способ "заюзать" RtlGenRandom из ADVAPI32.DLL, видимо, без внешних средств - вообще никак.

OFF: хм... посты слишком большие получаются, надо меньше "эпистолярить" и больше "по делу".

30

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

UNDYING пишет:

посты слишком большие получаются, надо меньше "эпистолярить" и больше "по делу"

Нормальные )))

Мысль интересная

setlocal enabledelayedexpansion

set procname=%~nx0
set procargs=%~1

WMIC Process WHERE "Name LIKE 'cmd%%' AND CommandLine LIKE '%%!procname!%%!procargs!%%' GET ProcessId

Только смущает необходимость указывать дополнительный параметр в конвейере.

( 2 * b ) || ! ( 2 * b )

31 (изменено: UNDYING, 2012-04-06 20:54:22)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:
UNDYING пишет:

if exist tmp\%ppid% (echo. > tmp\%ppid%_%1) else (echo. > tmp\%ppid%_%1)

Объясните что значит этот код?! Что Вы хотели этим сказать?

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

абсолютно не приемлемы, задача должна решаться средствами только ОС.

OFF: Мне вспонились ребята из Иваново, распространявшие со свой специфичной системой биллинга (честно - не знаю как верно пишется, с л или лл) 400-килобайтный "экзешник" (по размерам - явно на делфях) для перемещения файлов по маске, dir /s и пара if'ов в пару кбайт размером, им явно не угодили... И не забуду супербатники от них же, правда "исправляются", со временем, видимо, перейдут целиком на микс из Java и .Net.

Rumata пишет:

Name LIKE 'cmd%%'

этот момент меня только смущает, по-моему в Name может быть как cmd, так и cmd.exe (по условию проверки оба проходят), а в некоторых случаях и что-то вроде C:\Windows\System32\cmd.exe.

Вообще, изначально я пытался создать "левый" процесс (экземпляр cmd.exe с ключом /c) с помощью wmic process call create CommandLine="cmd.exe /c" - передавать скрипту в этом случае вообще ничего не надо и вероятность коллизий минимальна, но не смог подружить wmic и конвейер |.

for /f "tokens=2 delims=:" %%a in ('wmic process call create CommandLine="cmd.exe /c" ^| find /i "ProcessId"') do (echo %%a)

Нет ли у кого соображений как это заставить работать?
Попробую ещё через if и для двух токенов. Но, видимо, это будет завтра.

for /f "tokens=1,2 delims=:" %%a in ('wmic process call create CommandLine="cmd.exe /c"') do (if "%%a" == "ProcessID" set ppid=%%b)
set ppid=%ppid:~-1%

32

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata
OFF: Я видимо полюбил wmic, столько мата не извергал из себя давно...

Так-с, не выдержал и потестил:
z.bat

@echo off
setlocal
for /f "skip=5 tokens=1,3" %%a in ('wmic Process call create "cmd.exe /c"') do (if "%%a" == "ProcessId" set uid=%%b)
set uid=%uid:~0,-1%
echo %uid%
endlocal & exit /b

x.bat

@echo off
for /l %%a in (1,1,100) do (z|z)
exit /b

Ваша мечта сбылась - uid будет уникален и для z | z, и для z | z | z | z ; передавать скрипту ничего не надо, только вместо "cmd.exe /c" можно заюзать wscript.exe - чтоб не мелькало. Тестируйте и, если подойдёт, принимайте на вооружение.

OFF: Кто ж знал, что в for, wmic надо юзать по-особому, и вариант, который нормально выполняется вне источника для for /f, не катит...

33

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Кто ж знал, что в for, wmic надо юзать по-особому,

for /f "usebackq …" %%i in (`wmic.exe … where "bla-bla-bla='bla-bla-bla2'" ^| find.exe /i "bla-bla-bla"`) do …

34 (изменено: Rumata, 2012-04-07 00:04:37)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

UNDYING
Ого! А ведь мне чуть-чуть не хватило додуматься до этого. Спасибо!

( 2 * b ) || ! ( 2 * b )

35

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

alexii
Спасибо, будем знать и применять.

36

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

alexii
Еще добавлю, для полноты ответа, что неплохо STDERR перенаправлять в NUL:

for /f "usebackq …" %%i in (`wmic.exe … where "bla-bla-bla='bla-bla-bla2'" 2^>nul ^| find.exe /i "bla-bla-bla"`) do …

Это необходимо, чтобы исключить вывод пустой строки. У меня при каждом использовании WMIC выводится пустая строка. При перенаправлении 2>nul пустая строка исчезает.

( 2 * b ) || ! ( 2 * b )

37

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:

Еще добавлю, для полноты ответа, что неплохо STDERR перенаправлять в NUL:

Угу. Часто забываешь об этом.

Rumata пишет:

Это необходимо, чтобы исключить вывод пустой строки.

Не понял, каким образом? У меня что:

wmic.exe bla-bla-vbla

что

wmic.exe bla-bla-vbla 2>nul

при перенаправлении в файл дают полностью идентичный файл. От лишнего же я избавляюсь как раз применением «find.exe».

38

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

alexii
Без 2>nul у меня в консоли появляется одна пустая строка вначале. Когда сделал перенаправление - пустая строка исчезла.

Вопрос. Метод, предложенный UNDYING, работает замечательно. Но, как он уже заметил - "мелькает" окно консоли. Я подумал как запускать что-то, и чтобы оно не мелькало. Нашел, что svchost при обычном запуске в консоль ничего не пишет и просто "отваливается", так как (судя по всему он должен запускаться как сервис. Не опасно ли вызывать создание svchost для получения уникального значения, чтобы избавиться от моргания консольного окна?

( 2 * b ) || ! ( 2 * b )

39 (изменено: UNDYING, 2012-04-07 13:55:53)

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:

Вопрос. Метод, предложенный UNDYING, работает замечательно. Но, как он уже заметил - "мелькает" окно консоли. Я подумал как запускать что-то, и чтобы оно не мелькало. Нашел, что svchost при обычном запуске в консоль ничего не пишет и просто "отваливается", так как (судя по всему он должен запускаться как сервис. Не опасно ли вызывать создание svchost для получения уникального значения, чтобы избавиться от моргания консольного окна?

Чёрт знает, опасно или не опасно. svchost и cmd /c легко меняется на wscript //b - точно неопасно и мелькать не должно. Ну или любое другое GUI приложение, не создающее окна - hh, mshta etc.
SVCHOST точно должен запускаться с параметрами (если через тот же WMI-запрос посмотреть ком.строку у всех svchost), тогда он будет запускать что-то "полезное".

40

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Как я понял, SVCHOST это системный файл, должен запускаться только как служба и не от пользователя. То есть, в этом плане, безопасно. Другой момент, как на создание нового процесса svchost.exe могут отреагировать антивирусы.

UNDYING пишет:

wscript //b - точно неопасно и мелькать не должно. Ну или любое другое GUI приложение, не создающее окна - hh, mshta etc.

mshta - нельзя, потому что этот процесс будет висеть в памяти. и его надо выгружать вручную.
hh - видимо можно. Кстати его интересно запустить с параметром, например hh "c:\Program Files". Кавычки необязательны.

( 2 * b ) || ! ( 2 * b )

41

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Rumata пишет:

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

Экспериментировал на Windows Server 2008 R2 SP1, mshta на данной системе не "зависал" в памяти, как я понимаю, на др. редакциях необходимо выгружать. hh - точно не должен висеть после запуска без параметров.

Кстати его интересно запустить с параметром, например hh "c:\Program Files". Кавычки необязательны.

Сказывается использование движка IE. Сам IE (и просто explorer, который всё больше интегрируется с IE, для вышеуказанного метода его многие и используют ), по сути, может точно так же.

Как я понял, SVCHOST это системный файл, должен запускаться только как служба и не от пользователя. То есть, в этом плане, безопасно. Другой момент, как на создание нового процесса svchost.exe могут отреагировать антивирусы.

Ну не только как служба, на эту тему гуглить надо. Впринципе, если запускать без параметров, ничего страшного быть не должно и опасности никакой не представляется, только, как вы говорите, антивирусы "кричать" могут.

42

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Потестировал.

Практически все консольные утилиты отпадают, так как
-- некоторые из них не могу быть созданы в "скрытом" (не знаю как сказать точно) состоянии, то есть при их создании мелькает консольное окно. Перенаправление STDOUT?STDERR не помогают. Пример: PING
-- некоторые консольные утилиты можно создать без мелькания окна, но они создаются так быстро, что происходит коллизия. Пример: HOSTNAME

Графические тоже. Так как практически нет ни одной графической утилиты, которая не рисовала бы интерфейс. HH - исключение, но при ее использовании все-таки возникают коллизии. MSHTA - остается в памяти: надо выгружать вручную.

Остались 2 более-менее подходящие:
SVCHOST - но как посмотрят антивирусы на ее запуск (%windir%\System32\svchost.exe)
WSCRIPT //B - пока единственная более-менее надежная команда, которую можно запустить и которая пока ни разу не привела к коллизиям. Хотя коллизии теоретически возможны и с нею.

Следующий скрипт - z.bat использовался во всех тестах. Менялся только запускаемый процесс (строка set "proc=...").

Скрипт создает временный файл вида tmp.HHMMSS,CC_UID.tmp (HH - часы, MM - минуты, SS - секунды, CC - сотые секунды, UID - номер процесса). Содержимое файла - имя самого файла и первый аргумент, если он передан скрипту.

@echo off

set "proc=wscript.exe //b>nul"

:again
for /f "tokens=1,2 delims==; " %%a in ( 
    '%windir%\System32\Wbem\wmic.exe Process call create "%windir%\System32\%proc%" 2^>nul ^| %windir%\System32\find.exe "ProcessId"' 
) do (
    set UID=%%b
)

set UTIME=%TIME%

set UTIME=%UTIME: =0%
set UTIME=%UTIME::=%

set tmpfile=tmp.%UTIME%_%UID%.tmp

if exist tmpfile goto again

echo.%1: %tmpfile%>>"%tmpfile%"

Этот скрипт запускает предыдущий многократно. Для упрощения теста использовались утилиты из пакета UnxUtils. Тестовый скрипт вызывается в конвейере z 1 | z 2, 200 раз for /l %%l in ( 1, 1, 200 ). В результате должно быть создано 400 файлов.

@echo off

del tmp*

if not "%~1" == "" goto :EOF

echo.ERRORS
(

for /l %%l in ( 1, 1, 200 ) do @( z 1 | z 2 )

) 2>&1 | sort | uniq -c

echo.SUMMARY
ls -l tmp* | gawk "{ print $5 }" | sort | uniq -c | tee con | gawk "BEGIN { N = 0 } { N += $1 } END { print N }"

А вот тестовые результаты:
hostname.exe>nul
Из 400 файлов вида tmp.HHMMSS,CC_UID.tmp было создано только 353. Из них, 319 файлов размером 27 байт содержат по одной строке (как и должно быть). 34 файла содержат по две строки (54 байта), что значит два процесса писали в один файл. 13 раз конвейер был вызван с ошибкой.

ERRORS
     13 The process cannot access the file because it is being used by another p
rocess.
SUMMARY
    319 27
     34 54
353

wscript.exe //b
Было создано 11, 371 и 18 файлов, соответственно, длиной 26, 27 и 28 байт, содержащие ровно по одной строке. Всего 400 файлов. Различие в размерах связана с длиной строки, обозначающей номер процесса (3, 4 или 5 цифр), которая суммируется с количеством остальных символов.

ERRORS
SUMMARY
     11 26
    371 27
     18 28
400

( 2 * b ) || ! ( 2 * b )

43

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Снова решил поиграться в эту "игру". Узнать PID командного интерпретатора можно с помощью PowerShell. Можно было бы соорудить подобные примеры и с использованием WMIC. Они были бы очень похожи на то, что предлагал коллега UNDYING. Но фильтрация нужного экземпляра WMIC, к сожалению, невозможна.

Отобразить PID


:: $pid - PID powershell
::
:: этот код находит себя в списке процессов и 
:: выводит PPID (родительский PID), 
:: который является PID интерпретатора
powershell -NoLogo -NoProfile -Command "$p = (Get-WmiObject Win32_Process -Filter ProcessId=$pid).ParentProcessId; $p"

Запомнить PID


:: Здесь чуть посложнее: надо пройтись по дереву процессов полгубже, 
:: так как скрипт порождает еще одну копию интерпретатора. 
::
:: $pid - PID powershell
:: $p - PID копии интерпретатора
::
:: powershell нахрдит себя в списке процессов, 
:: запоминает PID интерпретатора и ищет PID родителя еще раз, 
:: чтобы получить PPID внешнего интерпретатора
for /f %%p in ( '
    powershell -NoLogo -NoProfile -Command "$p = (Get-WmiObject Win32_Process -Filter ProcessId=$pid).ParentProcessId; (Get-WmiObject Win32_Process -Filter ProcessId=$p).ParentProcessId" ^<nul
' ) do (
    set "PID=%%p"
)

Желающие могут потестировать следующий код, создавая несколько экземпляров скрипта, и убедиться, что каждый экземпляр создает уникальное значение, соответствующее PID интерпретатора. Для иллюстрации выводится значение переменой %RANDOM%.


:: Чтобы наблюдать уникальные значения рекомендуется 
:: запускать скрипт в конвейре. Для визуального различия 
:: каждого экземпляра надо указать в качестве параметра 
:: любое уникальное значение. Например
::
:: z 1 | z 2 | z 3
::
:: Каждый экземпляр выводит: 
:: 1. значение первого параметра
:: 2. PID интерпретатора
:: 3. Псевдослучайное значение из %RANDOM%
@echo off

for /f %%p in ( '
    powershell -NoLogo -NoProfile -Command "$p = (Get-WmiObject Win32_Process -Filter ProcessId=$pid).ParentProcessId; (Get-WmiObject Win32_Process -Filter ProcessId=$p).ParentProcessId" ^<nul
' ) do (
    echo:%~1: %%p: %RANDOM%
)>&2
( 2 * b ) || ! ( 2 * b )

44

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

Забавно, что в рамках обсуждения решение проблемы уже было озвучено, но все зачем-то закрутилось вокруг PID процесса, который, в принципе, не нужен вовсе. Используем генерацию GUID из PowerShell.


rem True random temp folder

set tempDir=%temp%\MyScript
for /f "usebackq" %%a in (`powershell -command "$([guid]::NewGuid().ToString())"`) do (
    set tempDir=%tempDir%%%a
)

У приведенного примера есть только один недостаток - внешний вызов PowerShell - довольно медлительная процедура

45

Re: CMD/BAT: Проблема создания уникальных значений в конвейерных командах

nicestep
Ваше решение хорошо, но уверенности что генерируемые значения уникальны нет. PID гарантирует уникальное значениев в текущий момент времени.

( 2 * b ) || ! ( 2 * b )