Feather / src / dist / documentation / Commands.md
Commands
This is an optional feature, which must be explicitly turned on if you require it (for security reasons).
I like writing simple command line tools, but I've yet to find a good language for them. Using shell scripts is nasty, the syntax is weird and unforgiving, and the feature set is small.
Java is dreadful as a shell scripting language; using ProcessBuilder isn't pleasant! Python isn't any better either. So I've added an extra feature to Feather which is the perfect solution (for me anyway!).
To turn this feature on :
FeatherCompile.configuration.allowCommands = true
FeatherCompile.configuration.includeRuntime() // Ensure the feather-runtime jar is in the classpath.
To run an operating system command from a feather script :
$( mycommand arg1 arg2 ).run()
Between $( and ) is a smart string, so you can include expressions :
val name = "Nick"
$( echo Hello $name ).run()
But what if name
contained special characters such as "*", we need to enclose it in quotes :
$( echo 'Hello $name' ).run()
But what if name contained a single quote such as O'Reilly? Feather magic handles that automatically. The command passed to ProcessBuilder is actually :
sh -c echo 'Hello O'\''Reilly'
See CommandLineBuilder
for details on how this magic happens!
If you want to collect the output from a command, then don't use run()
, use collect()
instead :
$( myCommand arg1 arg2 ).collect()
Standard output and standard error are now available as properties on the CommandResult return value :
val output = $( myCommand arg1 arg2 ).collect().out
As a convenience, collect().out
can be replaced by eval()
:
val output = $( myCommand arg1 arg2 ).eval()
Here's an example feather script which uses ImageMagick to convert
all jpg files beginning with "Photo"
to png files :
// splitLines is an extension function of String, which returns a List<String>
for name in $( ls Photo* ).eval().splitlines() {
val file = File( name )
val ext = file.extension().toLowerCase()
if ( file.isFile() && ( ext == "jpg" || ext == "jpeg" ) ) {
$( convert -- '$file' '${file.nameWithoutExtension()}.png' ).run()
}
}
BTW, we could have used File(".").list()
and a filter instead of ${ ls Photo* }
But, IMHO, using ls
is easier.
Do you know of another language that can do this so succinctly, and is not error prone?
By default, commands use the CommandRunner
found in DefaultCommandRunner.defaultInstance
.
However, you may like to create another implementation, for example, to add a timeout, so
that long-running command do not stop the entire thread. In which case, you can run a
command using a custom CommandRunner like so :
$(myCommand arg1 arg2).run( myCommandRunner )
To set the current directory for a command :
$(myCommand).dir( File("/tmp") ).run()
Windows
The defaults assume your operating system has a shell called "sh" (which all unix variant have). Windows users should install a unix-like shell, such as bash.
You could use CMD.EXE, but I wouldn't recommend it, because AFAIK, there is no generic way to escape command line strings using CMD.EXE. CMD.EXE is truly awful, but some historic design mistakes cannot be easily removed or fixed.
There is no out-of-the-box cross platform solution.