Я пытаюсь реализовать оболочку класса вокруг Powershell
.
Это почти работает, за исключением того, что зависает, когда не может больше прочитать вывод из STREAM
.
Таким образом, ему удается прочитать весь вывод, но после того, как его больше нет, он просто зависает в операторе IMPORT
:
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS Powershell:
DEF PRIVATE STREAM stPowershell.
CONSTRUCTOR PUBLIC Powershell():
INPUT-OUTPUT STREAM stPowershell THROUGH VALUE("powershell").
THIS-OBJECT:ReadOutput().
END.
DESTRUCTOR Powershell():
INPUT-OUTPUT STREAM stPowershell CLOSE.
END.
METHOD PUBLIC CHAR Input(i_cInput AS CHAR):
IF i_cInput = ? THEN UNDO, THROW NEW Progress.Lang.AppError(SUBST("&1: 'i_cInput' is 'UNKNOWN'!", PROGRAM-NAME(1))).
PUT STREAM stPowershell UNFORMATTED i_cInput SKIP.
RETURN THIS-OBJECT:ReadOutput().
END.
METHOD PROTECTED CHAR ReadOutput():
DEF VAR cOutputs AS CHAR NO-UNDO.
DEF VAR cOutput AS CHAR NO-UNDO.
DEF VAR lFirst AS LOGICAL NO-UNDO INIT TRUE.
REPEAT:
IF lFirst THEN lFirst = FALSE.
ELSE cOutputs = cOutputs + "~n".
IMPORT STREAM stPowershell UNFORMATTED cOutput NO-ERROR.
cOutputs = cOutputs + cOutput.
END.
RETURN cOutputs.
END.
END.
Использование:
DEF VAR oPowershell AS CLASS Powershell NO-UNDO.
DEF VAR cOutput AS CHAR NO-UNDO.
oPowershell = NEW Powershell().
cOutput = oPowershell:Input("$num = 12").
cOutput = oPowershell:Input("(New-Object -ComObject Wscript.Shell).Popup($num, 0, 'Done', 0x0)").
cOutput = oPowershell:Input("Write-Output 'test output'").
В документации @MathiasR.Jessen Progress указано, что оператор SEEK в этом случае не работает: docs.progress.com/bundle/openedge-abl-reference-117/page/…
Как прекращается вывод? Есть ли последняя новая строка? Или он просто закрывает выход?
@TomBascom Согласно READKEY
Powershell завершает каждую выходную строку до ~r~r
. Если читать с IMPORT
, в конце каждой прочитанной строки добавляется ~n
.
Ваша цель — открыть «канал» между OpenEdge и экземпляром Powershell, чтобы вы могли отправлять произвольные команды туда и обратно и очищать их выходные данные? Или ваша цель — время от времени запускать определенные команды? (Как ваш пример «ping».) Я изо всех сил пытаюсь понять, почему вы не используете просто OS-COMMAND или более прямой вызов INPUT THROUGH.
Я думаю, что pipe
соответствует моей цели. Идея состоит в том, чтобы обернуть сеанс Powershell
в экземпляр объекта и использовать этот экземпляр для связи между OpenEdge
и Powershell
. Поскольку сеанс остается прежним, у нас появляется больше возможностей. Например, мы могли бы использовать это для SFTP
или SSH
.
Вы можете попробовать что-то вроде этого:
METHOD PROTECTED CHAR ReadOutput():
DEF VAR cOutputs AS CHAR NO-UNDO.
do while true:
readkey STREAM stPowershell pause 0.
if lastkey <= 0 then
leave.
else
cOutputs = cOutputs + chr( lastkey ).
END.
RETURN cOutputs.
END.
Спасибо, Том, но, похоже, здесь та же проблема. Он правильно читает весь вывод, но после последнего существующего символа код зависания READKEY
больше не продолжается.
Кажется, что эта версия читается больше. Кажется, что IMPORT
зависает, когда в конце последней строки отсутствует «~n». Этот идет до самого конца, но также зависает после последнего символа.
READKEY должен возвращать -1 по истечении времени ожидания, поскольку ввода больше нет. Это должно привести к выполнению LEAVE. Если это произойдет, появится надпись «RETURN cOutputs». должно выполняться (добавьте СООБЩЕНИЕ непосредственно перед ним для проверки). Если все это правда, то «зависание» происходит не в READKEY или ReadOutput(), а в какой-то другой части вашего кода. Если бы мне пришлось угадывать, я бы предположил, что ваш Powershell ожидает ввода и не завершил работу, поэтому выходной поток все еще открыт.
Я провел еще несколько тестов. Кажется, что если я использую прямую команду (например, ping google.com
в INPUT-OUTPUT THROUGH
вместо powershell
, вывод можно читать нормально, без зависаний. Похоже, что чтение вывода предполагает, что поток уже «закрыт», когда мы его читаем. Значение когда мы запускаем одну команду, она запускается, выводит свои данные в выходной поток и завершает работу. Однако, если после этого мы оставим поток открытым и попытаемся запустить вторую команду с помощью PUT
, команды ничего не делают, и вывод всегда пуст
Один из способов заставить это работать — узнать, сколько строк существует в выводе после каждой отправленной команды. Мы могли бы запустить дополнительный вывод EOF
после фактической команды и использовать READKEY
для чтения, пока он не противодействует этому EOF
и не перестанет читать, пока мы не отправим новую команду.
Нет, IMPORT и READKEY не предполагают, что «выход» закрыт. Они с радостью будут читать данные, пока они доступны. Я заметил, что вы не подтвердили (или не опровергли), если READKEY PAUSE 0. Вернул -1. Разница между «прямой командой» и оболочкой заключается в том, что оболочка ожидает больше команд, поэтому по своей конструкции она не закрывает вывод после каждой команды. (Хотя во многих оболочках также часто есть опция запуска одной команды и последующего завершения, может быть, это то, что вам нужно?)
READKEY PAUSE 0
ничего не возвращает после последнего выходного символа, поскольку он вечно зависает на этом операторе. Таким образом, нам никогда не удастся получить -1
, поскольку код зависает на последнем операторе READKEY PAUSE 0
, и мы никогда не сможем прочитать LASTKEY
. Например, если в выводе есть ab
, ему удается прочитать a
и b
, но после b
он пытается получить еще один символ, и в этой ситуации READKEY PAUSE 0
зависает, и выполнение кода останавливается навсегда. После этого поможет только остановка процесса. READKEY
и IMPORT
просто не работают с powershell
или cmd
при чтении вывода
Хорошо, отсутствие возврата READKEY подтверждает (для меня), что проблема в том, что powershell.exe не закрывает вывод после завершения. (Может быть, для вас это очевидно, но не для меня.) Итак, мое решение использовать READKEY само по себе не сработает. Но, по крайней мере, с моей точки зрения, это было полезно для устранения проблемы.
Мне удалось заставить эту версию работать, но я бы не назвал ее идеальным решением.
Чтобы избежать проблемы с зависанием чтения вывода, я сделал вывод Powershell
символом EOF
после каждой команды. Таким образом, любое синхронное выполнение всегда будет заканчиваться символом EOF
в выводе Powershell
. Мы будем читать вывод, пока не достигнем EOF
и остановимся на этом.
Возможные проблемы могут возникнуть, если мы запускаем асинхронные команды или выходные данные содержат символы EOF
.
Я думаю, что лучшим решением было бы использовать API
, специфичные для платформы (например, WIN32 API
). Нам нужен способ прочитать весь существующий вывод. Но я думаю, что эта версия подойдет для многих обычных случаев.
BLOCK-LEVEL ON ERROR UNDO, THROW.
CLASS Powershell:
&SCOPED-DEFINE POWERSHELL_EOF "[char]26"
&SCOPED-DEFINE PROGRESS_EOF 26
DEF PRIVATE STREAM stPowershell.
CONSTRUCTOR PUBLIC Powershell():
/* Start new 'Powershell' */
INPUT-OUTPUT STREAM stPowershell THROUGH VALUE("powershell") NO-ECHO.
/* Disable prompt */
PUT STREAM stPowershell UNFORMATTED "$global:Prompt = $null" SKIP.
/* Flush startup output */
THIS-OBJECT:Read().
END.
DESTRUCTOR Powershell():
INPUT-OUTPUT STREAM stPowershell CLOSE.
END.
METHOD PUBLIC CHAR Write(i_cInput AS CHAR):
IF i_cInput = ? THEN UNDO, THROW NEW Progress.Lang.AppError(SUBST("&1: 'i_cInput' is 'UNKNOWN'!", PROGRAM-NAME(1))).
/* Write input */
PUT STREAM stPowershell UNFORMATTED i_cInput SKIP.
/* Read output */
RETURN THIS-OBJECT:Read().
END.
METHOD PROTECTED CHAR Read():
DEF VAR cOutputs AS CHAR NO-UNDO.
/* Output 'EOF' manually */
PUT STREAM stPowershell UNFORMATTED {&POWERSHELL_EOF} SKIP.
/* Read all output until 'EOF' */
REPEAT:
READKEY STREAM stPowershell PAUSE 0.
/* If we reached the 'EOF', stop reading */
IF LASTKEY = {&PROGRESS_EOF} THEN DO:
LEAVE.
END.
cOutputs = cOutputs + CHR(LASTKEY).
END.
RETURN cOutputs.
END.
END.
Я не очень хорошо знаком с ABL/OpenEdge, но попробуйте проверить, есть ли в потоке что-нибудь для чтения с помощью
IF SEEK(stPowerShell) <> ? THEN [... proceed to read output]
или чего-то подобного.