Что нового

Чтение потока вывода STDOUT и одновременный вывод в консоль

koros

Новичок
Сообщения
32
Репутация
1
Как известно в Autoit можно получить данные потока вывода STDOUT и записать их в переменную:
Код:
$run = Run($command, $sWorkingdir, $show_flag, $STDERR_MERGED)
Do
	Sleep(1)
	$return &= StdoutRead($run)
Until @error


При этом после применения $STDERR_MERGED в функции Run() в самой консоли отображается не поток STDOUT, а черное окно. Возможно ли одновременно получать STDOUT и в скрипт и в само окно консоли?
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
Данные из потока направляются либо в окно, либо в скрипт. В оба сразу не получится.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
Может есть какая-нибудь сторонняя утилита?

Или направлять поток в скрипт и сразу же выводить его в окно?
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
Нарисуй своё окно и выводи данные туда.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
Тут возникает такой вопрос: Предположим в качестве команды у меня задается xcopy и позникает вопрос перезаписи существующих файлов. Как отправить ввод с клавиатуры обратно в консоль?
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
У xcopy есть ключ перезаписывать без вопросов, можешь попробовать отправлять в процесс через StdinWrite.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
xcopy приведен для примера. И потом, например, может надо использовать xcopy без ключа, а для каждого файла решать надо ли перезаписывать или нет.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
koros
Работать с живой консолью довольно проблематично, т.к. сложно определить, когда она закончила вывод и ожидает ввод. Дополнительно возникают проблемы с остановкой цикличных процессов типа ping -t. А ещё cmd не выводит в поток некоторые ошибки, например "Непредвиденное появление: &."
Код:
$GUI = GUICreate("Console", 600, 500)
$Inp = GUICtrlCreateCombo("", 5, 5, 540, 20)
$Btn = GUICtrlCreateButton("Enter", 550, 5, 45, 20, 0x01) ; $BS_DEFPUSHBUTTON
$Edt = GUICtrlCreateEdit("", 5, 30, 590, 465, 0x200840) ; $ES_READONLY + $ES_AUTOVSCROLL + $WS_VSCROLL
GUICtrlSendMsg(-1, 0xC5, -1, 0) ; $EM_LIMITTEXT
GUICtrlSetFont(-1, 9, 0, 0, "Consolas")
GUISetState(@SW_SHOW)

$PID = Run(@ComSpec, "", @SW_HIDE, 9) ; $STDIN_CHILD + $STDERR_MERGED
Sleep(100)
StdoutRead($PID)

Do
  Switch GUIGetMsg()
    Case -3
      Exit
    Case $Btn
      $Line = GUICtrlRead($Inp)
      GUICtrlSetData($Inp, $Line, $Line)
      GUICtrlSetData($Edt, ">")
      StdinWrite($PID, DllCall('user32.dll', 'bool', 'CharToOem', 'str', $Line, 'str', $Line)[2] & @CRLF)
      Do
        Sleep(100)
        $Out = StdoutRead($PID)
        GUICtrlSetData($Edt, DllCall('user32.dll', 'bool', 'OemToChar', 'str', $Out, 'str', $Out)[2], 1)
        If Not ProcessExists($PID) Or GUIGetMsg() = -3 Then Exit
      Until StringRight($Out, 1) = ">" Or StringRight($Out, 2) = "? "
    EndSwitch
Until 0
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
InnI
Спасибо за помощь.
У меня не ожидаются сложные моменты. Объясню поподробнее мою задачу. Мне это надо для Universal Extractor-а. В нем при распаковки различных архивов/инсталляторов используются различные консольный утилиты. Результаты их работы выводятся в текстовый файл, который потом анализируется или, при неудачной распаковке, просто открывается. Я хочу отказаться от использования такого файла - получать результаты работы этих консольных утилит в пременную/массив. Но может понадобиться какая-то реакция пользователя - ввести пароль, ответить на запрос о перезаписи файлов. При использовании текстового дебаг-файла для этого можно просто выводить консоль, а при использовании StdoutRead консоль - пустая.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
InnI
Не подскажите, можно ли в приведенном вами коде отказаться от поля ввода $Inp, а вводить команды непосредственно в поле отображения $Edt ?
Заранее спасибо.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
А как тогда сделать, чтобы команды в $Edt можно было бы вводить только после ">" или "? ", а не в любом месте этого поля - как это делается в обычной cmd?
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
koros
как тогда сделать, чтобы команды в $Edt можно было бы вводить только после ">" или "? "
Никак не сделать. В лучшем случае, можно отправить курсор в конец текста, примерно так
Код:
GUICtrlSetState($Edt, 256) ; $GUI_FOCUS
      ControlSend($GUI, "", GUICtrlGetHandle($Edt), "^{end}")
Но потом придётся весь этот текст парсить на предмет нахождения введённой команды, проверять ошибки, очищать или добавлять текст в зависимости от команды... Короче, с input проще и понятней. А ещё проще - работать в самой консоли.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
InnI сказал(а):
Короче, с input проще и понятней.
Жаль. Подскажите тогда, пожалуйста, как правильно сделать так, чтобы в окне $Edt отображалось динамическое выполнение "долгой" команды (например распаковка большого архива или копирование большого количества файлов) и при этом до появления ">" или "? " поле $Inp было бы неактивным.
Я хотел бы через $PID = Run(...) запускать требуемую мне команду (распаковку архива или инсталлятора), наблюдать в $Edt за ходом её выполнения и затем, при необходимости, иметь возможность что-либо ввести в комстроку.
Заранее спасибо.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
С активированием/деактивированием понятно.

Не могу разобраться с отображением "динамики" выполнения команды. Я создал достаточно большой архив 1.7z и прописал:
Код:
$PID = Run(@comspec & ' /d /k g:\UniExtract\UniExtractsetup\bin\7z.exe  x g:\UniExtract\1.7z', "g:\UniExtract\1\", @SW_HIDE, 9)

Но в $Edt не отображается ход распаковки архива.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
koros
не отображается ход распаковки архива
Потому что это уже не консоль cmd, которая вам сама предоставляет информацию, а настоящая консоль Windows, у которой эту информацию ещё нужно уметь забирать. Вот, изучайте (как раз чтение вывода 7zip) https://www.autoitscript.com/forum/topic/91283-_7zread-udf/
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
InnI
Спасибо. Разрешите ещё вас помучать :smile:

Посмотрел я вывод 7-zip по вашей ссылке - запустил скрипт ничего не меняя. В консольном окне выводится ход распаковки, в окне скрипта не выводится. То есть если скрытоь консоль, то при большом архиве можно предположить, что все зависло. Насколько я понял вывод из консоли в скрипт там организован через DllCall('Kernel32.dll'...... Не подскажите, где можно было бы прочитать об этом?

Если же вернуться к скрипту, который вы мне любезно предложили в начале топика, я его немного изменил и попробовал извлеч из rar-архива:
Код:
$GUI = GUICreate("Console", 600, 500)
$Inp = GUICtrlCreateCombo("", 5, 5, 540, 20)
$Btn = GUICtrlCreateButton("Enter", 550, 5, 45, 20, 0x01) ; $BS_DEFPUSHBUTTON
$Edt = GUICtrlCreateEdit("", 5, 30, 590, 465, 0x200040) ; $ES_READONLY + $ES_AUTOVSCROLL + $WS_VSCROLL
GUICtrlSendMsg(-1, 0xC5, -1, 0) ; $EM_LIMITTEXT
GUICtrlSetFont(-1, 9, 0, 0, "Consolas")
GUISetState(@SW_SHOW)
local $Out
$PID = Run(@comspec & ' /d /k "c:\Program Files\WinRAR\UnRAR.exe"  x g:\1.rar', "g:\1\", @SW_HIDE, 9) ; $STDIN_CHILD + $STDERR_MERGED
Sleep(100)
$Out = StdoutRead($PID)

Do
  Switch GUIGetMsg()
    Case -3
      Exit
    Case $Btn
      $Line = GUICtrlRead($Inp)
      GUICtrlSetData($Inp, $Line, $Line)
      GUICtrlSetData($Edt, ">")
      StdinWrite($PID, DllCall('user32.dll', 'bool', 'CharToOem', 'str', $Line, 'str', $Line)[2] & @CRLF)
      Do
        Sleep(100)
        $Out &= StdoutRead($PID)
        GUICtrlSetData($Edt, DllCall('user32.dll', 'bool', 'OemToChar', 'str', $Out, 'str', $Out)[2], 1)
        If Not ProcessExists($PID) Or GUIGetMsg() = -3 Then Exit
      Until StringRight($Out, 1) = ">" Or StringRight($Out, 2) = "? "
    EndSwitch
Until 0


Скрипт работает, но чтобы начался отображаться процесс распаковки в $Edt необходимо в начале нажать кнопку "Enter". Помимо этого в строке распаковки каждого файла отображаются квадратики (наверное это символы перевода строки и их в принципе можно повырезать их переменной $Line) и, если извлекаемый файл большой, то отображаются несколько значений процентов - тут я не знаю, что можно поделать...
Если вас не затруднит, то может быть получиться довести скрипт до ума...
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
koros
Если вас не затруднит
Совершенно нет желания ковыряться в выводах консольных программ. Я вообще считаю, что подменять консоль собственным GUI проблематично и непрактично. И к этому выводу я пришёл, когда делал ради интереса этот самый скрипт. Возни много - толку ноль.
 
Автор
K

koros

Новичок
Сообщения
32
Репутация
1
Жаль... Не буду спорить по поводу нужности... Мне нужно в общем-то не подменять консоль собственным GUI, а просто получить в скрипт вывод консоли не через лог-файл, а напрямую. Еслиб можно было бы отображать консоль и одновременно получать данные STDOUT в скрипт, то было бы замечательно...
Может подскажите, где можно было бы почитать об DllCall('Kernel32.dll'...... ? Может через это можно сделать мою хотелку?
 
Верх