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).


>> - 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...):


> Fail should take a return code too.

Good idea. Mind filing an enhancement request here?:

Reply via email to