erlang:shell处理

使用erlang运行shell命令,并获取返回值,shell命令同步返回可直接处理返回结果,异步返回加一步文本处理

处理sh命令部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
%%
%% 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}]s
%% 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) -> [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}) ->
?ERROR_MSG(Message)
end.

-spec log_and_abort(string(), {integer(), string()}) -> no_return().
log_and_abort(Command, {Rc, Output}) ->
?ERROR_MSG("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.

异步shell命令

1
-define(CMD(Id),  io_lib:format("cd ../test/x64/Debug/ && uic_demo_v4.exe 2458 ~s 1 ~p ~s 1 > info_~p.txt", [bc3b6612, 100, "china_dirty", Id])).

异步操作读取文件部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
%% @doc test
t(Id) ->
sh(?CMD(Id)),
Dir = lists:concat([?CONF_FILE_DIR, Id, ".txt"]),
Msg = file_op(Dir),
?ERROR_MSG("tss sdk return ~w", [Msg]).

%% @doc 文本处理
file_op(Dir) ->
Txt= read_n_lines(Dir),
TxtL = string:tokens(Txt,":"),
Fin = lists:last(TxtL),
Fin.

%% @doc 匹配文本数据并处理
read_n_lines(Filename) ->
{ok, FileDev} = file:open(Filename, [raw, read, read_ahead]),
Lines = do_read(FileDev),
file:close(FileDev),
Line = string:strip(Lines, right, $\n),
Line.

do_read(FileDev) ->
case file:read_line(FileDev) of
{ok, Line} ->
case string:str(Line, "msg:") of
0 -> do_read(FileDev);
_ -> Line
end;
eof ->
{error, no_msg}
end.