модуль для запуска консольных программ и получения их вывода

Здесь выкладываются готовые к использованию макросы и скрипты.
User avatar
zg
zero-gravity
Posts: 5302
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 47 times
Contact:

Re: модуль для запуска консольных программ и получения их вы

Post by zg » Tue 10 Jun, 2014 16:18

Latest post of the previous page:

John Doe wrote:В таком случае в чём преимущество обсуждаемой тут реализации Execute перед стандартной io.popen?
как у io.popen задать текущую директорию для запускаемого процесса?

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

Re: модуль для запуска консольных программ и получения их вы

Post by John Doe » Tue 10 Jun, 2014 16:40

zg wrote:
John Doe wrote:В таком случае в чём преимущество обсуждаемой тут реализации Execute перед стандартной io.popen?
как у io.popen задать текущую директорию для запускаемого процесса?
Предварительно вызвать SetCurrentDirectory (через ffi).
Или cd DIR & COMMAND.

И я не утверждаю, что преимуществ нет, а наоборот, спрашиваю в чём они, т.к. эта информация должна быть в шапке.

User avatar
zg
zero-gravity
Posts: 5302
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 47 times
Contact:

Re: модуль для запуска консольных программ и получения их вы

Post by zg » Tue 10 Jun, 2014 16:47

John Doe wrote:Предварительно вызвать SetCurrentDirectory (через ffi).
Или cmd /c cd ... &
а текущий путь сетевой и в реестре нет DisableUNCCheck.

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

Re: модуль для запуска консольных программ и получения их вы

Post by John Doe » Tue 10 Jun, 2014 17:01

Не имею представления, как и многие другие пользователи, поэтому и прошу указать подробности в шапке.

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

Re: модуль для запуска консольных программ и получения их вы

Post by John Doe » Wed 11 Jun, 2014 07:33

Третий параметр Execute - функция-коллбек, которая будет вызываться на каждой полученной строке вывода запущенной программы.
Мне кажется для lua 5.x идиоматичнее было бы использовать итератор.

Update
Если использовать корутины, то требуемые изменения минимальны:
После этого функцией можно пользоваться например так:

Code: Select all

local output = Execute("cmd /cdir")
if output then
  for line in output do far.Show(line)
end
Update2
При таком использовании закрывать хэндлы надёжнее в финалайзере, как-то так:

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

Re: модуль для запуска консольных программ и получения их вы

Post by John Doe » Thu 12 Jun, 2014 11:41

zg wrote:
tstr wrote:При запуске через Execute "доходит" ли до процесса CtrlC/CtrlBreak ?
да.
Почему-то не доходит: Execute("cmd /cdir /s","c:\\",function()end)

User avatar
zg
zero-gravity
Posts: 5302
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 47 times
Contact:

Re: модуль для запуска консольных программ и получения их вы

Post by zg » Thu 12 Jun, 2014 12:48

John Doe wrote:Почему-то не доходит: Execute("cmd /cdir /s","c:\\",function()end)
у меня всё ок.

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

Re: модуль для запуска консольных программ и получения их вы

Post by John Doe » Thu 12 Jun, 2014 13:10

Windows XP x86, FAR 3.0 build 3951, запущеный командой C:\APPs\Far3\far.exe /p%FARHOME%\Plugins\LuaMacro /m.
Беру execute.lua из шапки, запускаю так: lua:loadfile"execute.lua"()("cmd /cdir /s","c:\\",function()end).

После этого фар надолго зависает (не ждал до конца), CtrlBreak/CtrlC не действуют.

Shmuel
Posts: 5229
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 16 times
Been thanked: 151 times

Re: модуль для запуска консольных программ и получения их вы

Post by Shmuel » Thu 12 Jun, 2014 19:12

John Doe wrote:фар надолго зависает (не ждал до конца), CtrlBreak/CtrlC не действуют.
Модуль execute не является причиной зависания. Если запустить
lflua -e"(require 'execute')('cmd /cdir /s','c:\\',function() end)",
то действуют и CtrlBreak, и CtrlC.

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

Re: модуль для запуска консольных программ и получения их вы

Post by John Doe » Thu 12 Jun, 2014 20:27

Windows 8.1 x86
CtrlBreak действует, CtrlC - нет.
При запуске через lflua работают оба сочетания.

buniak_a_h
Posts: 362
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 39 times
Been thanked: 5 times
Contact:

модуль для запуска консольных программ и получения их вывода

Post by buniak_a_h » Thu 07 Jan, 2016 20:26

Мне не хватало кода возврата. Тупым подбором (не зная языка) добился:
1. В начало процедуры function Execute (после определения pipe_security) добавить строку
local iRet=ffi.new("int[1]")
2. Перед строкой C.CloseHandle(info.hProcess) вставить строки:
if not C.GetExitCodeProcess(info.hProcess, iRet) then
iRet[0] = -1
end

3. В конец процедуры function Execute (непосредственно перед end) вставить строку:
return iRet[0]
--
Мне кажется, это было бы полезно не только мне...
Программировать просто. Знай, жми себе на кнопочки... © Владелец IT-компании

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

модуль для запуска консольных программ и получения их вывода

Post by John Doe » Thu 12 Oct, 2017 19:06

buniak_a_h wrote:
Thu 07 Jan, 2016 20:26
C.GetExitCodeProcess(info.hProcess, iRet)
Это не будет работать, если предварительно не определить прототип.

Code: Select all

ffi.cdef[[//https://msdn.microsoft.com/library/ms683189
BOOL WINAPI GetExitCodeProcess(
  HANDLE  hProcess,
  LPDWORD lpExitCode
);]]
buniak_a_h wrote:
Thu 07 Jan, 2016 20:26
if not C.GetExitCodeProcess(info.hProcess, iRet)
Сомневаюсь что это работает, ведь BOOL это int.
И записывать в переменную -1 не очень осмысленно.

Ещё надо учесть, что в момент выхода процесс может ещё не быть завершён, и в этом случае значимого результата мы не получим.
Если же результат действительно нужен, то стоит использовать WaitForSingleObject.
.

Smitis
Posts: 1489
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 14 times
Been thanked: 24 times
Contact:

модуль для запуска консольных программ и получения их вывода

Post by Smitis » Thu 12 Oct, 2017 20:47

John Doe,
Я пробовал давно, у меня с WaitForSingleObject для консольных программ не сраслось что-то (не помню уже).
Мой вариант модуля был такой (с кодом возврата)
  1. local ffi = require "ffi"
  2. local C = ffi.C
  3. ffi.cdef [[
  4.     typedef struct { // _SECURITY_ATTRIBUTES
  5.         unsigned long nLength;
  6.         void* lpSecurityDescriptor;
  7.         int32_t bInheritHandle;
  8.     } SECURITY_ATTRIBUTES;
  9.  
  10.     typedef struct { // _STARTUPINFOW
  11.         unsigned long cb;
  12.         wchar_t* lpReserved;
  13.         wchar_t* lpDesktop;
  14.         wchar_t* lpTitle;
  15.         unsigned long dwX;
  16.         unsigned long dwY;
  17.         unsigned long dwXSize;
  18.         unsigned long dwYSize;
  19.         unsigned long dwXCountChars;
  20.         unsigned long dwYCountChars;
  21.         unsigned long dwFillAttribute;
  22.         unsigned long dwFlags;
  23.         unsigned short wShowWindow;
  24.         unsigned short cbReserved2;
  25.         unsigned char* lpReserved2;
  26.         void* hStdInput;
  27.         void* hStdOutput;
  28.         void* hStdError;
  29.     } STARTUPINFOW;
  30.  
  31.     typedef struct { // _PROCESS_INFORMATION
  32.         void* hProcess;
  33.         void* hThread;
  34.         unsigned long dwProcessId;
  35.         unsigned long dwThreadId;
  36.     } PROCESS_INFORMATION;
  37.  
  38.     int32_t CreatePipe(void** hReadPipe,void** hWritePipe,SECURITY_ATTRIBUTES* lpPipeAttributes,unsigned long nSize);
  39.     int32_t CreateProcessW(const wchar_t* lpApplicationName,wchar_t* lpCommandLine,SECURITY_ATTRIBUTES* lpProcessAttributes,SECURITY_ATTRIBUTES* lpThreadAttributes,int32_t bInheritHandles,unsigned long dwCreationFlags,void* lpEnvironment,const wchar_t* lpCurrentDirectory,STARTUPINFOW* lpStartupInfo,PROCESS_INFORMATION* lpProcessInformation); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
  40.     int32_t ReadFile(void* hFile,void* lpBuffer,unsigned long nNumberOfBytesToRead,unsigned long* lpNumberOfBytesRead,void* lpOverlapped);
  41.     int32_t CloseHandle(void* hObject);
  42.     void* GetStdHandle(unsigned long nStdHandle);
  43.     int32_t GetLastError(void);
  44.     bool GetExitCodeProcess(HANDLE hProcess, int32_t* lpExitCode); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683189(v=vs.85).aspx
  45.     int32_t WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
  46.  
  47.     int32_t IsTextUnicode(const void* lpv,int iSize,int* lpiResult);
  48. ]]
  49. local advapi32 = ffi.load("advapi32")
  50. local IS_TEXT_UNICODE_ASCII16 = 0x0001
  51. local IS_TEXT_UNICODE_STATISTICS = 0x0002
  52. local STILL_ACTIVE = 0x0103 -- = 259 = STATUS_PENDING in WinNT.h
  53.  
  54. local CREATE_NO_WINDOW = 0x8000000
  55. local STD_INPUT_HANDLE = -10
  56. local STARTF_USESTDHANDLES = 0x00000100
  57. local INFINITE = 0xFFFFFFFF
  58.  
  59. local function split(data,callback)
  60.     while true do
  61.         local s,f=string.find(data,"[\n\r]")
  62.         if s then
  63.             local line=string.sub(data,1,s-1)
  64.             callback(line)
  65.             if string.sub(data,s,f+1)=="\r\n" then f=f+1 end
  66.             data=string.sub(data,f+1)
  67.         else
  68.             break
  69.         end
  70.     end
  71.     return data
  72. end
  73.  
  74. local function Execute(command,folder,callback,...)
  75.     local exitcode = ffi.new("DWORD[1]")
  76.     local ret,err
  77.     local pipe_security=ffi.new("SECURITY_ATTRIBUTES")
  78.     callback = callback or function(...) panel.GetUserScreen() io.write(...) io.write("\n") panel.SetUserScreen() end
  79.     pipe_security.nLength=ffi.sizeof(pipe_security)
  80.     pipe_security.bInheritHandle=true
  81.     local pipe_in,pipe_out=ffi.new("void*[1]"),ffi.new("void*[1]")
  82.     if C.CreatePipe(pipe_in,pipe_out,pipe_security,0) then
  83.         local startup = ffi.new("STARTUPINFOW")
  84.         startup.cb = ffi.sizeof(startup)
  85.         startup.dwFlags = STARTF_USESTDHANDLES
  86.         startup.hStdInput = C.GetStdHandle(STD_INPUT_HANDLE)
  87.         startup.hStdOutput = pipe_out[0];
  88.         startup.hStdError = pipe_out[0];
  89.         local info = ffi.new("PROCESS_INFORMATION")
  90.         if C.CreateProcessW(
  91.                 ffi.NULL,
  92.                 command and ffi.cast("void*",win.Utf8ToUtf16(command.."\0")) or ffi.NULL,
  93.                 ffi.NULL,ffi.NULL,true,CREATE_NO_WINDOW,ffi.NULL,
  94.                 folder and ffi.cast("void*",win.Utf8ToUtf16(folder.."\0")) or ffi.NULL,
  95.                 startup,info)~=0 then
  96.             C.CloseHandle(pipe_out[0])
  97.             local size = 1024
  98.             local buffer = ffi.new("char[?]",size)
  99.             local readed = ffi.new("unsigned long[1]")
  100.             local unicode = ffi.new("int[1]")
  101.             unicode[0] = bit64.bor(IS_TEXT_UNICODE_ASCII16,IS_TEXT_UNICODE_STATISTICS)
  102.             local output = ""
  103.             while true do
  104.                 C.ReadFile(pipe_in[0],buffer,size,readed,ffi.NULL)
  105.                 if readed[0]==0 then
  106.                     break
  107.                 else
  108.                     output = output..(advapi32.IsTextUnicode(buffer,readed[0],unicode)==0 and win.OemToUtf8 or win.Utf16ToUtf8)(ffi.string(buffer,readed[0]))
  109.                     output = split(output,callback)
  110.                 end
  111.             end
  112.             if #output>0 then callback(output,...) end
  113.             --C.WaitForSingleObject(info.hProcess,INFINITE)
  114.             if C.GetExitCodeProcess(info.hProcess,exitcode) then
  115.                 ret,err = exitcode[0],nil
  116.             else
  117.                 ret,err = false,C.GetLastError()
  118.             end
  119.             C.CloseHandle(info.hThread)
  120.             C.CloseHandle(info.hProcess)
  121.         else
  122.             ret,err = nil,C.GetLastError()
  123.         end
  124.         C.CloseHandle(pipe_in[0])
  125.     end
  126.     return ret,err
  127. end
  128.  
  129. return Execute
Last edited by Smitis on Thu 12 Oct, 2017 20:49, edited 1 time in total.
Разум когда-нибудь победит.

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

модуль для запуска консольных программ и получения их вывода

Post by John Doe » Fri 13 Oct, 2017 07:05

Smitis wrote:
Thu 12 Oct, 2017 20:47
у меня с WaitForSingleObject для консольных программ не сраслось что-то (не помню уже).
У меня ваш код работает.

buniak_a_h
Posts: 362
Joined: Sat 20 Apr, 2013 00:17
Location: Питер
Has thanked: 39 times
Been thanked: 5 times
Contact:

модуль для запуска консольных программ и получения их вывода

Post by buniak_a_h » Fri 13 Oct, 2017 07:14

John Doe wrote:
Thu 12 Oct, 2017 19:06
Это не будет работать, если предварительно не определить прототип.
Я не определял... Может, в каком-то другом макросе определяется?
Указанная последовательность у меня повешена на компилятор Харбора (язык такой, на xBase и интерпретацию заточенный...).
Код возврата анализируется; 0 - всё ок, окно компиляции закрывается. НЕ 0 - ошибка разного уровня; перехваченные сообщения об ошибках открываются в новом окне; по <Enter> происходи переход на ошибку (если файл не открыт, он открывается). Практически IDE на базе FARа. Естественно, т.к. делалось для себя, то никаких "красот" типа настроек нету. Если кого интересует - могу макрос приложить. В принципе, перенастроить его для запуска другого компилятора, наверно, несложно.

User avatar
John Doe
Бюрократ
Posts: 11395
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 17 times
Been thanked: 101 times

модуль для запуска консольных программ и получения их вывода

Post by John Doe » Fri 13 Oct, 2017 07:46

buniak_a_h wrote:
Fri 13 Oct, 2017 07:14
Я не определял... Может, в каком-то другом макросе определяется?
Очевидно.
buniak_a_h wrote:
Fri 13 Oct, 2017 07:14
Код возврата анализируется
В вашем случае всё работает (и я подозреваю что в большинстве других случаев тоже будет), но если код возврата важен, то надо всё-таки следовать документации.
buniak_a_h wrote:
Fri 13 Oct, 2017 07:14
Если кого интересует - могу макрос приложить.
Если будете выкладывать, то не в этой теме.

Smitis
Posts: 1489
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 14 times
Been thanked: 24 times
Contact:

модуль для запуска консольных программ и получения их вывода

Post by Smitis » Fri 13 Oct, 2017 19:02

John Doe wrote:
Fri 13 Oct, 2017 07:05
У меня ваш код работает.
Разумеется он работает, я его использую. Только строка c WaitForSingleObject у меня закомментирована. Насколько я помню, я так и не разобрался тогда, нужна ли эта проверка для консольных программ или нет.
Разум когда-нибудь победит.

Post Reply

Return to “Полезные макросы и скрипты”