The following simple Erlang netcat client demonstrates the communication between Erlang program and external OS process. The rc.erl start a netcat client to connect to a netcat server, and send some message to the server at a given time.
-
Create Erlang server;
-module(rc). -export([cmd/1]). cmd(Cmd) -> Opt = [stream, exit_status, stderr_to_stdout, eof], P = open_port({spawn, Cmd}, Opt), get_data(P, [], 0). get_data(P, Sofar, 3) -> port_command(P, "abcd"), get_data(P, Sofar, 4); get_data(P, Sofar, Cnt) -> receive {P, {data, Bin}} -> io:format("rec Bin: ~p, Cnt=~p~n", [Bin, Cnt]), get_data(P, [Bin|Sofar], Cnt+1); {P, eof} -> port_close(P), receive {P, {exit_status, N}} -> {N, lists:reverse(Sofar)} end end.
-
Start netcat server: run command
nc -l 3333
at host 10.21.3.31; -
Start netcat client:
$ erl ...
1> c(rc).
{ok,rc} 2> rc:cmd("nc 10.21.3.31 3333").
-
Send message from server to client: press "1
2 3 4 ^C", client output as follows: rec Bin: "1\n", Cnt=0 rec Bin: "2\n", Cnt=1 rec Bin: "3\n", Cnt=2 rec Bin: "4\n", Cnt=4 {0,["1\n","2\n","3\n","4\n"]} 3>
When you press
The pattern of Erlang-Shell communication is:
-
Use "open_port" to start a external shell process;
-
Use "receive" clause to receive output of the process;
-
Use "port_command" to send message to process;
-
Use "port_close" to close the shell;
Notes:
-
For non-interactive shell command, os:cmd/1 is a better choice;
-
If you want to catch the event that the peer close actively, add "eof" into option list of open_port; If you want to catch the exit value of the shell process, add "exit_status" to option list of open_port;
-
Not specify "in" in option list of open_port when you want to send the process some message, or a "ebadf" exception arises, because "in" means this process only used for input, and "ebadf" means the file is not open for reading or writing.
-
Erlang port communicate with python is a good demonstration of erlang calling a python script; Chapter 12 of "Programming Erlang" also concentrate Erlang interfacing techniques.