Scriptlike: New lib to aid in writing script-like programs

Nick Sabalausky SeeWebsiteToContactMe at semitwist.com
Tue Feb 11 16:07:35 PST 2014


On 2/11/2014 1:52 PM, Jesse Phillips wrote:
 > On Tuesday, 11 February 2014 at 11:38:06 UTC, Nick Sabalausky wrote:
 >> I've released a little one-module utility, Scriptlike, to help
 >> simplify writing shell script-like programs in D:
 >>
 >>   https://github.com/Abscissa/scriptlike
 >
 > It looks like you've covered a lot of the short comings for doing some
 > cmdln scripting. It also sounds like it would fit right into what I was
 > doing (I hand only covered the user input portion)
 > https://github.com/JesseKPhillips/JPDLibs/tree/cmdln (I really probably
 > should move it into its own project, maybe I should instead submit mine
 > to yours? Though I use Boost License).
 >

Oh yea, I hadn't even thought of user input :P. I guess I got in the 
habit of avoiding it in CLI apps. I remember seeing your lib for that a 
while back and rather liked it.

I think that would fit very well into Scriptlike, as long as you don't 
mind it all being in the same module as the rest of scriptlike, and 
preferably using same formatting style (not that that's strictly 
important, but consistency is nice of course).

As for the license, if you don't mind switching to zlib then great, just 
go ahead and submit a pull request if you'd like to. But I'm not married 
to zlib license or anything. My reasons for using zlib license are 
relatively minor, and I'm not opposed to switching to Boost, especially 
since it's already the Phobos license after all. What are your thoughts?

 > [OT] I also want to thank those behind std.process, std.path, and
 > std.algorithms these things have been really awesome and I use heavily
 > (great improvements).

///ditto

 >> - A thin wrapper over std.path and std.file that provides a dedicated
 >> Path type specifically designed for managing file paths in a simple,
 >> reliable, cross-platform way. No more dealing with slashes,
 >> paths-with-spaces, calling buildPath, normalizing, or getting paths
 >> mixed up with ordinary strings.
 >
 > Personally I've found the new std.path makes this much easier,

Yea, it *definitely* does. But at the same time, I've found I still end 
up mucking with slashes and such anyway for a couple reasons:

1. In simpler situations, calling buildPath sometimes just seems to make 
what should be trivial become more verbose than I'd like. So I don't 
always like to use it even when I know I should.

But with Scriptlike's Path type, it's just a simple partA~partB and 
slashes are handled behind-the-scenes (internally via 
buildNormalizedPath). Can't get much less verbose than that :) Maybe not
the most efficient strategy, but this is intended for shell-like 
scripts, so it's unlikely to be anything near a problematic bottleneck.

2. std.path (and buildPath in particular) likes to use / on Posix and \ 
on Windows. While this makes a lot of sense in certain ways, I find that 
when I do deal with paths, having all my code internally operate on 
forward-slashes-only often leads to much, much simpler code. The 
downside is that not only do I have to sanitize all my inputs to 
forward-slash-only, I also have to do the same for many paths I get back 
from std.path. And then I have to convert back to backslashes on windows 
if I want to display it to the user in a nice OS-correct way, or pass it 
to certain WinAPI functions.

So with Scriptlike, I'm hoping to avoid the need to ever deal with 
slashes at all, because all the clutter and meticulous care of always 
doing it the proper std.path-way is (hopefully) hidden behind-the-scenes 
via the Path type.


 > I'm not
 > user how you can address paths-with-spaces as this depends on who you
 > call, internally I don't need to worry. But maybe this simplifies it
 > more and is still worth it.

Path.toString() automatically quotes paths correctly when they contain 
spaces. So you can just pass it off to spawnShell or whatever and it's 
automatically all ready-to-go. Don't even need to call the 
std.path.escape*() funcs (which I've had trouble with on windows anyway, 
although DMD #10863 is apparently fixed in master now, so that should at 
least help).

Of course, if for any reason you need to keep the path un-quoted, 
there's Path.toRawString() for that.


 > Command echoing, and dry run are both valuable.

Oh, dry run, of course, I didn't think of that! Some scripts may still 
have to take extra care to handle dry runs correctly, for example if one 
step relies on the result of an earlier step *actually* being executed. 
But Scriptlike could probably help with at least some of the work. I've 
added a ticket for that: https://github.com/Abscissa/scriptlike/issues/8

Do you think enabling dry run should automatically enable command echoing?

 >> - Less-pedantic filesystem operations for when you don't care whether
 >> it exists or not: existsAsFile, existsAsDir, existsAsSymlink,
 >> tryRename, trySymlink, tryCopy, tryMkdir, tryMkdirRecurse, tryRmdir,
 >> tryRmdirRecurse, tryRemove: All check whether the source path exists
 >> and return WITHOUT throwing if there's nothing to do.
 >
 > This is my biggest gripe with the current available functions!

Yea, I'm constantly making wrappers for those things in my scripts. 
Finally decided to just toss them into a common lib.

I *do* think std.file's pedantic behavior with those does make a lot of 
sense in the general case. I'm not sure that I'd even want std.file to 
relax those checks by default. But for simple shell-script-like stuff, 
it does tend to be more bother than benefit.


 >> - One simple call, runShell, to run a shell command script-style (ie,
 >> synchronously with forwarded stdout/in/err) from any working
 >> directory. (Also automatically works around DMD #10863 without waiting
 >> for v2.066 - BTW, thanks all involved who fixed that.)
 >
 > Aside from the bug, I don't understand what this provides over "execute."

First of all, "spawn(Process|Shell)().wait()" is a better comparison for 
runShell than execute(Process|Shell). The execute functions, at least by 
default, hide the child's stdout/stderr. Granted, execute does capture 
the child's stdout, so you *could* output it yourself afterwords, but 
then the user doesn't see the output in real-time (and forget about 
anything interactive), so that's not a good solution.

Regarding runShell vs "spawn(Process|Shell)().wait()", I admit the 
motivations are somewhat on the weak side, but I think it's still enough 
to be worth having:

- There's the (now fixed in master for 2.066) bug, like we said.

- Out of all the ways to launch a process with std.process, 
"spawnShell().wait()" is the *one* way that actually matches a shell 
script's behavior (aside from the bug), and is therefore (at least IMO 
and in my experience) the one you'd usually want in a script-like 
program. But looking at the available choices and options in 
std.process, it's not immediately obvious how to do "Run a command just 
like a shell script would". So Scriptlike says "runShell, that's how you 
do it".

- runShell optionally takes a Path to use as the initial working 
directory to launch the process from (and then uses scope(exit) to 
automatically chdir back when the process finishes). Nothing in 
std.process does that right now, although there is a request in bugzilla 
for it: http://d.puremagic.com/issues/show_bug.cgi?id=11363

Plus there's the automatic command echoing (not that I couldn't do that 
in some more direct std.process wrappers, like I do for certain std.file 
functions).

 >> - One simple function, fail(string msg), to help you exit with an
 >> error message in an exception-safe way. (Does require some minor
 >> boilerplate added to your main().)
 >
 > I've just been using exceptions,

Yea, that's exactly what fail() is, really. It just throws a "class Fail 
: Exception", and then the user's main() is expected to catch it, do a 
"stderr.writeln(e.msg)" and return 1.

I wanted to provide a nice way to reduce all that main() boilerplate the 
user needs, but I'm not sure if that's possible right now. Here's the 
relevant D.learn thread (ooh, which appears to have some new posts now...):

http://forum.dlang.org/thread/ldc6qt$22tv$1@digitalmars.com

 > Fail should take a return code too.

Good idea. Mind filing an enhancement request here?:
https://github.com/Abscissa/scriptlike/issues



More information about the Digitalmars-d-announce mailing list