Giraffe Basics

This guide covers the basic features of Giraffe and is a good introduction for new users.

Accessing Files

To do anything with a file or directory, you need a Path. A Path combines a path string with a FileSystem that can interpret the string. This configuration isn’t unique to Giraffe and is actually part of the java.nio.file API introduced in Java 7.

You can get a path from a FileSystem or, if you are only using the local file system, a static factory method:

// local paths
Path logo = Paths.get("resources/images/kittens.png");
Path betterLogo = Paths.get("resources", "images", "kittens.gif");

// local or remote, depending on fileSystem
Path bestLogo = fileSystem.getPath("resources/images", "laser_kittens.gif");

The factory methods take full path strings, path components, or some mix of the two. Path strings must use the same syntax as the file system, so providing individual components is safer with systems of unknown origin.

You can also build a path by adding to an existing one:

Path images = Paths.get("resources/images");
Path sloths = images.resolve("sloths.gif");

Because Path is immutable, methods like resolve return new objects and the original path is not modified.

To manipulate or access the file or directory given by a path, use the methods in Files and MoreFiles:

Path data = Paths.get("/var/data");
Files.createDirectory(data);
MoreFiles.writeString(data.resolve("json.yaml"), getJson(), StandardCharsets.UTF_8);
Files.copy(Paths.get("text.bin"), data.resolve("bin.sh"));

Take some time to explore the full APIs for these two classes, as they define all of the operations you can perform on files and directories.

Note

The methods on Files and MoreFiles only work with paths from open file systems. Once you close a file system, all Path objects associated with it are effectively useless.

Executing Commands

To execute any external process, you need a Command. A Command combines the name or path of an executable with a set of arguments and a ExecutionSystem that can run the executable.

Tip

The command execution API intentionally follows the pattern of the file system API, with Command taking the place of Path and ExecutionSystem taking the place of FileSystem. If you understand how to use one, the other should feel familiar.

You can get a command from an ExecutionSystem or, if you are only running executables on the local system, a static factory method:

// local commands
Command server = Commands.get("bin/kitten.sh", "--verbose", "string.txt");
Command betterServer = Commands.get(Paths.get("bin/freeKitten.sh"), "--debug", "--type", "spiders");

// local or remote, depending on execSystem
Command bestServer = execSystem.getCommand("laserKitten.sh", "-n", 1000, "-o", "/tmp/litter");

The executable can be a name, a path string, or a Path. When given a name, a system-dependent method is used to find the executable, usually using the value of the PATH environment variable. Any additional parameters are passed to the command as arguments. Arguments can have any type and are automatically converted to strings and escaped.

For commands with more complicated arguments, use Command.Builder:

Command.Builder builder = Commands.getBuilder("sloth-parse");
builder.addArgument("--use-tree");
builder.addArguments("--speed", "slow", "-f", Paths.get("leaves.sloth"));

List<String> outputArgs = getOutputArgs();
builder.addArguments(outputArgs);

Command slothParse = builder.build();

To run a Command, use the methods in Commands:

Command zeros = Commands.get("ones.py", "--negate", 8);
CommandResult zeroResult = Commands.execute(zeros);
assertEquals("00000000", zeroResult.getStdOut());

Command ones = Commands.get("ones.py", "--high-precision", 10000);
CommandFuture future = Commands.executeAsync(ones);
doImportantThings();
CommandResult oneResult = Commands.waitFor(future);

Commands also provides methods to execute commands with timeouts or with a modified environment.

By default, the various execute methods assume that successful commands exit with status 0, throwing CommandException when commands exit with a different status. To change this behavior, use a method that takes a CommandContext, setting it to ignore the exit status or check for a different condition.