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:
> 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).
>> - 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
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
- 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
>> - 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...):
> Fail should take a return code too.
Good idea. Mind filing an enhancement request here?: