Вы можете использовать PowerShell в качестве пакетного подпроцесса для создания именованного канала и использовать его для создания IPC между пакетом и другими пакетами/подсистемами в Windows.
Вот пример использования этого для быстрого окрашивания пакетного вывода: PowerShell как подпроцесс через именованный канал
РЕДАКТИРОВАНИЕ: установите его в режим сообщения и сделайте его более читаемым для @Bbb.
EDIT2: Добавление отсутствующего NOP и предупреждения безопасности.
Будьте осторожны в вопросах безопасности. Используйте учетные данные для именованного канала и обратите внимание, что fd 5 доступен для чтения любой консолью/процессом на сервере. Используйте настоящий двунаправленный канал или другой именованный канал вместо fd 5, чтобы избежать этого. Не используйте это в чистом виде.
Пример пакетного кода для создания двунаправленного именованного канала через PowerShell:
::
:: Launch a PowerShell child process in the background linked to the console and
:: earing through named pipe which name provided as an argument of this function.
::
:: Parameters :
:: [ Name] : A name for the named pipe.
:: Return :
:: 0 if the child PowerShell has been successfully launched and the named pipe is available.
:: 1 if it fails.
:: 3 if PowerShell is not present or doesn't work.
::
:LaunchPowerShellSubProcessPipe
SET LOCALV_NAME=
IF NOT "%~1" == "" SET "LOCALV_NAME=%~1"
IF "%~1" == "" ( ECHO :LaunchPowerShellSubProcessPipe need a name.& EXIT /B 1)
powershell -command "$_" 2>&1 >NUL
IF NOT "!ERRORLEVEL!" == "0" EXIT /B 3
REM As properly stated by WReach [https://mathematica.stackexchange.com/questions/198187/how-to-read-a-named-pipe-on-windows], Windows named pipe have their own hand shake logic (WaitNamedPipe... {humor}Microsoft hate us all probably{/humor}
REM To "read" the named pipe from batch in a POSIX way, we redirected reply to fd 5. Reply to the output part of the named pipe can't be read from batch directly nor with common windows tools.
REM Do not use [System.IO.Pipes.PipeOptions]::Asynchronous !
(ECHO $CloseHandle = Add-Type 'A' -PassThru -MemberDefinition '[DllImport^^^("kernel32.dll"^^^)] public static extern bool CloseHandle^^^(IntPtr hObject^^^);'; & ^
ECHO # In case of, we close the fd beforehand & ^
ECHO $Clean = $CloseHandle::CloseHandle^^^(5^^^); & ^
ECHO $npipeServer = new-object System.IO.Pipes.NamedPipeServerStream^^^('%LOCALV_NAME%', [System.IO.Pipes.PipeDirection]::In, 1, [System.IO.Pipes.PipeTransmissionMode]::Message, [System.IO.Pipes.PipeOptions]::None, 256, 256^^^); & ^
ECHO Try { & ^
ECHO $npipeServer.WaitForConnection^^^(^^^); & ^
ECHO $pipeReader = new-object System.IO.StreamReader^^^($npipeServer^^^); & ^
ECHO Try { & ^
ECHO While^^^(^^^($msg = $pipeReader.ReadLine^^^(^^^)^^^) -notmatch 'QUIT'^^^) { & ^
ECHO Try { & ^
ECHO If ^^^($msg.Length -gt 0^^^) { & ^
ECHO $disp='Server got :'+$msg; & ^
ECHO Write-Host^^^($disp^^^); & ^
ECHO $curdte = Get-Date -Format 'HH:mm:ss'; & ^
ECHO $reply='Reply to '+$msg+', im here, time is '+$curdte; & ^
ECHO $reply_flag=0; & ^
ECHO While^^^($reply_flag -eq 0^^^) { & ^
ECHO Try { & ^
ECHO Write-Output^^^($reply^^^) ^^^>5; & ^
ECHO $reply_flag=1; & ^
ECHO } Catch [System.IO.IOException] { & ^
ECHO # Deal as you whish with potential errors & ^
ECHO }; & ^
ECHO }; & ^
ECHO } & ^
ECHO } Finally { & ^
ECHO $npipeServer.Disconnect^^^(^^^); & ^
ECHO $npipeServer.WaitForConnection^^^(^^^); & ^
ECHO } & ^
ECHO }; & ^
ECHO } Catch [System.IO.IOException] { & ^
ECHO # Deal as you whish with potential errors, you will have InvalidOperation here when the streamReader is empty & ^
ECHO } & ^
ECHO } Finally { & ^
ECHO If ^^^($npipeServer^^^) { & ^
ECHO $npipeServer.Dispose^^^(^^^); & ^
ECHO } & ^
ECHO # We close the fd & ^
ECHO $Clean = $CloseHandle::CloseHandle^^^(5^^^); & ^
ECHO } & ^
ECHO exit & ^
ECHO[)| START /B powershell 2>NUL >NUL
SET /A LOCALV_TRY=20 >NUL
:LaunchPowerShellSubProcessPipe_WaitForPipe
powershell -nop -c "& {sleep -m 50}"
SET /A LOCALV_TRY=!LOCALV_TRY! - 1 >NUL
IF NOT "!LOCALV_TRY!" == "0" cmd /C "ECHO[|SET /P=|MORE 1>\\.\pipe\!LOCALV_NAME!" 2>NUL || GOTO:LaunchPowerShellSubProcessPipe_WaitForPipe
IF "!LOCALV_TRY!" == "0" EXIT /B 1
REM 250 ms. PowerShell isn't the fastest thing to start when dealing with fd redirection... And we need it fully up to write to pipe and read from fd 5.
powershell -nop -c "& {sleep -m 250}"
EXIT /B 0
И пример реализации:
@echo off
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 (
ECHO Can't use extensions
EXIT /B 1
)
::
SETLOCAL ENABLEDELAYEDEXPANSION
IF ERRORLEVEL 1 (
ECHO Can't use expansion
EXIT /B 1
)
REM We create 'MyNamedPipe'
CALL:LaunchPowerShellSubProcessPipe "MyNamedPipe"
SET "LOCALV_RET=!ERRORLEVEL!"
IF NOT "!LOCALV_RET!" == "0" (
ECHO Failed to create the named pipe... Exit code from LaunchPowerShellSubProcessPipe : !LOCALV_RET!
EXIT /B 1
)
REM Sending something through the pipe to the PowerShell subprocess that can be used as an IPC gate for other processes
ECHO Batch send hi to the PowerShell subprocess
ECHO Hi from the batch>\\.\pipe\MyNamedPipe
REM A NOP equivalent to be sure pipe hand shake reach completion before any other comms and that nothing stay open from this CMD and the named pipe
ECHO[|SET /P=>NUL
REM At this time, the PowerShell subprocess will take few cycles to write on the console and then write the response through fd 5.
REM We can check the pipe availability in the same way it's done in :LaunchPowerShellSubProcessPipe, but a direct read will suffice here.
REM If we've read from the pipe, it should have ben a blocked read as we've created a synchronous pipe.
REM As we read reply from fd 5, it's not synchronous, so we need to wait until we get the reply.
:WaitForReply
SET LOCALV_REPLY=0
FOR /F "tokens=*" %%R IN ('MORE ^<5') DO (
ECHO Batch got a reply from SubProcess : %%R
SET LOCALV_REPLY=1
)
IF NOT "!LOCALV_REPLY!" == "1" GOTO :WaitForReply
ECHO caller is happy >\\.\pipe\MyNamedPipe
REM A NOP equivalent to be sure pipe hand shake reach completion before any other comms and that nothing stay open from this CMD and the named pipe
ECHO[|SET /P=>NUL
:WaitForReply2
SET LOCALV_REPLY=0
FOR /F "tokens=*" %%R IN ('MORE ^<5') DO (
ECHO Batch got a 2nd reply from SubProcess : %%R
SET LOCALV_REPLY=1
)
IF NOT "!LOCALV_REPLY!" == "1" GOTO :WaitForReply2
REM Waiting one second for visible time delta
powershell -nop -c "& {sleep -m 1000}"
ECHO This is my leave. >\\.\pipe\MyNamedPipe
REM A NOP equivalent to be sure pipe hand shake reach completion before any other comms and that nothing stay open from this CMD and the named pipe
ECHO[|SET /P=>NUL
:WaitForReply3
SET LOCALV_REPLY=0
FOR /F "tokens=*" %%R IN ('MORE ^<5') DO (
ECHO Batch got a 3th reply from SubProcess : %%R
SET LOCALV_REPLY=1
)
IF NOT "!LOCALV_REPLY!" == "1" GOTO :WaitForReply3
REM We can now tell goodbye to the PowerShell subprocess
ECHO QUIT>\\.\pipe\MyNamedPipe
EXIT /B 0
::
:: Launch a PowerShell child process in the background linked to the console and
:: earing through named pipe which name provided as an argument of this function.
::
:: Parameters :
:: [ Name] : A name for the named pipe.
:: Return :
:: 0 if the child PowerShell has been successfully launched and the named pipe is available.
:: 1 if it fails.
:: 3 if PowerShell is not present or doesn't work.
::
:LaunchPowerShellSubProcessPipe
SET LOCALV_NAME=
IF NOT "%~1" == "" SET "LOCALV_NAME=%~1"
IF "%~1" == "" ( ECHO :LaunchPowerShellSubProcessPipe need a name.& EXIT /B 1)
powershell -command "$_" 2>&1 >NUL
IF NOT "!ERRORLEVEL!" == "0" EXIT /B 3
REM As properly stated by WReach [https://mathematica.stackexchange.com/questions/198187/how-to-read-a-named-pipe-on-windows], Windows named pipe have their own hand shake logic (WaitNamedPipe... {humor}Microsoft hate us all probably{/humor}
REM To "read" the named pipe from batch in a POSIX way, we redirected reply to fd 5. Reply to the output part of the named pipe can't be read from batch directly nor with common windows tools.
REM Do not use [System.IO.Pipes.PipeOptions]::Asynchronous !
(ECHO $CloseHandle = Add-Type 'A' -PassThru -MemberDefinition '[DllImport^^^("kernel32.dll"^^^)] public static extern bool CloseHandle^^^(IntPtr hObject^^^);'; & ^
ECHO # In case of, we close the fd beforehand & ^
ECHO $Clean = $CloseHandle::CloseHandle^^^(5^^^); & ^
ECHO $npipeServer = new-object System.IO.Pipes.NamedPipeServerStream^^^('%LOCALV_NAME%', [System.IO.Pipes.PipeDirection]::In, 1, [System.IO.Pipes.PipeTransmissionMode]::Message, [System.IO.Pipes.PipeOptions]::None, 256, 256^^^); & ^
ECHO Try { & ^
ECHO $npipeServer.WaitForConnection^^^(^^^); & ^
ECHO $pipeReader = new-object System.IO.StreamReader^^^($npipeServer^^^); & ^
ECHO Try { & ^
ECHO While^^^(^^^($msg = $pipeReader.ReadLine^^^(^^^)^^^) -notmatch 'QUIT'^^^) { & ^
ECHO Try { & ^
ECHO If ^^^($msg.Length -gt 0^^^) { & ^
ECHO $disp='Server got :'+$msg; & ^
ECHO Write-Host^^^($disp^^^); & ^
ECHO $curdte = Get-Date -Format 'HH:mm:ss'; & ^
ECHO $reply='Reply to '+$msg+', im here, time is '+$curdte; & ^
ECHO $reply_flag=0; & ^
ECHO While^^^($reply_flag -eq 0^^^) { & ^
ECHO Try { & ^
ECHO Write-Output^^^($reply^^^) ^^^>5; & ^
ECHO $reply_flag=1; & ^
ECHO } Catch [System.IO.IOException] { & ^
ECHO # Deal as you whish with potential errors & ^
ECHO }; & ^
ECHO }; & ^
ECHO } & ^
ECHO } Finally { & ^
ECHO $npipeServer.Disconnect^^^(^^^); & ^
ECHO $npipeServer.WaitForConnection^^^(^^^); & ^
ECHO } & ^
ECHO }; & ^
ECHO } Catch [System.IO.IOException] { & ^
ECHO # Deal as you whish with potential errors, you will have InvalidOperation here when the streamReader is empty & ^
ECHO } & ^
ECHO } Finally { & ^
ECHO If ^^^($npipeServer^^^) { & ^
ECHO $npipeServer.Dispose^^^(^^^); & ^
ECHO } & ^
ECHO # We close the fd & ^
ECHO $Clean = $CloseHandle::CloseHandle^^^(5^^^); & ^
ECHO } & ^
ECHO exit & ^
ECHO[)| START /B powershell 2>NUL >NUL
SET /A LOCALV_TRY=20 >NUL
:LaunchPowerShellSubProcessPipe_WaitForPipe
powershell -nop -c "& {sleep -m 50}"
SET /A LOCALV_TRY=!LOCALV_TRY! - 1 >NUL
IF NOT "!LOCALV_TRY!" == "0" cmd /C "ECHO[|SET /P=|MORE 1>\\.\pipe\!LOCALV_NAME!" 2>NUL || GOTO:LaunchPowerShellSubProcessPipe_WaitForPipe
IF "!LOCALV_TRY!" == "0" EXIT /B 1
REM 250 ms. PowerShell isn't the fastest thing to start when dealing with fd redirection... And we need it fully up to write to pipe and read from fd 5.
powershell -nop -c "& {sleep -m 250}"
EXIT /B 0
Ожидаемый результат:
Batch send hi to the PowerShell subprocess
Batch got a reply from SubProcess : Reply to Hi from the batch, im here, time is 14:15:34
Batch got a 2nd reply from SubProcess : Reply to caller is happy , im here, time is 14:15:34
Batch got a 3th reply from SubProcess : Reply to This is my leave. , im here, time is 14:15:35
person
Zilog80
schedule
15.03.2021
exiftool, обычно являются файлами мультимедиа, поэтому операции ввода-вывода, связанные с записью и чтением управляющего файла, затмеваются операциями ввода-вывода, связанными с чтением и записью файлов мультимедиа. Концептуально, канал — это то, что я буду использовать в Linux, поэтому я надеялся, что в Windows будет эквивалентное решение. Если это не так, я просто выберу решение с временным файлом. - person Sven Marnach   schedule 02.08.2012