Terminal opening on os.execute() and io.popen()

156 views
Skip to first unread message

Gerrit Sangel

unread,
Nov 22, 2024, 5:23:11 PM11/22/24
to lua-l
Hello,

I'm coming from Darktable, where on every start on Windows, the embedded Lua interpreters opens a bunch of terminal windows, which is really quite distracting (and imho makes the UX of the entire application quite ... nerdy).

I'm now trying to find out if this problem can be solved.  My first thought was to "just" replace os.execute() and io.popen() by custom functions which use Windows specific APIs. I had for example a look at the Rust source code and how it approaches spawning a subprocess on Windows, and so it seems managable, even though not extremely straightforward (os.execute() does not seem to be sooo difficult, but io.popen() is a different beast).

This lead me to wonder whether it wouldn't make sense to change this in the Lua interpreter itself, instad of just once in the darktable source code. First because this of course would benefit other projects as well, and because I guess it's better maintanable here.

Interestingly I found here in the mailinglist a thread from 2008 (http://lua-users.org/lists/lua-l/2008-09/msg00137.html) where this topic was discussed, but it seems to have not been followed up (at least because the issue is still appearing), even though it even seems to have some solutions.

So therefore I'm wondering whether it would make sense to pick this up again and would also like to ask for some experience about it maybe :)


Best regards
Gerrit

Sainan

unread,
Nov 22, 2024, 11:13:18 PM11/22/24
to lu...@googlegroups.com
I think it matters mainly what the type of application is that you're launching. A console application obviously creates a console if launched by itself. But a GUI application will not create a window on launch, rather the app must create it itself, and if it doesn't, nothing ever shows up.

Alternatively, you might be interested in trying something like this: https://stackoverflow.com/a/67300159

-- Sainan

Sainan

unread,
Nov 22, 2024, 11:29:47 PM11/22/24
to lu...@googlegroups.com
This seems like quite an elegant solution as well: https://superuser.com/a/1080456

-- Sainan

Gerrit Sangel

unread,
Nov 23, 2024, 4:33:53 AM11/23/24
to lua-l
Thanks for your reply!

So probably these two suggestions don't help so much in my usecase. My problem is mainly:

- Darktable is I would say (without meaning this bad to the Darktable community) a Linux-driven application, and Windows only has a bit of secondary support.
- Darktable is a GTK GUI application which uses Lua for additional scripting, and imho this setup is also not really the problem.
- Darktable already comes with a lot of community-developed Lua scripts, which often just use os.execute() and io.popen(), which from the scripting perspective is also perfectly fine.

The problem now is that this all works perfectly fine on Linux. As far as I know, there is no real distinction between a terminal application and a GUI application in Linux, it's only how the application itself is started (via these .desktop files). So when you start Darktable on Linux, it will just start the GUI window, load a bit and then you have the main application.

On Windows, on the other hand, there is the distinction when compiling the application. If you use the windows subsystem (which is something different than the modern WSL), an application is _not_ starting a terminal window, which is exactly what you'd like with a GUI application. Darktable itself is also doing this for the main application, so no problem there - or one would think.

The reason why I'm now writing on this mailing list here is: Darktable uses quite some Lua scripts during startup, some of them spawn a subprocess. From what I found out, Lua just uses the normal libc system calls for this. They are also supported on Windows but .... well not so nicely. What they do is to actually spawn a terminal windows, because I guess there is no stdout or something to use... Not sure, but this is really really annoying when a GUI _on every start_ has 5 or 6 terminal windows just quickly flashing up, getting focus etc. One could now argue that actually Microsoft should improve it, but I guess that's unrealistic (and probably I'm missing some valid use cases why it's even done like this).

On the other hand, this is not an unfixable problem, because other programming languages/their runtimes (as mentioned, I've went a bit through the Rust's source code) use different Win32 APIs which do _not_ create any terminal window in this case (for example, https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw).

I would say a proper fix for every application is to replace the default Lua implementations for these two functions with functions which use the proper Win32 APIs... But then this issue is only solved in one application, it increases maintainably effort and I think this is actually a bug in the Lua runtime implementation.  So I'm just wondering if there is a reason for the current implementation, or just because nobody found an opportunity/necessity to improve it yet, and whether it makes sense to improve this in the default Lua runtime.

Gerrit

Yao Zi

unread,
Nov 23, 2024, 5:19:40 AM11/23/24
to lu...@googlegroups.com
On Sat, Nov 23, 2024 at 01:33:52AM -0800, Gerrit Sangel wrote:
> On the other hand, this is not an unfixable problem, because other
> programming languages/their runtimes (as mentioned, I've went a bit through
> the Rust's source code) use different Win32 APIs which do _not_ create any
> terminal window in this case (for example,
> https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw).
>
> I would say a proper fix for every application is to replace the default
> Lua implementations for these two functions with functions which use the
> proper Win32 APIs... But then this issue is only solved in one application,
> it increases maintainably effort and I think this is actually a bug in the
> Lua runtime implementation. So I'm just wondering if there is a reason for
> the current implementation,

Lua itself is a quite small language. As you can see, its standard
library is mostly derived from ISO C. Thus I think it's just no one
would like to make use of non-standard API in such a project in order to
work around a platform-specific "bug". It's kind of dirty, at least to
me.

And I don't even think it should be called a "bug". This is how Lua
manual describes os.execute(),

This function is equivalent to the ISO C function system. It
passes command to be executed by an operating system shell.

So it's expected that os.execute() does nothing different from system()
in C.

> or just because nobody found an
> opportunity/necessity to improve it yet, and whether it makes sense to
> improve this in the default Lua runtime.

IMHO, it's not always an improvement, maybe there're applications
depending on a working stdin/stdout?

Lua focuses on its core functionality as a script language, delegating
platform-specific operations completely to C library instead of
unifiying implementation details ;)

Best regards,
Yao Zi

rolf.kal...@kalbermatter.nl

unread,
Nov 25, 2024, 3:46:09 AM11/25/24
to lu...@googlegroups.com
On 23 November 2024 at 10:33:52 +01:00, Gerrit Sangel <gerrit...@gmail.com> wrote:
On the other hand, this is not an unfixable problem, because other programming languages/their runtimes (as mentioned, I've went a bit through the Rust's source code) use different Win32 APIs which do _not_ create any terminal window in this case (for example, https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw).
It's not unfixable but requires quite a bit of Windows specific code. Windows GUI applications indeed don't have a stdin and stdout by default but can opt to support that too. That is beyond the control of a caller like Lua so not of any concern here.

The CreateProcess() API supports assigning pipes to the stdin, stdout and stderr handles to redirect these channels to caller supplied streams. It's however quite a bit of work and not for the faint of heart. Also Windows pipes, while conceptually the same as Posix pipes, have somewhat different semantics that make it an additional challenge to use when you are not familiar with their Windows incarnation. It definitely is not something that can be supported through an interface like system().

In addition, the CreateProcess() API also has an option to indicate if the resulting window should be hidden or not. Again not something that a call to system() allows to specify.

And CreateProcess() only instantiates the executable on the command line, it does not in itself implement a terminal. You have to explicitedly call cmd.exe in the command line and pass the rest in the parameters.

This is definitely functionality that should be put in a binary module of some sorts together with other OS specific abstractions. There are quite a few of those and some of them likely support a more feature rich SystemExec() interface with options to specify these things. Adding it to Lua itself would not only require several 100 lines of extra code just for support of Windows, but also additional os.* functions.

Rolf Kalbermatter

r f

unread,
Jan 16, 2025, 4:07:23 AM1/16/25
to lua-l
 The `winapi` library, copyright 2011 Steve Donovan, has an `execute` method that does not pop a console window and returns the stdout text. Writing an `io.popen` replacement using that seems trivial... Looking through some old code I chanced upon it and recalled this lua-l thread: somebody had already written the code that deals with the Windows pipes.
Reply all
Reply to author
Forward
0 new messages