1 Востаннє редагувалося Betterthanyou (17.11.2022 01:51:01)

Тема: Process | RedirectStandardInput чи можна перевідкрити потік ?

Створення процесу виглядає так:

        public const string ContainerShell = "exec -i {0} bash";
        public const string containerId = "<ID>";
...
        private void RunDockerShell(string containerId)
        {
            processDockerShell = new Process();
            processDockerShell.StartInfo.FileName = "docker.exe";
            processDockerShell.StartInfo.RedirectStandardInput = true;
            processDockerShell.StartInfo.RedirectStandardError = true;
            processDockerShell.StartInfo.RedirectStandardOutput = true;
            processDockerShell.StartInfo.CreateNoWindow = true;
            processDockerShell.StartInfo.UseShellExecute = false;
            var CMDContainerShell = string.Format(CMD.ContainerShell, containerId);
            processDockerShell.StartInfo.Arguments = CMDContainerShell;
            processDockerShell.Start();
            processDockerShell.BeginOutputReadLine();
            processDockerShell.BeginErrorReadLine();
            processDockerShell.OutputDataReceived += new DataReceivedEventHandler(ProcOutputHandler);
            processDockerShell.ErrorDataReceived += new DataReceivedEventHandler(ProcOutputHandler);
        }

        private static void ProcOutputHandler(object sendingProcess,
            DataReceivedEventArgs outLine)
        {
            if (!String.IsNullOrEmpty(outLine.Data))
            {
                Console.WriteLine("ProcOutputHandler => " + outLine.Data);
            }
        }

1) Якщо я пробую через StandardInput.WriteLine надіслати команду, то вона записує символ нового рядка. Символ "\n" або "\r".

'...BTC/USDT\r', а має бути '...BTC/USDT'

2) processDockerShell.WaitForExit - не працює тому що це асинхронний потік. Тобто я не можу знати коли вивід завершився.


            public const string UpdatePairs = "myapp download-data --exchange binance --pairs BTC/USDT";
...
            var CMDUpdatePairs = string.Format(CMD.UpdatePairs, containerId);
            processDockerShell.StandardInput.WriteLine(CMDUpdatePairs);
            processDockerShell.StandardInput.Flush();

Тому я вирішив використовувати StandardInput.Write та закривати потік

            public const string UpdatePairs = "myapp download-data --exchange binance --pairs BTC/USDT";
...
            var CMDUpdatePairs = string.Format(CMD.UpdatePairs, containerId);
            processDockerShell.StandardInput.Write(CMDUpdatePairs);
            processDockerShell.StandardInput.Flush();
            processDockerShell.StandardInput.Close();
            processDockerShell.WaitForExit(1200000);
            if (!processDockerShell.HasExited)
            {
            ...

А чи можна  заново відкрити потік processDockerShell.StandardInput ? Для того щоб продовжити надсилати нові команди ?
Тобто не заново відкрити процес, а лише потік.

2

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

А нащо ви його закриваєте? По flush дані відправлені, тримайте собі потік на здоров'я.

Подякували: leofun01, Torbins2

3 Востаннє редагувалося Betterthanyou (18.11.2022 00:04:36)

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

koala написав:

А нащо ви його закриваєте? По flush дані відправлені, тримайте собі потік на здоров'я.

Якщо я його не закриваю, то нічого не відбувається. Тобто програма не викликає ProcOutputHandler, а дальше продовжує виконуватися

Методи очікування не працюють:
processDockerShell.StandardInput.Flush(); - не чикає
processDockerShell.WaitForInputIdle(); - не чикає
processDockerShell.WaitForExit(1200000); - чекає "вічно", але ProcOutputHandler все одно не викликається

Якщо створити паралельний потік у якому вводити дані в StandardInput, а в основному потоці зробити паузу, то теж ProcOutputHandler не викликається

4 Востаннє редагувалося Betterthanyou (18.11.2022 02:52:46)

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

Приклад з cmd.exe

using System.Diagnostics;

Process process;

void Cmd()
{
    process = new Process();
    process.StartInfo.FileName = "cmd.exe";
    process.StartInfo.RedirectStandardInput = true;
    process.StartInfo.RedirectStandardError = true;
    process.StartInfo.RedirectStandardOutput = true;
    process.StartInfo.CreateNoWindow = true;
    process.StartInfo.UseShellExecute = false;
    process.Start();
    process.BeginOutputReadLine();
    process.BeginErrorReadLine();
    process.OutputDataReceived += new DataReceivedEventHandler(ProcOutputHandler);
    process.ErrorDataReceived += new DataReceivedEventHandler(ProcOutputHandler);
}

void ProcOutputHandler(object sendingProcess,
            DataReceivedEventArgs outLine)
{
    if (!String.IsNullOrEmpty(outLine.Data))
    {
        Console.WriteLine("ProcOutputHandler => " + outLine.Data);
    }
}

Cmd();

// Не працює (пустий екран, WaitForExit очікує закінчення процесу "вічно")
StreamWriter sw2 = process.StandardInput;

if (sw2.BaseStream.CanWrite)
{
    sw2.Write("ping 127.0.0.1 -n 3 > nul");
}
sw2.Flush();
process.WaitForExit();

// Працює, але один раз. Потрібно знову відкрити стрім (для того щоб працювало потрібно закоментувати попередній варіант)
using (StreamWriter sw = process.StandardInput)
{
    if (sw.BaseStream.CanWrite)
    {
        sw.Write("echo 'I waited 3 sec'");
    }
}
process.WaitForExit();

process.Close();

Як можна зробити так, щоб відправити одну команду, дочекатися відповіді і відправити другу команду (і знову очікувати відповіді) ?

Подякували: leofun011

5

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

Тобто ви запускаєте cmd.exe, вводите команди, які не призводять до завершення його роботи, а потім дивуєтеся, що WaitForExit чекає вічно? Ви не думали, що воно так і має працювати? В такому режимі єдиним індикатором того, що команда відпрацювала, буде поява відповідного тексту в ProcOutputHandler. Але це лише при умові, що не використовується перенаправлення "> nul", бо воно як раз і потрібно щоб вимкнути вивід тексту.

Подякували: leofun011

6

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

Torbins написав:

...В такому режимі єдиним індикатором того, що команда відпрацювала, буде поява відповідного тексту в ProcOutputHandler. Але це лише при умові, що не використовується перенаправлення "> nul", бо воно як раз і потрібно щоб вимкнути вивід тексту.

Якщо замість "ping 127.0.0.1 -n 3 > nul" використати наприклад "time" (чи будь-яку іншу cmd команду), то все рівно не відпрацьовує ProcOutputHandler. Тобто метод не викликається.

// Не працює (пустий екран, WaitForExit очікує закінчення процесу "вічно")
StreamWriter sw2 = process.StandardInput;

if (sw2.BaseStream.CanWrite)
{
    sw2.Write("time");
}
sw2.Flush();
process.WaitForExit();

// Працює, але один раз. Потрібно знову відкрити стрім 
using (StreamWriter sw = process.StandardInput)
{
    if (sw.BaseStream.CanWrite)
    {
        sw.Write("echo 'I called time'");
    }
}
process.WaitForExit();

process.Close();

Мені потрібно викликати декілька команд в одному процесі.

7 Востаннє редагувалося koala (18.11.2022 12:56:20)

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

Спершу виставте OutputDataRecieved, а потім запускайте процес.

А які саме команди треба запускати?

8 Востаннє редагувалося Betterthanyou (18.11.2022 14:56:19)

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

koala написав:

Спершу виставте OutputDataRecieved, а потім запускайте процес.

Нічого не змінилося. Метод не викликається OutputDataRecieved

koala написав:

А які саме команди треба запускати?

Що мені "реально" потрібно зробити
1) Відкрити docker.exe
2) Відкрити Unix shell в одному із контейнерів exec -i {0} bash | {0} - ID контейнера
3) Відкрити програму myapp в контейнері і оновити дані про крипто валюту  myapp download-data --exchange binance --pairs BTC/USDT
4) Якщо виникла помилка оновлення даних, почекати 1 хв і ще раз оновити myapp download-data --exchange binance --pairs BTC/USDT
5) Якщо оновлення пройшло успішно запустити бектест myapp backtest

Але я зробив приклад з CMD (для того щоб спростити для форумчан програму / щоб можна було її запустити без додаткових програм)
В цьому прикладі я хочу запустити будь-які CMD команди. Спочатку має запуститися команда перша, очікувати завершення свого виконання, і потім друга команда, і також очікувати завершення свого виконання. Наприклад time -> echo ""

Уточнення: Без надсилання символі "\n" або "\r". Тому що bash їх додає в кінець рядка '...BTC/USDT\r', а має бути '...BTC/USDT'

9

Re: Process | RedirectStandardInput чи можна перевідкрити потік ?

Коли ви в docker CLI/cmd/чому завгодно натискаєте Enter, то це і є отой \r чи \n (чи взагалі \r\n). Тобто без цього ви не передаєте команду на виконання, а лише вводите її.

Подякували: leofun011