-module(cmd). -type atom_opt() :: output | return_code. -type cmd_opt() :: {mode, atom_opt()} | {max_size, non_neg_integer()}. -type atom_or_proplist() :: atom_opt() | list(cmd_opt()). %% API exports -export([run/1, run/2]). %%==================================================================== %% API functions %%==================================================================== run(Command) -> do_run(Command, [{mode, output}]). run(Command, Arg) when is_integer(Arg) -> do_run(Command, [{mode, output}, {max_size, Arg}]); run(Command, Arg) -> do_run(Command, Arg). %%==================================================================== %% Internal functions %%==================================================================== -spec run(Command :: string(), Option :: atom_or_proplist()) -> string() | integer(). do_run(Command, output) -> os:cmd(Command); do_run(Command, [{mode, output}]) -> os:cmd(Command); do_run(Command, [{mode, output}, {max_size, Arg}]) -> os:cmd(Command, #{max_size => Arg}); do_run(Command, return_code) -> Res = os:cmd(Command ++ "\nRET_CODE=$?\necho \"\n$RET_CODE\""), [[], RetCode | _Rest] = lists:reverse(string:split(Res, "\n", all)), list_to_integer(RetCode). %%==================================================================== %% EUnit tests %%==================================================================== -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). -define(NUM_BYTES, 10000000). %% ~1MiB return_code_success_test() -> ?assertEqual(0, ?MODULE:run("ls", return_code)). return_code_failure_test() -> ?assertEqual(get_unix_error_code(), ?MODULE:run("cp /", return_code)). return_code_from_text_output_with_breakline_test() -> ?assertEqual(0, ?MODULE:run("echo \"..........1\"", return_code)). return_code_from_text_output_without_breakline_test() -> Command = "perl -e \"print '.' x 10;\"", ?assertEqual(0, ?MODULE:run(Command, return_code)). simple_output_test() -> String = "cmd can be useful!", Output = ?MODULE:run("echo \"" ++ String ++ "\""), ?assertEqual(String ++ "\n", Output). big_return_code_test_() -> {timeout, 120, fun() -> NumBytes = integer_to_list(?NUM_BYTES), Command = "perl -e \"print '.' x " ++ NumBytes ++ ";\"", ?assertEqual(0, ?MODULE:run(Command, return_code)) end}. big_output_test_() -> {timeout, 120, fun() -> NumBytes = integer_to_list(?NUM_BYTES), Command = "perl -e \"print '.' x " ++ NumBytes ++ ";\"", ?assertEqual(?NUM_BYTES, length(?MODULE:run(Command))) end}. big_truncated_output_test_() -> {timeout, 120, fun() -> NumBytes = integer_to_list(?NUM_BYTES), MaxOutputBytes = 256, Command = "perl -e \"print '.' x " ++ NumBytes ++ ";\"", Opts = [{mode, output}, {max_size, MaxOutputBytes}], ?assertEqual(MaxOutputBytes, length(?MODULE:run(Command, Opts))) end}. get_unix_error_code() -> case os:type() of {unix, darwin} -> 64; _ -> 1 end. -endif.