Обернуть Powershell в класс Progress

Я пытаюсь реализовать оболочку класса вокруг 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'").

Я не очень хорошо знаком с ABL/OpenEdge, но попробуйте проверить, есть ли в потоке что-нибудь для чтения с помощью IF SEEK(stPowerShell) <> ? THEN [... proceed to read output] или чего-то подобного.

Mathias R. Jessen 08.05.2024 17:26

В документации @MathiasR.Jessen Progress указано, что оператор SEEK в этом случае не работает: docs.progress.com/bundle/openedge-abl-reference-117/page/…

W0lfw00ds 08.05.2024 17:37

Как прекращается вывод? Есть ли последняя новая строка? Или он просто закрывает выход?

Tom Bascom 08.05.2024 22:48

@TomBascom Согласно READKEY Powershell завершает каждую выходную строку до ~r~r. Если читать с IMPORT, в конце каждой прочитанной строки добавляется ~n.

W0lfw00ds 09.05.2024 11:20

Ваша цель — открыть «канал» между OpenEdge и экземпляром Powershell, чтобы вы могли отправлять произвольные команды туда и обратно и очищать их выходные данные? Или ваша цель — время от времени запускать определенные команды? (Как ваш пример «ping».) Я изо всех сил пытаюсь понять, почему вы не используете просто OS-COMMAND или более прямой вызов INPUT THROUGH.

Tom Bascom 10.05.2024 17:53

Я думаю, что pipe соответствует моей цели. Идея состоит в том, чтобы обернуть сеанс Powershell в экземпляр объекта и использовать этот экземпляр для связи между OpenEdge и Powershell. Поскольку сеанс остается прежним, у нас появляется больше возможностей. Например, мы могли бы использовать это для SFTP или SSH.

W0lfw00ds 10.05.2024 18:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
6
91
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете попробовать что-то вроде этого:

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 больше не продолжается.

W0lfw00ds 09.05.2024 10:50

Кажется, что эта версия читается больше. Кажется, что IMPORT зависает, когда в конце последней строки отсутствует «~n». Этот идет до самого конца, но также зависает после последнего символа.

W0lfw00ds 09.05.2024 11:14

READKEY должен возвращать -1 по истечении времени ожидания, поскольку ввода больше нет. Это должно привести к выполнению LEAVE. Если это произойдет, появится надпись «RETURN cOutputs». должно выполняться (добавьте СООБЩЕНИЕ непосредственно перед ним для проверки). Если все это правда, то «зависание» происходит не в READKEY или ReadOutput(), а в какой-то другой части вашего кода. Если бы мне пришлось угадывать, я бы предположил, что ваш Powershell ожидает ввода и не завершил работу, поэтому выходной поток все еще открыт.

Tom Bascom 09.05.2024 16:40

Я провел еще несколько тестов. Кажется, что если я использую прямую команду (например, ping google.com в INPUT-OUTPUT THROUGH вместо powershell, вывод можно читать нормально, без зависаний. Похоже, что чтение вывода предполагает, что поток уже «закрыт», когда мы его читаем. Значение когда мы запускаем одну команду, она запускается, выводит свои данные в выходной поток и завершает работу. Однако, если после этого мы оставим поток открытым и попытаемся запустить вторую команду с помощью PUT, команды ничего не делают, и вывод всегда пуст

W0lfw00ds 09.05.2024 22:01

Один из способов заставить это работать — узнать, сколько строк существует в выводе после каждой отправленной команды. Мы могли бы запустить дополнительный вывод EOF после фактической команды и использовать READKEY для чтения, пока он не противодействует этому EOF и не перестанет читать, пока мы не отправим новую команду.

W0lfw00ds 09.05.2024 22:08

Нет, IMPORT и READKEY не предполагают, что «выход» закрыт. Они с радостью будут читать данные, пока они доступны. Я заметил, что вы не подтвердили (или не опровергли), если READKEY PAUSE 0. Вернул -1. Разница между «прямой командой» и оболочкой заключается в том, что оболочка ожидает больше команд, поэтому по своей конструкции она не закрывает вывод после каждой команды. (Хотя во многих оболочках также часто есть опция запуска одной команды и последующего завершения, может быть, это то, что вам нужно?)

Tom Bascom 10.05.2024 02:04
READKEY PAUSE 0 ничего не возвращает после последнего выходного символа, поскольку он вечно зависает на этом операторе. Таким образом, нам никогда не удастся получить -1, поскольку код зависает на последнем операторе READKEY PAUSE 0, и мы никогда не сможем прочитать LASTKEY. Например, если в выводе есть ab, ему удается прочитать a и b, но после b он пытается получить еще один символ, и в этой ситуации READKEY PAUSE 0 зависает, и выполнение кода останавливается навсегда. После этого поможет только остановка процесса. READKEY и IMPORT просто не работают с powershell или cmd при чтении вывода
W0lfw00ds 10.05.2024 09:48

Хорошо, отсутствие возврата READKEY подтверждает (для меня), что проблема в том, что powershell.exe не закрывает вывод после завершения. (Может быть, для вас это очевидно, но не для меня.) Итак, мое решение использовать READKEY само по себе не сработает. Но, по крайней мере, с моей точки зрения, это было полезно для устранения проблемы.

Tom Bascom 10.05.2024 17:10
Ответ принят как подходящий

Мне удалось заставить эту версию работать, но я бы не назвал ее идеальным решением.

Чтобы избежать проблемы с зависанием чтения вывода, я сделал вывод 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.

Другие вопросы по теме