---
description: Control where Jawk sends output, from simple streams to fully custom sinks.
date_published: 2026-05-13
date_modified: 2026-05-13
canonical_url: https://jawk.io/java-output.html
---

# Custom Output

On this Page

- [Capture as a String](#capture-as-a-string)
- [Redirect to a Stream](#redirect-to-a-stream)
- [Capture into an Appendable](#capture-into-an-appendable)
- [Custom Output with AwkSink](#custom-output-with-awksink) 
  
    - [Implementing an AwkSink](#implementing-an-awksink)
    - [Sink print and printf Parameters](#sink-print-and-printf-parameters)
    - [getPrintStream](#getprintstream)
    - [Using a Custom Sink](#using-a-custom-sink)
    - [Built-In Sink Implementations](#built-in-sink-implementations)
- [Numeric Locale](#numeric-locale) 
  
    - [With the Builder](#with-the-builder)
    - [With a Custom AwkSink](#with-a-custom-awksink)
- [Choosing the Right Output Strategy](#choosing-the-right-output-strategy)
- [Subprocess Output](#subprocess-output)
- [See Also](#see-also)

Output is specified per-call on the builder, to capture `print()` and `printf(...)` output in a stream, an `Appendable`, a custom [AwkSink](apidocs/io/jawk/jrt/AwkSink.html)[1], or simply as a `String`.

## [Capture as a String](#capture-as-a-string)

A no-argument `execute()` returns the printed output as a `String`:

```java
Awk awk = new Awk();
String result = awk.script("BEGIN { print \"hello\" }").execute();
// result == "hello\n"
```

## [Redirect to a Stream](#redirect-to-a-stream)

Pass an `OutputStream` or `PrintStream` to `execute(...)`:

```java
Awk awk = new Awk();
awk.script("BEGIN { print \"logged\" }").execute(new FileOutputStream("output.txt"));
```

To print to `System.out` explicitly:

```java
awk.script("BEGIN { print \"hello\" }").execute(System.out);
```

## [Capture into an Appendable](#capture-into-an-appendable)

Pass an `Appendable` to `execute(...)` to collect output in a `StringBuilder`, `StringWriter`, or any `Appendable`:

```java
Awk awk = new Awk();
StringBuilder output = new StringBuilder();
awk.script("BEGIN { print \"captured\" }").execute(output);
// output.toString() == "captured\n"
```

## [Custom Output with AwkSink](#custom-output-with-awksink)

Use [AwkSink](apidocs/io/jawk/jrt/AwkSink.html)[1] when plain text is not the right abstraction. An `AwkSink` receives raw `print(...)` and `printf(...)` calls together with the current AWK formatting state.

### [Implementing an AwkSink](#implementing-an-awksink)

Extend `AwkSink` and override `print(...)`, `printf(...)`, and `getPrintStream()`:

```java
public final class CollectingSink extends AwkSink {
    private final List<List<Object>> prints = new ArrayList<List<Object>>();
    private final PrintStream processOutput = System.out;

    public CollectingSink() {
        super(Locale.US);
    }

    @Override
    public void print(String ofs, String ors, String ofmt, Object... values) {
        prints.add(Arrays.asList(Arrays.copyOf(values, values.length)));
    }

    @Override
    public void printf(String ofs, String ors, String ofmt, String format, Object... values) {
        // store format + values however your application wants
    }

    @Override
    public PrintStream getPrintStream() {
        return processOutput;
    }

    public List<List<Object>> getCollectedPrints() {
        return prints;
    }
}
```

### [Sink print and printf Parameters](#sink-print-and-printf-parameters)

`print(...)`

**`print(...)`**

| Parameter | AWK Variable | Description |
| --- | --- | --- |
| `ofs` | `OFS` | Output Field Separator, inserted between values |
| `ors` | `ORS` | Output Record Separator, appended after the record |
| `ofmt` | `OFMT` | Default numeric output format |
| `values` | — | The raw AWK values passed to `print` |

`printf(...)`

**`printf(...)`**

| Parameter | AWK Variable | Description |
| --- | --- | --- |
| `ofs` | `OFS` | Output Field Separator, inserted between values |
| `ors` | `ORS` | Output Record Separator, appended after the record |
| `ofmt` | `OFMT` | Default numeric output format |
| `format` | — | The AWK format string |
| `values` | — | The AWK values to be formatted |

### [getPrintStream](#getprintstream)

`getPrintStream()` provides the `PrintStream` used for pumping the stdout of spawned processes (`system("...")` and pipe output from `print ... | "cmd"`) back into the host output. File redirection (`print > "file"`) does *not* use this stream — it creates its own file-backed sink internally.

The default implementation returns a no-op stream that silently discards output. Override this method in sinks that need to capture subprocess output. Implementations typically return `System.out` or a custom stream.

### [Using a Custom Sink](#using-a-custom-sink)

Pass the sink to `execute(...)` on the builder:

```java
Awk awk = new Awk();
CollectingSink sink = new CollectingSink();

awk.script("{ print $1, $2 }")
        .input("alpha beta\ngamma delta\n")
        .execute(sink);

// sink.getCollectedPrints() contains [[alpha, beta], [gamma, delta]]
```

### [Built-In Sink Implementations](#built-in-sink-implementations)

Jawk provides two built-in `AwkSink` implementations:

- **`AwkSink.from(PrintStream)`** / **`AwkSink.from(PrintStream, Locale)`** creates a sink that renders output to a `PrintStream`. This is the default behavior.
- **`AwkSink.from(Appendable)`** / **`AwkSink.from(Appendable, Locale)`** renders output to any `Appendable` such as `StringBuilder` or `StringWriter`.

The overloads without a `Locale` parameter default to `Locale.US`.

## [Numeric Locale](#numeric-locale)

The locale controls how AWK formats numbers in `print` and `printf` output (e.g. decimal separator). The default is `Locale.US`.

### [With the Builder](#with-the-builder)

When you use the `AwkRunBuilder` methods (`execute()`, `execute(OutputStream)`, `execute(Appendable)`), the locale is taken automatically from `AwkSettings`. Set it once on the `Awk` instance:

```java
Awk awk = new Awk();
awk.getSettings().setLocale(Locale.FRANCE);

// All execute() variants use the French locale for number formatting
String result = awk.script("BEGIN { print 3.14 }").execute();
// result == "3,14\n"
```

### [With a Custom AwkSink](#with-a-custom-awksink)

When you pass an `AwkSink` to `execute(AwkSink)`, the sink carries its own locale — `AwkSettings` is not involved. Specify the locale when constructing the sink:

```java
AwkSink frenchSink = AwkSink.from(System.out, Locale.FRANCE);
awk.script("BEGIN { print 3.14 }").execute(frenchSink);
```

When extending `AwkSink` directly, pass the locale to the `super(...)` constructor:

```java
public class MySink extends AwkSink {
    public MySink(Locale locale) {
        super(locale);
    }
    // ...
}
```

## [Choosing the Right Output Strategy](#choosing-the-right-output-strategy)

| Goal | API | Example |
| --- | --- | --- |
| Capture as `String` | `execute()` | `awk.script(s).execute()` |
| Print to a `PrintStream` | `execute(PrintStream)` | `awk.script(s).execute(System.out)` |
| Print to an `OutputStream` | `execute(OutputStream)` | `awk.script(s).execute(fileOut)` |
| Capture to `Appendable` | `execute(Appendable)` | `awk.script(s).execute(sb)` |
| Structured collection | `execute(AwkSink)` | `awk.script(s).execute(mySink)` |

## [Subprocess Output](#subprocess-output)

When AWK runs an external command via `system("...")` or a pipe (`print ... | "cmd"`), the command's **stdout** is pumped into the sink's `PrintStream` returned by `getPrintStream()`. The built-in `OutputStreamAwkSink` and `AppendableAwkSink` both override this method, so subprocess stdout is captured alongside normal output. For a custom `AwkSink` that does *not* override `getPrintStream()`, subprocess stdout is silently discarded (the default no-op stream). To capture subprocess stdout, override `getPrintStream()` in your sink so it returns a real stream.

Subprocess **stderr** defaults to the sink's `PrintStream` as well. To redirect it to a separate stream, use `errorStream(PrintStream)` (this only affects **stderr**, not stdout):

```java
awk.script("BEGIN { system(\"mycommand\") }")
        .errorStream(System.err)
        .execute(System.out);
```

The CLI uses `.errorStream(System.err)` so that command errors appear on the console rather than mixing with normal output.

## [See Also](#see-also)

- [Java Quickstart](java.html)[2]
- [Variables and Arguments](java-variables.html)[3]
- [Structured Input](java-input.html)[4]
- [Advanced Runtime](java-advanced.html)[5]
- [AwkSink Javadoc](apidocs/io/jawk/jrt/AwkSink.html)[1]
