Erlang Port实现调用系统命令并逐行输出执行过程
2015-01-24 10:11
656 查看
最近在做一个通过WEB调用系统命令的工具,难点是如何获取执行过程,同时可以逐行输出?
想起以前有看到霸爷提到rebar中封装了类似的功能,于是从rebar_utils中整出了下面的东西,很好用。
(本文来自瑞仙的Erlang开发博客)
想起以前有看到霸爷提到rebar中封装了类似的功能,于是从rebar_utils中整出了下面的东西,很好用。
-module(sh_port). -export([sh/1, sh/2]). %% %% Options = [Option] -- defaults to [use_stdout, abort_on_error] %% Option = ErrorOption | OutputOption | {cd, string()} | {env, Env} %% ErrorOption = return_on_error | abort_on_error | {abort_on_error, string()} %% OutputOption = use_stdout | {use_stdout, bool()} %% Env = [{string(), Val}] %% Val = string() | false %% sh(Command) -> sh(Command, []). sh(Command0, Options0) -> DefaultOptions = [use_stdout, abort_on_error], Options = [expand_sh_flag(V) || V <- proplists:compact(Options0 ++ DefaultOptions)], ErrorHandler = proplists:get_value(error_handler, Options), OutputHandler = proplists:get_value(output_handler, Options), Command = patch_on_windows(Command0, proplists:get_value(env, Options, [])), PortSettings = proplists:get_all_values(port_settings, Options) ++ [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide], Port = open_port({spawn, Command}, PortSettings), case sh_loop(Port, OutputHandler, []) of {ok, _Output} = Ok -> Ok; {error, {_Rc, _Output}=Err} -> ErrorHandler(Command, Err) end. sh_loop(Port, Fun, Acc) -> receive {Port, {data, {eol, Line}}} -> sh_loop(Port, Fun, Fun(Line ++ "\n", Acc)); {Port, {data, {noeol, Line}}} -> sh_loop(Port, Fun, Fun(Line, Acc)); {Port, {exit_status, 0}} -> {ok, lists:flatten(lists:reverse(Acc))}; {Port, {exit_status, Rc}} -> {error, {Rc, lists:flatten(lists:reverse(Acc))}} end. expand_sh_flag(return_on_error) -> {error_handler, fun(_Command, Err) -> {error, Err} end}; expand_sh_flag({abort_on_error, Message}) -> {error_handler, log_msg_and_abort(Message)}; expand_sh_flag(abort_on_error) -> {error_handler, fun log_and_abort/2}; expand_sh_flag(use_stdout) -> {output_handler, fun(Line, Acc) -> io:format("~s", [Line]), [Line | Acc] end}; expand_sh_flag({use_stdout, false}) -> {output_handler, fun(Line, Acc) -> [Line | Acc] end}; expand_sh_flag({cd, _CdArg} = Cd) -> {port_settings, Cd}; expand_sh_flag({env, _EnvArg} = Env) -> {port_settings, Env}. %% We do the shell variable substitution ourselves on Windows and hope that the %% command doesn't use any other shell magic. patch_on_windows(Cmd, Env) -> case os:type() of {win32,nt} -> Cmd1 = "cmd /q /c " ++ lists:foldl(fun({Key, Value}, Acc) -> expand_env_variable(Acc, Key, Value) end, Cmd, Env), %% Remove left-over vars re:replace(Cmd1, "\\\$\\w+|\\\${\\w+}", "", [global, {return, list}]); _ -> Cmd end. -type err_handler() :: fun((string(), {integer(), string()}) -> no_return()). -spec log_msg_and_abort(string()) -> err_handler(). log_msg_and_abort(Message) -> fun(_Command, {_Rc, _Output}) -> abort(Message, []) end. -spec log_and_abort(string(), {integer(), string()}) -> no_return(). log_and_abort(Command, {Rc, Output}) -> abort("sh(~s)~n" "failed with return code ~w and the following output:~n" "~s~n", [Command, Rc, Output]). %% %% Given env. variable FOO we want to expand all references to %% it in InStr. References can have two forms: $FOO and ${FOO} %% The end of form $FOO is delimited with whitespace or eol %% expand_env_variable(InStr, VarName, RawVarValue) -> case string:chr(InStr, $$) of 0 -> %% No variables to expand InStr; _ -> ReOpts = [global, unicode, {return, list}], VarValue = re:replace(RawVarValue, "\\\\", "\\\\\\\\", ReOpts), %% Use a regex to match/replace: %% Given variable "FOO": match $FOO\s | $FOOeol | ${FOO} RegEx = io_lib:format("\\\$(~s(\\s|$)|{~s})", [VarName, VarName]), re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts) end. -spec abort() -> no_return(). abort() -> throw(rebar_abort). -spec abort(string(), [term()]) -> no_return(). abort(String, Args) -> io:format(String, Args), abort().
(本文来自瑞仙的Erlang开发博客)
相关文章推荐
- python调用os.system执行系统命令,中文输出显示乱码
- C++调用系统终端执行命令,将输出保存到文件中
- Java 调用系统命令,并打印出执行结果
- 关闭系统进程,以及如何调用cmd并执行命令
- linux0.11系统调用的执行过程是怎样的?
- c#关闭系统进程以及如何调用cmd并执行命令
- java 中调用window系统中的文件,或者执行命令(shell、.CMD、.EXE)并获取返回值(如果有的话)
- JAVA调用系统命令或可执行程序
- 数据库操作_连接SQL Server数据库示例;连接ACCESS数据库;连接到 Oracle 数据库示例;SqlCommand 执行SQL命令示例;SqlDataReader 读取数据示例;使用DataAdapter填充数据到DataSet;使用DataTable存储数据库表;将数据库数据填充到 XML 文件;10 使用带输入参数的存储过程;11 使用带输入、输出参数的存储过程示;12 获得数据库中表的数目和名称;13 保存图片到SQL Server数据库示例;14 获得插入记录标识号;Exce
- 用perl调用系统命令来实现IPC$远程猜解(Reship)
- 关闭系统进程,以及如何调用CMD并执行命令
- linux系统下,c++程序,调用system命令失败,分析过程
- linux下使用系统调用编程实现copy命令功能
- Linux 调用系统命令并截获标准输出(stdout)和错误输出(stderr)
- 调用系统命令实现删除文件的操作,java调用系统命令实现删除文件的操作
- [转]数据库存储过程中利用extproc调用共享程序库/动态链接库,间接可以实现调用操作系统命令
- 关闭系统进程,以及如何调用cmd并执行命令
- c#关闭系统进程以及如何调用cmd并执行命令
- [转]数据库存储过程中利用extproc调用共享程序库/动态链接库,间接可以实现调用操作系统命令 - 雪山之巅的阳光 - 博客园
- C# WINForm 如何关闭系统进程,调用cmd并执行命令(隐藏CMD窗口) - 赤色火焰 - 京华志