Help needed: Running external commands from Lua (no os.exec).

2,569 views
Skip to first unread message

Lor

unread,
Aug 10, 2010, 10:29:29 AM8/10/10
to scite-interest
Hi all!

I need a way to run an external command from a Lua script loaded in
Scite, but I wouldn't want to use os.exec because on Windows it brings
up (briefly) an ugly console window.

I came up with a solution, but it seems a bit of a hack:

-- simplified version
function RunFoo()
local command = [[foo.exe dummy1 dummy2]] -- command line usually
fed to os.exec
-- redefine user command #49
props["command.49.*"] = command
props["command.mode.49.*"] = "subsystem:shellexec"
-- trigger user command #49
scite.MenuCommand(1149)
-- restore the original definitions
props["command.mode.49.*"] = nil
props["command.49.*"] = nil
end


It seems to work well, but It feels rather brittle (I don't know if
there could be some unwanted interactions or subtle problems).
Is there a direct way to feed commands to SciTE execution subsystems
without this sort of tricks?
Any help appreciated (or at least some reassurance that I didn't do
something really nasty in the above).
Thanks In Advance
Lorenzo

Neil Hodgson

unread,
Aug 10, 2010, 11:10:39 PM8/10/10
to scite-i...@googlegroups.com
Lor:

> I came up with a solution, but it seems a bit of a hack:

> ...


> props["command.49.*"] = command
> props["command.mode.49.*"] = "subsystem:shellexec"
> -- trigger user command #49
> scite.MenuCommand(1149)

It is a bit of a hack and this may break in the future.

> It seems to work well, but It feels rather brittle (I don't know if
> there could be some unwanted interactions or subtle problems).
> Is there a direct way to feed commands to SciTE execution subsystems
> without this sort of tricks?

There were some proposals for better functionality in this area but
it is complex as it involves multiple threads and/or tasks.

Neil

mozers

unread,
Aug 11, 2010, 5:06:59 AM8/11/10
to Lor
Tuesday, August 10, 2010, 6:29:29 PM, Lor wrote:

> function RunFoo()
> local command = [[foo.exe dummy1 dummy2]] -- command line usually
> fed to os.exec
> -- redefine user command #49
> props["command.49.*"] = command
> props["command.mode.49.*"] = "subsystem:shellexec"
> -- trigger user command #49
> scite.MenuCommand(1149)
> -- restore the original definitions
> props["command.mode.49.*"] = nil
> props["command.49.*"] = nil
> end

This is an interesting trick.
But there are problems. Fixed them in scite-ru <http://code.google.com/p/scite-ru/issues/detail?id=53>
You can use shell.exec <http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html#exec>
It's easier and safer.

--
mozers
<http://scite.net.ru>

Lor

unread,
Aug 11, 2010, 1:37:42 PM8/11/10
to scite-interest
Neil:
Thanks for the prompt reply


> It is a bit of a hack and this may break in the future.

Mmmh, I had a bad feeling about it, indeed. It seemed too simple and
too clever to be sound! :-)

But I'm sticking to SciTE 2.03 because sometimes I need to work with
some old Win98 machines and for now it is sufficient that it is not a
timebomb waiting to destroy data. For the future I will try to find
another solution.

>
> There were some proposals for better functionality in this area but
> it is complex as it involves multiple threads and/or tasks.
>
Ouch! I didn't think it was *so* difficult :-(


Best Regards,
Lorenzo

Lor

unread,
Aug 11, 2010, 1:42:01 PM8/11/10
to scite-interest

mozers:

Thanks for the reply.


> This is an interesting trick.
> But there are problems. Fixed them in scite-ru <http://code.google.com/p/scite-ru/issues/detail?id=53>

Sorry, it is in russian and I cannot understand it. :-(

> You can use shell.exec <http://scite-ru.googlecode.com/svn/trunk/pack/tools/LuaLib/shell.html...>
> It's easier and safer.

I will give it a try. Thanks.

steve donovan

unread,
Aug 12, 2010, 2:23:07 AM8/12/10
to scite-i...@googlegroups.com
On Wed, Aug 11, 2010 at 7:37 PM, Lor <lorenzo....@tiscali.it> wrote:
>>    There were some proposals for better functionality in this area but
>> it is complex as it involves multiple threads and/or tasks.
>>
> Ouch! I didn't think it was *so* difficult :-(

Oh, it does get tricky! So when I needed this kind of functionality I
wrote a little Lua extension in C to do the job.

If you look at scite-debug.zip from

http://luaforge.net/frs/?group_id=327

you'll see a spawner-ex.dll.

The extman.lua shows how to load this and use its version of popen if available.

-- by default, the spawner lib sits next to extman.lua
spawner_path = scite_GetProp('spawner.extension.path',extman_path)
if GTK then
fn,err = loadlib(spawner_path..'/unix-spawner-ex.so','luaopen_spawner')
else
fn,err = loadlib(spawner_path..'\\spawner-ex.dll','luaopen_spawner')
end
if fn then
fn() -- register spawner
else
print('cannot load spawner '..err)
end

Then you have spawner.popen, which doesn't flash that irritating box.
This dll also provides a means to capture processes interactively,
that is effectively a popen2. I needed this to spawn debug processes
- this does get a bit involved because you have to pump the output of
the process asynchronously back into SciTE lua, using a callback.

BTW, even on GTK I found the stock io.popen to cause Glib memory
crashes sometimes - this is why spawner.popen is also defined for
Unix.

steve d.

Lor

unread,
Aug 12, 2010, 5:46:22 AM8/12/10
to scite-interest
Thank you very much Steve!
>
> If you look at scite-debug.zip from
>
> http://luaforge.net/frs/?group_id=327
>
> you'll see a spawner-ex.dll.
>
I've downloaded it. I will give it a try and see if it works for me.
It looks promising.
Best Regards.
Lorenzo D.

Lorenzo Donati

unread,
Aug 12, 2010, 12:45:09 PM8/12/10
to scite-interest
Hi steve!

>
> If you look at scite-debug.zip from
>
> http://luaforge.net/frs/?group_id=327
>
> you'll see a spawner-ex.dll.

Ok, I "installed" the dll and loaded it using "require" (renamed it
spawner.dll). spawner.popen works, but with a funny glitch (no
extensive testing done):


local command = [["G:\root\main\prg\SciTE\app\wscite\SciTE.exe" G:
\root\main\prg\SciTE\libs\snippets\CompareWithOriginalSnippet.lua]]
spawner.popen(command)

works; but this:

local command = local command = [["G:\root\main\prg\SciTE\app\wscite
\SciTE.exe" "G:\root\main\prg\SciTE\libs\snippets
\CompareWithOriginalSnippet.lua"]]
spawner.popen(command)

don't (note that the second path is quoted).

BTW: those paths were autogenerated by my script, they were not
hardcoded.


In this case there was no big problem, but what if the second path had
some space in it? Would the invoked program get one or two command
line args (I suppose two)? How to do proper quoting then?
Am I missing something in how to use it?

Thank you in advance.
Lorenzo

P.S.: I skimmed through the source code for spawner-ex.dll and I saw
it registers a bunch of functions which seem quite useful. Is there
some specific documentation on their usage available (even some sort
of bare API docs would do, such as basic usage, parameters in,
parameters out, return values, limits)?
I'm not a C wizard and this goes down to Win32 API (ouch! :-), and I
would have a really hard time in inferring their usage from the
source.
Thanks again.


Lorenzo Donati

unread,
Aug 12, 2010, 12:51:35 PM8/12/10
to scite-interest
Sorry for the typo:

>
> local command = local command = [["G:\root\main\prg\SciTE\app\wscite
> \SciTE.exe" "G:\root\main\prg\SciTE\libs\snippets
> \CompareWithOriginalSnippet.lua"]]


of course it should read:

local command = [["G:\root\main\prg\SciTE\app\wscite\SciTE.exe" "G:
\root\main\prg\SciTE\libs\snippets\CompareWithOriginalSnippet.lua"]]

I'm having some problems with google groups: when I try to reply to
any post that is not the last one, I get no caret in the edit window,
so I must copy and paste from my editor (guess? SciTE :-) into my
browser and this back and forth sometimes goes awry!

steve donovan

unread,
Aug 13, 2010, 5:04:37 AM8/13/10
to scite-i...@googlegroups.com
On Thu, Aug 12, 2010 at 6:45 PM, Lorenzo Donati
<lorenzo...@interfree.it> wrote:
> local command =         local command = [["G:\root\main\prg\SciTE\app\wscite
> \SciTE.exe" "G:\root\main\prg\SciTE\libs\snippets
> \CompareWithOriginalSnippet.lua"]]
> spawner.popen(command)
>
> don't (note that the second path is quoted).

Ah, I'm lookiing at the code and I can see that it has a little
problem with quoted arguments, I'll fix that.

> P.S.: I skimmed through the source code for spawner-ex.dll and I saw
> it registers a bunch of functions which seem quite useful. Is there
> some specific documentation on their usage available (even some sort
> of bare API docs would do, such as basic usage, parameters in,
> parameters out, return values, limits)?

My bad, always forgot about documentation ;) You correctly note that
it really should be called spawner.dll and then require() will work
out of the box.

out = spawner.popen(cmd) -- does prepend cmd.exe /c (depends on COMSPEC)

This can be useful because you can both write to a process and then
read from it.

inf,out = spawner.popen2(cmd)

These are file objects, but I couldn't work out how to make them
actual standard Lua file objects. So this DLL basically copies code
from the Lua liolib.c implementation. You can write(), read() and
lines() also works.

However, it does block, which is not a cool thing with a GUI app like SciTE.

So we have the spawner object. For instance, when running a debugger subprocess:

spawner_obj = spawner.new(dbg_cmd)
spawner_obj:set_output('ProcessChunk')
spawner_obj:set_result('ProcessResult')
spawner_obj:run()

This returns immediately; any output from the process is passed to a
global SciTE function ProcessChunk as a string, and the final result
will go to ProcessResult (receives the result code as a string.) This
happens asynchronously in the main GUI thread so SciTE is not
affected.

spawner_obj:write() is available for speaking to the captured process,
just passes a string.

This is how scite-debug manages to run GDB and the Python and Lua
debuggers in the background.

steve d.

Lorenzo Donati

unread,
Aug 13, 2010, 10:21:37 AM8/13/10
to scite-i...@googlegroups.com
Hi Steve!
Thank you for the prompt response!

> Ah, I'm lookiing at the code and I can see that it has a little
> problem with quoted arguments, I'll fix that.
>
>

Ok, thanks.

> [snip (lots of hints)]
>
>
Thank you again! Lots of stuff to ponder about!
Best Regards
Lorenzo


Lorenzo Donati

unread,
Aug 14, 2010, 4:56:34 AM8/14/10
to scite-i...@googlegroups.com
Many thanks again to Steve and Mozers for support.

Mozers:
shell.dll works well (no extensive testing done, though). I still can't
find licensing info even in the zip you sent me by PM. Is it public
domain? Or MIT, like Lua, or GPL/LGPL?.

Mozers & Steve:
Both libraries (shell.dll & spawner.dll) inject themselves into Lua's
global namespace (IIRC this practice has been discussed in Lua's mailing
list lately and deprecated in upcoming Lua 5.2).
For my purposes It would be much better if this didn't happen, so I'd
like to try and hack the source to make those libraries only return a
table when "required" (that's why I also need to know the licensing term
for shell.dll source).

Best Regards,
Lorenzo


steve donovan

unread,
Aug 14, 2010, 7:14:45 AM8/14/10
to scite-i...@googlegroups.com
On Sat, Aug 14, 2010 at 10:56 AM, Lorenzo Donati
<lorenzo...@interfree.it> wrote:
> Both libraries (shell.dll & spawner.dll) inject themselves into Lua's global
> namespace (IIRC this practice has been discussed in Lua's mailing list
> lately and deprecated in upcoming Lua 5.2).

You are welcome to make any modifications (it's licensed under the
same terms as ScITE), although I think people get a little overexcited
about changes to the global Lua table. What we should be able to
assume is that 'local mod = require 'package.mod' makes mod to be the
module, and that there is then a global 'package.mod' containing the
module. Some modules don't behave so nicely (e.g mod is just true)
and these are the problem.

The general problem of global namespace pollution (to use the C++
term) in Lua is probably better solved by your environment performing
static analysis on the code, like David Manura's lua-inspect. He
should be announcing his SciTE plugin soon.

steve d.

mozers

unread,
Aug 14, 2010, 5:43:49 PM8/14/10
to Lorenzo Donati
Saturday, August 14, 2010, 12:56:34 PM, Lorenzo wrote:
> shell.dll works well (no extensive testing done, though). I still can't
> find licensing info even in the zip you sent me by PM. Is it public
> domain? Or MIT, like Lua, or GPL/LGPL?.

Source code is absolutely free. No licensing restrictions.
I.e. you can freely modify it and use it anywhere.
If you share with us a improved code it will be good.
If you mention the source it would be just wonderful.

--
mozers
<http://scite.net.ru>

Lorenzo Donati

unread,
Aug 16, 2010, 3:13:52 AM8/16/10
to scite-i...@googlegroups.com
steve donovan wrote:
> You are welcome to make any modifications (it's licensed under the
> same terms as ScITE),
Thanks!

> although I think people get a little overexcited
> about changes to the global Lua table. What we should be able to
> assume is that 'local mod = require 'package.mod' makes mod to be the
> module, and that there is then a global 'package.mod' containing the
> module. Some modules don't behave so nicely (e.g mod is just true)
> and these are the problem.
>

Mmmh. I'm not an extremist about globals (regardless of language), but I
still try to avoid them like plague, except for quick and dirty
throw-away scripts/programs.
In the years I've grown a sort of itch about them: whenever the code I'm
writing grows longer than, say, about 20-30 lines, I feel an urge to
make any symbol "as local as possible" :-) .
For the same reason I feel rather disturbed when external code has
"unexpected" side effects (well, with Lua modules this is not so
"unexpected"... :-) ). I highly value the principle of least surprise
and unknown globals popping up from almost nowhere is not something I
would want to care about!
I'm not really an expert of Lua, but I don't see the _need_ for a module
to make its "symbols" directly accessible as globals (or as a global
table). It's Just a convenience, but if the module returned the "symbol"
table (as you point out), there would be no need to put anything in _G
(or am I missing something?!?). This is maybe more troublesome with
modules loaded indirectly: in this case I may not even know that
something is being put in _G (the module I require might behave
"nicely", but require in turn another module that doesn't)! (I hope I'm
not being too much OT for this list - maybe all this should be moved to
Lua mailing list?)

Best Regards,
Lorenzo

Lorenzo Donati

unread,
Aug 16, 2010, 3:18:03 AM8/16/10
to scite-i...@googlegroups.com
Thank you mozers!
I don't know yet if and when I will have the time and the real need to
do that modification, but if I do that I will gladly share it.
Best Regards,
Lorenzo

steve donovan

unread,
Aug 16, 2010, 3:56:07 AM8/16/10
to scite-i...@googlegroups.com
On Mon, Aug 16, 2010 at 9:13 AM, Lorenzo Donati
<lorenzo...@interfree.it> wrote:
> I'm not really an expert of Lua, but I don't see the _need_ for a module to
> make its "symbols" directly accessible as globals (or as a global table).
> It's Just a convenience, but if the module returned the "symbol" table (as
> you point out), there would be no need to put anything in _G (or am I
> missing something?!?).

Believe me, the lua list is full of this discussion at the moment ;)

But globals with SciTE Lua are unavoidable, e.g OnKey etc callbacks.
One just has to be awake...

steve d.

Lorenzo Donati

unread,
Aug 16, 2010, 4:09:36 AM8/16/10
to scite-i...@googlegroups.com

> Believe me, the lua list is full of this discussion at the moment ;)
>
Ooops! Too Late! ;-) :-)

> But globals with SciTE Lua are unavoidable, e.g OnKey etc callbacks.
> One just has to be awake...
>
Yes, I'm aware of that. I'm not really complaining about this. Probably
this is one case of sensible usage of globals (when Lua is used embedded
in another software). I fear it's my mindset: I tend to view Lua more as
a standalone script language (I know it is not just that! I've been
lurking on Lua list for about a year :-) ). You know, when a fool has a
shiny new hammer everything looks like a nail! :-))

Best Regards,
Lorenzo

Reply all
Reply to author
Forward
0 new messages