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

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

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

Post by zg »

сам модуль:
execute_v2.7z
(2.32 KiB) Downloaded 543 times
пример, как можно скомпилировать программу на c++ при помощи gcc и если есть ошибки — перейти к месту их обнаружения:

 ! Message from: John Doe
Альтернативная версия модуля, с дополнительными функциями, см. тут: viewtopic.php?p=167895#p167895
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Latest post of the previous page:

Shmuel wrote:Можно взглянуть на пример "неюникодности" io.popen в LuaFAR?
Был не прав, перепутал с функциями os.*
В таком случае в чём преимущество обсуждаемой тут реализации Execute перед стандартной io.popen?
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

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

Post by zg »

John Doe wrote:В таком случае в чём преимущество обсуждаемой тут реализации Execute перед стандартной io.popen?
как у io.popen задать текущую директорию для запускаемого процесса?
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

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

И я не утверждаю, что преимуществ нет, а наоборот, спрашиваю в чём они, т.к. эта информация должна быть в шапке.
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

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

Post by zg »

John Doe wrote:Предварительно вызвать SetCurrentDirectory (через ffi).
Или cmd /c cd ... &
а текущий путь сетевой и в реестре нет DisableUNCCheck.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Не имею представления, как и многие другие пользователи, поэтому и прошу указать подробности в шапке.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Третий параметр 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: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

zg wrote:
tstr wrote:При запуске через Execute "доходит" ли до процесса CtrlC/CtrlBreak ?
да.
Почему-то не доходит: Execute("cmd /cdir /s","c:\\",function()end)
User avatar
zg
zero-gravity
Posts: 6015
Joined: Tue 15 Mar, 2005 17:36
Location: riga
Been thanked: 185 times
Contact:

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

Post by zg »

John Doe wrote:Почему-то не доходит: Execute("cmd /cdir /s","c:\\",function()end)
у меня всё ок.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

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: 6815
Joined: Thu 23 Mar, 2006 21:36
Location: Israel
Has thanked: 41 times
Been thanked: 526 times

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

Post by Shmuel »

John Doe wrote:фар надолго зависает (не ждал до конца), CtrlBreak/CtrlC не действуют.
Модуль execute не является причиной зависания. Если запустить
lflua -e"(require 'execute')('cmd /cdir /s','c:\\',function() end)",
то действуют и CtrlBreak, и CtrlC.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Windows 8.1 x86
CtrlBreak действует, CtrlC - нет.
При запуске через lflua работают оба сочетания.
User avatar
buniak_a_h
Posts: 4234
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 642 times
Contact:

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

Post by buniak_a_h »

Мне не хватало кода возврата. Тупым подбором (не зная языка) добился:
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: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

buniak_a_h wrote: Thu 07 Jan, 2016 20:26C.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:26if not C.GetExitCodeProcess(info.hProcess, iRet)
Сомневаюсь что это работает, ведь BOOL это int.
И записывать в переменную -1 не очень осмысленно.

Ещё надо учесть, что в момент выхода процесс может ещё не быть завершён, и в этом случае значимого результата мы не получим.
Если же результат действительно нужен, то стоит использовать WaitForSingleObject.
.
User avatar
Smitis
Posts: 1709
Joined: Fri 18 Mar, 2005 12:51
Location: Питер
Has thanked: 23 times
Been thanked: 55 times

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

Post by Smitis »

John Doe,
Я пробовал давно, у меня с WaitForSingleObject для консольных программ не сраслось что-то (не помню уже).
Мой вариант модуля был такой (с кодом возврата)
  1. local ffi = require "ffi"
  2.  
  3. local C = ffi.C
  4.  
  5. ffi.cdef [[
  6.  
  7.     typedef struct { // _SECURITY_ATTRIBUTES
  8.  
  9.         unsigned long nLength;
  10.  
  11.         void* lpSecurityDescriptor;
  12.  
  13.         int32_t bInheritHandle;
  14.  
  15.     } SECURITY_ATTRIBUTES;
  16.  
  17.  
  18.  
  19.     typedef struct { // _STARTUPINFOW
  20.  
  21.         unsigned long cb;
  22.  
  23.         wchar_t* lpReserved;
  24.  
  25.         wchar_t* lpDesktop;
  26.  
  27.         wchar_t* lpTitle;
  28.  
  29.         unsigned long dwX;
  30.  
  31.         unsigned long dwY;
  32.  
  33.         unsigned long dwXSize;
  34.  
  35.         unsigned long dwYSize;
  36.  
  37.         unsigned long dwXCountChars;
  38.  
  39.         unsigned long dwYCountChars;
  40.  
  41.         unsigned long dwFillAttribute;
  42.  
  43.         unsigned long dwFlags;
  44.  
  45.         unsigned short wShowWindow;
  46.  
  47.         unsigned short cbReserved2;
  48.  
  49.         unsigned char* lpReserved2;
  50.  
  51.         void* hStdInput;
  52.  
  53.         void* hStdOutput;
  54.  
  55.         void* hStdError;
  56.  
  57.     } STARTUPINFOW;
  58.  
  59.  
  60.  
  61.     typedef struct { // _PROCESS_INFORMATION
  62.  
  63.         void* hProcess;
  64.  
  65.         void* hThread;
  66.  
  67.         unsigned long dwProcessId;
  68.  
  69.         unsigned long dwThreadId;
  70.  
  71.     } PROCESS_INFORMATION;
  72.  
  73.  
  74.  
  75.     int32_t CreatePipe(void** hReadPipe,void** hWritePipe,SECURITY_ATTRIBUTES* lpPipeAttributes,unsigned long nSize);
  76.  
  77.     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
  78.  
  79.     int32_t ReadFile(void* hFile,void* lpBuffer,unsigned long nNumberOfBytesToRead,unsigned long* lpNumberOfBytesRead,void* lpOverlapped);
  80.  
  81.     int32_t CloseHandle(void* hObject);
  82.  
  83.     void* GetStdHandle(unsigned long nStdHandle);
  84.  
  85.     int32_t GetLastError(void);
  86.  
  87.     bool GetExitCodeProcess(HANDLE hProcess, int32_t* lpExitCode); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms683189(v=vs.85).aspx
  88.  
  89.     int32_t WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds); // https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx
  90.  
  91.  
  92.  
  93.     int32_t IsTextUnicode(const void* lpv,int iSize,int* lpiResult);
  94.  
  95. ]]
  96.  
  97. local advapi32 = ffi.load("advapi32")
  98.  
  99. local IS_TEXT_UNICODE_ASCII16 = 0x0001
  100.  
  101. local IS_TEXT_UNICODE_STATISTICS = 0x0002
  102.  
  103. local STILL_ACTIVE = 0x0103 -- = 259 = STATUS_PENDING in WinNT.h
  104.  
  105.  
  106.  
  107. local CREATE_NO_WINDOW = 0x8000000
  108.  
  109. local STD_INPUT_HANDLE = -10
  110.  
  111. local STARTF_USESTDHANDLES = 0x00000100
  112.  
  113. local INFINITE = 0xFFFFFFFF
  114.  
  115.  
  116.  
  117. local function split(data,callback)
  118.  
  119.     while true do
  120.  
  121.         local s,f=string.find(data,"[\n\r]")
  122.  
  123.         if s then
  124.  
  125.             local line=string.sub(data,1,s-1)
  126.  
  127.             callback(line)
  128.  
  129.             if string.sub(data,s,f+1)=="\r\n" then f=f+1 end
  130.  
  131.             data=string.sub(data,f+1)
  132.  
  133.         else
  134.  
  135.             break
  136.  
  137.         end
  138.  
  139.     end
  140.  
  141.     return data
  142.  
  143. end
  144.  
  145.  
  146.  
  147. local function Execute(command,folder,callback,...)
  148.  
  149.     local exitcode = ffi.new("DWORD[1]")
  150.  
  151.     local ret,err
  152.  
  153.     local pipe_security=ffi.new("SECURITY_ATTRIBUTES")
  154.  
  155.     callback = callback or function(...) panel.GetUserScreen() io.write(...) io.write("\n") panel.SetUserScreen() end
  156.  
  157.     pipe_security.nLength=ffi.sizeof(pipe_security)
  158.  
  159.     pipe_security.bInheritHandle=true
  160.  
  161.     local pipe_in,pipe_out=ffi.new("void*[1]"),ffi.new("void*[1]")
  162.  
  163.     if C.CreatePipe(pipe_in,pipe_out,pipe_security,0) then
  164.  
  165.         local startup = ffi.new("STARTUPINFOW")
  166.  
  167.         startup.cb = ffi.sizeof(startup)
  168.  
  169.         startup.dwFlags = STARTF_USESTDHANDLES
  170.  
  171.         startup.hStdInput = C.GetStdHandle(STD_INPUT_HANDLE)
  172.  
  173.         startup.hStdOutput = pipe_out[0];
  174.  
  175.         startup.hStdError = pipe_out[0];
  176.  
  177.         local info = ffi.new("PROCESS_INFORMATION")
  178.  
  179.         if C.CreateProcessW(
  180.  
  181.                 ffi.NULL,
  182.  
  183.                 command and ffi.cast("void*",win.Utf8ToUtf16(command.."\0")) or ffi.NULL,
  184.  
  185.                 ffi.NULL,ffi.NULL,true,CREATE_NO_WINDOW,ffi.NULL,
  186.  
  187.                 folder and ffi.cast("void*",win.Utf8ToUtf16(folder.."\0")) or ffi.NULL,
  188.  
  189.                 startup,info)~=0 then
  190.  
  191.             C.CloseHandle(pipe_out[0])
  192.  
  193.             local size = 1024
  194.  
  195.             local buffer = ffi.new("char[?]",size)
  196.  
  197.             local readed = ffi.new("unsigned long[1]")
  198.  
  199.             local unicode = ffi.new("int[1]")
  200.  
  201.             unicode[0] = bit64.bor(IS_TEXT_UNICODE_ASCII16,IS_TEXT_UNICODE_STATISTICS)
  202.  
  203.             local output = ""
  204.  
  205.             while true do
  206.  
  207.                 C.ReadFile(pipe_in[0],buffer,size,readed,ffi.NULL)
  208.  
  209.                 if readed[0]==0 then
  210.  
  211.                     break
  212.  
  213.                 else
  214.  
  215.                     output = output..(advapi32.IsTextUnicode(buffer,readed[0],unicode)==0 and win.OemToUtf8 or win.Utf16ToUtf8)(ffi.string(buffer,readed[0]))
  216.  
  217.                     output = split(output,callback)
  218.  
  219.                 end
  220.  
  221.             end
  222.  
  223.             if #output>0 then callback(output,...) end
  224.  
  225.             --C.WaitForSingleObject(info.hProcess,INFINITE)
  226.  
  227.             if C.GetExitCodeProcess(info.hProcess,exitcode) then
  228.  
  229.                 ret,err = exitcode[0],nil
  230.  
  231.             else
  232.  
  233.                 ret,err = false,C.GetLastError()
  234.  
  235.             end
  236.  
  237.             C.CloseHandle(info.hThread)
  238.  
  239.             C.CloseHandle(info.hProcess)
  240.  
  241.         else
  242.  
  243.             ret,err = nil,C.GetLastError()
  244.  
  245.         end
  246.  
  247.         C.CloseHandle(pipe_in[0])
  248.  
  249.     end
  250.  
  251.     return ret,err
  252.  
  253. end
  254.  
  255.  
  256.  
  257. return Execute
Last edited by Smitis on Thu 12 Oct, 2017 20:49, edited 1 time in total.
Разум когда-нибудь победит.
User avatar
John Doe
Бюрократ
Posts: 13801
Joined: Wed 27 Apr, 2005 20:42
Has thanked: 72 times
Been thanked: 425 times
Contact:

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

Post by John Doe »

Smitis wrote: Thu 12 Oct, 2017 20:47 у меня с WaitForSingleObject для консольных программ не сраслось что-то (не помню уже).
У меня ваш код работает.
User avatar
buniak_a_h
Posts: 4234
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 642 times
Contact:

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

Post by buniak_a_h »

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

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