Port.open EACCES on Windows

I’m using Port.open to execute some npm tools. This works fine on macOS and Linux but fails on Windows with the following reason:

* (ErlangError) erlang error: :eacces
    erlang.erl:2080: :erlang.open_port({:spawn_executable, "/path/to/node_modules/.bin/concisecss.cmd"}, [:exit_status, :stderr_to_stdout, {:args, ["compile"]}, {:line, 1024}])

I can execute concisecss.cmd normally by opening it with Explorer, and I have checked that I have execute permission for it (this happens for every npm program, concise is here just as an example). Is it because it is a bat script? How should I run it on Windows?

I can’t try it, but I think, you have to try to spawn it throuch cmd.exe or powershell.exe or whatever interpreter you need. Windows does not know about a concept similar to UNIX’ shebang, but has only some crippled system do determine such stuff by looking at the files ending. But this behaviour isn’t available for tools outside of windows “core” without hooping some loops.

1 Like

This sounds plausible. I will have to test it tonight. Running in cmd or PowerShell has the problem of opening needless shell windows though.

Google says that cmd /c foo.cmd should run foo.cmd without spawning a new window.

Buf as far as I can remember from my erlang things I did, you will loose the access to the spawned programs stdin and -out, which is probably the mainreason for using a Port

1 Like

Ah yes, that is a big problem. I need the output for logging. :confused:

Answering myself here with the solution. The problem was that cmd scripts can’t be spawned as executables on Windows, so I had to use cmd.exe. I looked at the :os.cmd sources to see how Erlang does it and found the winning combination.

In the end, the answer was to use cmd.exe /c foo.cmd, but with a Windows-specific flag :hide to the Port opening call. That hides the opened cmd window and sends stdout correctly. Without the flag, the cmd window flashes on the screen and stdout never arrives back to BEAM.

Here is the solution used in the end:

Port.open({:spawn_executable, "c:/windows/system32/cmd.exe"}, [:exit_status, :stderr_to_stdout, :hide, line: 1024, args: ["/c", "c:/path/to/node_modules/.bin/concisecss.cmd"]])
1 Like

Huh, I’d never come across that, fascinating! Thanks for the info! :slight_smile:

Yeah, I only found it by looking at how :os.cmd was implemented in the Erlang source. Later on I discovered it’s documented in the Erlang docs as:

When running on Windows, suppresses creation of a new console window when spawning the port program. (This option has no effect on other platforms.)

1 Like