port 执行命令的封装和参数详解
下面代码摘自rebar_utils.erl
-module(tt7). %-export([start/0]). -compile(export_all). -define(FAIL, abort()). -define(ABORT(Str, Args), abort(Str, Args)). -define(CONSOLE(Str, Args), io:format(Str, Args)). -define(DEBUG(Str, Args), io:format("debug :" ++ Str, Args)). -define(INFO(Str, Args), io:format("info :" ++ Str, Args)). -define(WARN(Str, Args), io:format("warn :" ++ Str, Args)). -define(ERROR(Str, Args), io:format("standard_error error :" ++ Str, Args)). -define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))). sh(Command0, Options0) -> ?INFO("sh info:\n\tcwd: ~p\n\tcmd: ~s\n", [file:get_cwd(), Command0]), ?DEBUG("\topts: ~p\n", [Options0]), DefaultOptions = [{use_stdout, false}, abort_on_error], %% use_stdout表示是否输出cmd执行结果 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, [])), Command = Command0, PortSettings = proplists:get_all_values(port_settings, Options) ++ [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide], %%open_port的一些常用参数 ?DEBUG("Port Cmd: ~p\nPort Opts: ~p\n", [Command, PortSettings]), 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) -> %%接受port执行结果 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. %%处理return_on_error、abort_on_error、use_stdout、cd、env这几个参数的结果
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) -> ?CONSOLE("~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}. log_msg_and_abort(Message) -> fun(_Command, {_Rc, _Output}) -> ?ABORT(Message, []) end. 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]). abort() -> throw(execute_abort). abort(String, Args) -> ?ERROR(String, Args), abort().
这样就能用port方法执行一些shell命令
执行如下
1 11> tt7:sh("/bin/echo this is a $name", [{env,[{"name", "tanwen"}]}]). 2 info :sh info: 3 cwd: {ok,"/home/erlang/fortest"} 4 cmd: /bin/echo this is a $name 5 debug : opts: [{env,[{"name","tanwen"}]}] 6 debug :Port Cmd: "/bin/echo this is a $name" 7 Port Opts: [{env,[{"name","tanwen"}]}, 8 exit_status, 9 {line,16384}, 10 use_stdio,stderr_to_stdout,hide] 11 {ok,"this is a tanwen\n"} %%没有输出默认的cmd结果,这个是port的打印
去掉调试,修改DefaultOptions = [{use_stdout, true}, abort_on_error]
1 13> tt7:sh("/bin/echo this is a $name", [{env,[{"name", "tanwen"}]}]). 2 this is a tanwen %%这个是{use_stdout, true}的输出 3 {ok,"this is a tanwen\n"}