Current section

Files

Jump to
exe src sh.erl
Raw

src/sh.erl

-module(sh).
-behaviour(supervisor).
-behaviour(application).
-export([init/1, start/2, stop/1]).
-export([ fdlink_executable/0, executable/1, oneliner/1, oneliner/2, sh_loop/2, sh_loop/3, sh_loop/4,
run/1, run/2, run/3, run/4, run/5 ]). % fold this
start(_StartType, _StartArgs) -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
stop(_State) -> ok.
init([]) -> {ok, { {one_for_one, 5, 10}, []} }.
fdlink_executable() -> filename:absname(filename:join(code:priv_dir(sh), "fdlink")).
oneliner(C) -> run(C, ignoreeol, ".").
oneliner(C, Cwd) -> run(C, ignoreeol, Cwd).
run(C) -> run(C, binary, ".").
run(C, Log) -> run(C, Log, ".").
executable(C) ->
case filename:pathtype(C) of
absolute -> C;
relative -> case filename:split(C) of
[C] -> os:find_executable(C);
_ -> C end;
_ -> C
end.
run([C|Args], Log, Cwd) when is_list(C) -> run(executable(C), Args, Log, Cwd);
run(Command, Log, Cwd) when is_list(Command) -> run(executable("sh"), ["-c", Command], Log, Cwd).
run(Command, Args, ignoreeol, Cwd) ->
Port = erlang:open_port({spawn_executable, Command},
[stream, stderr_to_stdout, binary, exit_status,
{args, Args}, {cd, Cwd}, {line, 16384}]),
sh_loop(Port, fun({_, Chunk}, Acc) -> [Chunk|Acc] end, []);
run(Command, Args, binary, Cwd) -> run(Command, Args, binary, Cwd, []);
run(Command, Args, Log, Cwd) ->
{ok, File} = file:open(Log, [append, raw]),
file:write(File, [">>> ", ts(), " ", Command, " ", [[A, " "] || A <- Args], "\n"]),
Port = erlang:open_port({spawn_executable, Command},
[stream, stderr_to_stdout, binary, exit_status,
{args, Args}, {cd, Cwd}]),
{done, Status, _} = sh_loop(Port, fun(Chunk, _Acc) -> file:write(File, Chunk), [] end, []),
file:write(File, [">>> ", ts(), " exit status: ", integer_to_list(Status), "\n"]),
{done, Status, Log}.
run(Command, Args, _Log, Cwd, Env) ->
Port = erlang:open_port({spawn_executable, executable(Command)},
[stream, stderr_to_stdout, binary, exit_status,
{args, Args}, {cd, Cwd}, {env, Env}]),
sh_loop(Port, binary).
sh_loop(Port, binary) -> sh_loop(Port, fun(Chunk, Acc) -> [Chunk|Acc] end, []).
sh_loop(Port, Fun, Acc) when is_function(Fun) -> sh_loop(Port, Fun, Acc, fun erlang:iolist_to_binary/1).
sh_loop(Port, Fun, Acc, Flatten) when is_function(Fun) ->
receive
{Port, {data, {eol, Line}}} -> sh_loop(Port, Fun, Fun({eol, Line}, Acc), Flatten);
{Port, {data, {noeol, Line}}} -> sh_loop(Port, Fun, Fun({noeol, Line}, Acc), Flatten);
{Port, {data, Data}} -> sh_loop(Port, Fun, Fun(Data, Acc), Flatten);
{Port, {exit_status, Status}} -> {done, Status, Flatten(lists:reverse(Acc))}
end.
ts() ->
Ts = {{_Y,_M,_D},{_H,_Min,_S}} = calendar:now_to_datetime(os:timestamp()),
io_lib:format("~p", [Ts]).