Echo with new line from fun() - erlang

I've recently been trying to get to grips with Erlang, but am slightly confused by this...
Problem
I am trying to write a function object in the shell, which receives any message and echoes it back to the shell followed by a new line.
Attempt
My attempt at the function is:
Echo = fun() -> receive Any -> io:fwrite(Any), io:fwrite("~n") end end.
However, if I spawn a new process for this...
someNode#someHost 2> Pid = spawn(Echo).
<0,76,0>
...and then send it a message...
someNode#someHost 3> Pid ! "Hello".
Hello"Hello"
...I don't seem to get a new line character after the write, and before the message return.
Question
Is there something wrong with my approach, that stops this (very simple) example working as expected?
Thanks in advance

Your problem is atomicity.
After printing the first line, the "main thread" gets scheduled and prints the result of Pid ! Msg, i.e. Msg.
io:fwrite can take arguments just like printf in C:
Echo = fun() ->
receive
Any ->
io:fwrite("~p~n", [Any])
end
end

Related

How to execute system command in Erlang and get results using os:cmd/1?

When I try to execute following command that returns error or doesn't exit on Windows - I always get empty list instead of error returned as string so for example:
I get:
[] = os:cmd("blah").
instead of something like
"command not found" = os:cmd("blah").
In linux - everything works as expected so I get "/bin/sh: line 1: blah: command not found\n"
Therefore I cannot rely on that function when I need to know how execution finished etc.
Please suggest some general way how to execute command and get results including error code.
Thanks!
I am not familiar with windows at all, but I'm sure, you should look this. This is implementation os:cmd/1 function.
There is problem with os:cmd/1. This function doesn't let you know, was command execution successful or not, so you only have to rely on certain command shell behaviour (which is platform dependent).
I'd recommend you to use erlang:open_port/2 function. Something like that:
my_exec(Command) ->
Port = open_port({spawn, Command}, [stream, in, eof, hide, exit_status]),
get_data(Port, []).
get_data(Port, Sofar) ->
receive
{Port, {data, Bytes}} ->
get_data(Port, [Sofar|Bytes]);
{Port, eof} ->
Port ! {self(), close},
receive
{Port, closed} ->
true
end,
receive
{'EXIT', Port, _} ->
ok
after 1 -> % force context switch
ok
end,
ExitCode =
receive
{Port, {exit_status, Code}} ->
Code
end,
{ExitCode, lists:flatten(Sofar)}
end.
So function my_exec/1 will return process exit code along with process stdout.

Erlang spawn simple process from erl .. no such process or port

When running this code in the Erlang console
Pid = spawn(fun() -> "foo" end),link(Pid),receive X -> X end.
I receive the following error.
** exception error: no such process or port
in function link/1
called as link(<0.71.0>)```
This happens because the process you spawn finishes very quickly: it only "returns" a string (and the return value goes nowhere, since it is the top-level function in the call stack of the new process), so it's very likely to finish before the emulator gets to the link call.
You can make it more likely to succeed by making the process sleep before exiting:
2> Pid = spawn(fun() -> timer:sleep(1000), "foo" end),link(Pid).
true
Note however that the receive expression in your example most likely won't receive anything, since the spawned process doesn't send any message, and the link won't generate any message either since the process exits normally, and the calling process most likely isn't trapping exits. You may want to do something like:
Parent = self(),
spawn(fun() -> Parent ! "foo" end),
receive X -> X end.
That returns "foo".

Run a command in erlang with a parameter

I'm trying to write a erlang module for ejabberd and can't quite work out the syntax.
What I'm trying to do is when a user sends a off-line message, run a script I've written.
on_offline(_From, To, _Packet) ->
?INFO_MSG("recieved offline packet to ~p", [To]),
?INFO_MSG("Got offline message", []),
osReturn = os:cmd("/opt/ejabberd-2.1.10/newmods/handleoffline.py " ++ To),
?INFO_MSG("Send to handler and got return ~s", [osReturn]),
none.
However when I run it, I get a few errors, if I have ++ To there I get the error
E(<0.423.0>:ejabberd_hooks:294) : {function_clause,
However even if I run it without the ++ I get another error telling me Bad Match.
All I'd like to do is run handleoffline.py with the first part of the user ID, anyone got any suggestions?
From the little information you have provided and assuming that your functions is being called with the ejabberd offline_message_hook I can at least find a problem.
In
osReturn = os:cmd("/opt/ejabberd-2.1.10/newmods/handleoffline.py " ++ To)
you are trying to use the ++ to concatenate the list you provided with To. But the major problem is that To isn't a string (list), and that generates your error. Your function is in the form of
on_offline(From, To, Packet) -> ok
where
To = From = #jid (see jlib)
one way to correct that would be to use
on_offline(_From, To=#jid{user = User, server=Server}, _Packet) ->
...
OsReturn = os:cmd("/opt/ejabberd-2.1.10/newmods/handleoffline.py " ++ User),
?INFO_MSG("Send to handler and got return ~s", [OsReturn]),
none.
because the field user on #jid is a string.
Edit: another error is that you need to chang osReturn to OsReturn because the first is an atom and will raise an error on your code, like selle pointed out
Edit your question and add more information if you have more problems, or to make this one clearer.

Erlang: Why can't I register self()?

This line fails with a badarg exception:
register(myproc, self()),
The documentation says that self/0 returns a pid and that register/2 takes a pid. So what gives?
Edit: No, seriously, it's not already registered, it's not a reserved atom, and it works when I register it from the process that's spawning it.
Oh weird! Okay, I got some more clues. When I move the call to register() around to different places, sometimes it works and sometimes it breaks. Here's my sample code. Run it before you call me crazy. :-)
-module(pingpong).
-export([start/1, ping/2, pong/0]).
ping(N, Pong_Pid) ->
link(Pong_Pid),
pingr(N, Pong_Pid).
pingr(0, _) ->
io:format("Ping exiting~n", []),
exit(ping);
pingr(N, Pong_Pid) ->
Pong_Pid ! {ping, self()},
receive
pong ->
io:format("Ping received pong~n", [])
end,
pingr(N - 1, Pong_Pid).
pong() ->
%% This one works.
%%register(pong, self()),
process_flag(trap_exit, true),
pongr().
pongr() ->
%% This one fails.
register(pong, self()),
receive
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pongr();
{'EXIT', From, Reason} ->
io:format("pong exiting, got ~p~n", [{'EXIT', From, Reason}])
end.
start(Ping_Node) ->
PongPID = spawn(pingpong, pong, []),
spawn(Ping_Node, pingpong, ping, [3, PongPID]).
If the process is already registered, it will throw a badarg. There is also some other cases that causes this, like the name is already used. See the erlang:register/2 docs for more.
EDIT
It's great that you posted code to reproduce your problem.
So, the first time you enter pongr/0 you will register self(). When you receive a message, you will process it and call pongr/0 again. The second time you enter pongr/0 you try to register self(), which fails because it's already registered.
Also, if you want to use register a large number of processes, you should look into gproc. register/2 requires an atom as the key and there is a limit of around one million atoms, unless you explicitly change it. See the efficiency guide. gproc can also run distributed and may thus be used instead of the global module.
is myproc already registered?
first call should succeed, additional calls will cause badarg exception.
1> register(myproc, self()).
true
2> myproc ! foo.
foo
3> flush().
Shell got foo
ok
4> register(myproc, self()).
** exception error: bad argument
in function register/2
called as register(myproc,<0.30.0>)

Erlang error when spawning a process

I start a process as follows
start() ->
register (dist_erlang, spawn(?MODULE, loop, [])),
ok.
But get the following error when trying to run start().
Error in process <0.62.0> with exit value: {undef,[{dist_erlang,loop,[]}]}
The module is called dist_erlang.
What am I doing wrong?
Thanks
Although the question is old, I post what helped me when I was wrestling with the Erlang compiler.
This (incomplete) snippet
-export([start/0]).
start() ->
Ping = spawn(?MODULE, ping, [[]]),
...
ping(State) ->
receive
...
end.
fails with error:
=ERROR REPORT==== 2-Sep-2013::12:17:46 ===
Error in process <0.166.0> with exit value: {undef,[{pingpong,ping,[[]],[]}]}
until you export explicitly ping/1 function. So with this export:
-export([start/0, ping/1]).
it works. I think that the confusion came from some examples from Learn You Some Erlang for great good where the modules sometimes have
-compile(export_all).
which is easy to overlook
Based on your previous question, your loop function takes one parameter, not none. Erlang is looking for loop/0 but can't find it because your function is loop/1.
The third parameter to spawn/3 is a list of parameters to pass to your function, and in the case you've shown the list is empty. Try:
register (dist_erlang, spawn(?MODULE, loop, [[]]))
In this case, the third parameter is a list that contains one element (an empty list).

Resources