Command

open class Command

A Command is built up in the same way as smart Strings. i.e. the string is parsed from plain text strings, and expressions. Expression are in the form ${expression} or $reference.

To create a Command from within a Feather script use something like this :

$( echo Hello $name )

This will NOT run the command, it will only create a Command object, so instead :

$( echo Hello $name ).run()

You can also use .collect() or .eval() instead of .run().

If you are used to using Java's ProcessBuilder, forget what you know... Feather does things differently! I want commands to as powerful as the Unix command line, including features such as redirection, piping, glob wildcards, shell variables, aliases etc. So feather runs commands using a shell. The default shell is "sh". Therefore the example above actually runs the program "sh" with 2 arguments "-c" and "echo Hello Nick".

Because we are using a shell, it is important that arguments are escaped correctly. The example above is WRONG. If "name" were "O'Reilly", then we would end up with :

sh -e echo Hello O'Reilly

And sh will moan about the unmatched quote. Here's the correction :

$( echo Hello '$name' ).run()

Feather does some magic behind the scenes... Whenever it finds an expression, such as $name, it looks to see it we are currently inside single quotes by counting the number of single quote to the left of the expression (in this case the count is odd (1), so we ARE inside a quoted region). It then evaluates $name, and adjusts it accordingly. In this example, it builds :

sh -c echo Hello 'O'\''Reilly'

FYI, We have a quoted O then an escaped quote (using a backslash), then then text Reilly in quotes. If you include double quotes, then Feather does nothing special, the command string is passed to sh unchanged. So you could do :

ls "foo.${extension}"

If the feather variable extension contains the string "*", then the command will be :

ls "foo.*"

And the shell will see the * as a wildcard as normal. However, if extension is a quote, or double quote, the the command will fail. Therefore you can only use these kinds of quotes if you know for sure that the feather variable will never contains any unexpected special characters.

Here's another example, this time echo is passed only one parameter, previously it was passed 2. You are not limited to only one expression within the single quotes.

$( echo 'Hello $forename $surname' ).run()

Note. It is good practice to use the special argument "--" when building commands. For example :

$( rm '$file' )

will sometimes fail! If $file begins with a dash, then rm will think it is a flag, not a filename. So instead, include "--" which tells rm that there are no more flags :

$( rm -- '$file' )

Now $file will be treated as a filename even if it being with a dash. This isn't specific to Feather, you should do this for all your shell scripts ;-) Most commands support the special "--" argument, but commands written by noobs won't!

If you want to use a different shell, then either specify it each time you run a command :

$( echo Hello ).run( myCommandRunner )

Or change the default command runner once and for all :

Command.defaultCommandRunner = myCommandRunner
$( echo Hello ).run()
// All future commands will also use myCommandRunner

If you are using Windows, you might think that using cmd.exe is a good idea. I disagree! I believe it is impossible to safely escape commands using cmd.exe, so I recommend installing a "proper" shell instead. e.g. install bash as part of cygwin.

If you want cross platform out of the box without installing bash on windows, then don't use feather's command features. Use Java's ProcessBuilder instead. Oh, and complain to Microsoft too. Wouldn't it be so much better if they included sh out of the box (so that nobody ever needs to use cmd.exe nor powershell ever again ;-)

Constructors

Link copied to clipboard
constructor(commandParts: Array<Command.CommandPart>)
constructor(directory: File, commandParts: Array<Command.CommandPart>)
constructor(directory: File, env: Map<String, String>, commandParts: Array<Command.CommandPart>)

Types

Link copied to clipboard
open class CommandPart
A command part is simply a partial string when building the command.

Properties

Link copied to clipboard
When run(), collect() and eval() are called without an explicit CommandRunner, this is the CommandRunner that is used.
Link copied to clipboard
Link copied to clipboard
Link copied to clipboard

Functions

Link copied to clipboard
Runs the command, and also collects the standard output and standard error of the process into String objects.
open fun collect(commandRunner: CommandRunner): CommandResult
Link copied to clipboard
open fun dir(dir: File): Command
Link copied to clipboard
open fun env(env: Map<String, String>): Command
Link copied to clipboard
open fun eval(): String
A convenience method, which is the same as Command.collect().out Note, if the command outputs lots of text, then you should NOT use this, as you will have a huge String object.
open fun eval(commandRunner: CommandRunner): String
Link copied to clipboard
open fun run(): CommandResult
open fun run(commandRunner: CommandRunner): CommandResult
Link copied to clipboard
open fun toString(): String