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"}

 

posted @ 2016-03-15 10:54  土豆008  阅读(832)  评论(0编辑  收藏  举报