Shake: Every Program Can Be a Clojure Function
sunng.info
sunng.info
It is easy to get things 95% right, and I am sure that Shake does that. But think twice before using it for production programs, particularly if there is any possibility of it encountering untrusted input.
If i'm using Perl's Shell.pm (as mentioned elsewhere here by btilly) and wanted to avoid quoting arguments then I could do:
use 5.016;
use warnings;
use Shell ();
use PerlX::QuoteOperator ls => {
-emulate => 'qq',
-with => sub ($) { Shell::ls($_[0]) },
};
use PerlX::QuoteOperator uname => {
-emulate => 'qq',
-with => sub ($) { Shell::uname($_[0]) },
};
use PerlX::QuoteOperator ip => {
-emulate => 'qq',
-with => sub ($) { Shell::ip($_[0]) },
};
# and then...
ls();
uname(-a);
ip(-4 addr);
But this seems to be an overkill because I could just use Perl's backticks (http://perldoc.perl.org/perlop.html#Quote-Like-Operators)... `ls`;
`uname -a`;
`ip -4 addr`;
And it works with variables... my $x = "/usr/local/";
my $files = `ls -l $x`;
and piping... my $perl_files = `ls | grep .pl`;
Shell.pm also works with variables & piping but I think that backticks are probably Perl's best DSL for dealing (easily) with shell stuff :) (let [x "/usr/local"] (ls -l x))
And it clobbers so many things in the core namespace when 'use'd.Good luck.
Yes the ...it indexes all the executables in your path... is different to the approach used by Perl's Shell.pm and I think Python sh.
These simply catch any undefined function calls and attempts to execute them in the shell (found in your $PATH).
use 5.016;
use warnings;
use Shell;
say cat("~/.vimrc"); # runs /bin/cat
say incorrect_spelled_command(); # ERROR can't find in $PATH
However nothing is clobbered here: sub cat { "This is my cat()!" }
use Shell;
say cat('~/.vimrc'); # => "This is my cat()!"
The best approach is to not use the lookup dispatch and instead tell it what executable you want to wrap... use Shell qw(cat);
Now incorrect_spelled_execute is never passed to Shell.pm and in fact will give a compilation error (Undefined subroutine). And any cat() subroutine will be overwritten but it does give redefined warning.I'd rather have something like https://github.com/clojure/tools.cli but in reverse: a library for building, manipulating, and running command lines and interacting with the results ... maybe something that supports setting up mappings so that external programs can be exposed as Clojure fns with "Clojuresque" arguments "--rather" "than" "--arrays-of" "strings".
Also, I think that path should not be passed in as an environment variable, but in Clojure somehow. There's no reason for a program to pollute a global namespace with it's internal details.
Namespaces are a honking great idea, let's have more of those!
Also, I think I just realized an another problem -- if I understood the lib correctly, it scans through the PATH once at require time. This is bad, because Clojure is often deployed in places where processes run for very long times. Even things as immutable as tools in PATH can change when given long enough time.
That's a step in the right direction, but it still triggers tons of warnings replacing vars in clojure.core.
=> (use '[clojure.set :as set])
=> set/intersection
#<set$intersection clojure.set$intersection@58ca40be>
Prior to the addition of :refer to require, this was often paired with an :only (...) in order to alias a namespace as well as refer in some limited set of vars if desired, all in one declaration.http://palletops.com/doc/reference/script/ https://github.com/pallet/stevedore
See: http://hugoduncan.org/post/2010/shell_scripting_in_clojure_w...
The project lives at: https://github.com/pallet/stevedore and is heavily used in pallet, clojure's configuration management and cloud control library
I can't really imagine a use case for this for me (bash! But with parens!) but still very neat idea and cool execution.