# Executors

Executors are the engines responsible for executing each item of the sequence of command lines defined in your command.
Yes, too many words. Let's clarify that. By default, the command lines will be executed by the _Command-line executor_.
However, you can add a prefix to the line, and a different executor will run it.

Let's explore all the possibilities and understand each one of them.

## Command-line Executor

The _Command-line Executor_ is the most basic one. It'll execute the instructions in your terminal. It doesn't require any prefixes
and supports any command-line instruction you can use on your favorite terminal.

Before executing each instruction, the variables will be interpolated.

```json
...
  {
    "name": "command",
    "execute": [
      "echo 'create directory ${dir}'",
      "mkdir ${dir}",
      "cat files.txt | xargs -I{} bash -c 'echo {} > ${dir}/{}.txt'"
    ],
    ...
  }
...
```

For each instruction it executes, its output will be printed to the standard output, and the errors will be printed to the error output.

If you don't want the output printed, please check the [Nout Executor](#nout-executor).

## Set Executor

The _set executor_ is responsible for adding variables or changing their values. It has the `set:` prefix, allowing you to define one or multiple variables.

```text
set:<VARIABLE NAME>=<VARIABLE VALUE>
set:<VARIABLE1>=<VALUE1>;<VARIABLE2>=<VALUE2>;...
```

Here are some examples:

```json
...
  {
    "name": "command",
    "execute": [
      "set:name=John Doe",
      "set:firstName=John;lastName=Doe",
      "set:fullName='${firstName} ${lastName}'",
      "echo 'Hello, ${fullName}!'"
    ],
    ...
  }
...
```

You can set the value from some instruction output. Just use an exclamation mark after the equals sign to indicate that it is a command line instruction.

```json
...
  {
    "name": "command",
    "execute": [
      "set:addresses=!cat addresses.txt",
      ...
    ],
    ...
  }
...
```

You can also parse a JSON string into a navigable object using the `json:` prefix. This allows you to access nested fields with dot notation and array indexing:

```json
...
  {
    "name": "command",
    "execute": [
      "set:person=json:${data}",
      "log:${person.name} is ${person.age}"
    ],
    ...
  }
...
```

```bash
> aux4 command --data '{"name":"Alice","age":30}'
```

```text
Alice is 30
```

You can also build an object field by field using dot notation on the left side of the `set:`.
Each assignment sets a field on the object, creating the base object (and any intermediate objects)
automatically if it doesn't exist yet. Existing fields are preserved:

```json
...
  {
    "name": "command",
    "execute": [
      "set:person.name=Alice",
      "set:person.age=json:30",
      "set:person.address.city=NYC",
      "log:${person.name} is ${person.age} and lives in ${person.address.city}"
    ],
    ...
  }
...
```

```bash
> aux4 command
```

```text
Alice is 30 and lives in NYC
```

A bare value is stored as a string (`person.age` would be `"30"`); use the `json:` prefix, as shown
above, when you need a number, boolean, array, or object as the field value.

This executor doesn't print anything to the console output.

## Log Executor

The _log executor_ only prints to the standard output. It has the `log:` prefix and it also interpolates the variables.
It works like the `echo` command, but it has a huge benefit.
It doesn't require _aux4_ to request the OS to execute an external command.
It runs directly, so it's faster than using `echo`.

```json
...
  {
    "name": "create-dir",
    "execute": [
      ...
      "log:the directory ${dir} was created successfully"
    ],
    ...
  }
...
```

## Debug Executor

The _debug executor_ works very similarly to the _log executor_. The main difference is that output goes to the error output
instead of the standard output. So, it shouldn't impact your command if you redirect the output to another file or command.

Also, the debug executor will print the message only if you define an environment variable `AUX4_DEBUG=true`.

```json
...
  {
    "name": "create-dir",
    "execute": [
      "debug:before creating ${dir}",
      ...
      "log:the directory ${dir} was created successfully",
      "debug:after creating ${dir}"
    ],
    ...
  }
...
```

When you run without the environment variable, it prints just the standard output.

```bash
> aux4 create-dir --dir test
```

```text
the directory test was created successfully
```

However, when the environment variable `AUX4_DEBUG` is true, it'll include the debug messages.

```bash
> export AUX4_DEBUG=true
```

```bash
> aux4 create-dir --dir test
```

```text
before creating test
the directory test was created successfully
after creating test
```

## Nout Executor

_Nout_ stands for `No Output`. The name says everything, it doesn't print the output of that command-line. But it still
saves the output to the [response variable](/variables#response-variable), in case you need to use it later.

```json
...
  {
    "name": "list-addresses",
    "execute": [
      "nout:cat addresses.txt",
      ...
    ],
    ...
  }
...
```

## Redirect Shortcuts

Inside a command line you can discard a command's output with three readable shortcuts instead of raw shell redirections. Unlike the [Nout Executor](#nout-executor) — which silences a whole execute step and keeps the output in the [response variable](/variables#response-variable) — these operate on a single command, so you can silence one command in a compound line while the rest runs normally.

| shortcut | shell equivalent | effect |
|----------|------------------|--------|
| `>ignore` | `>/dev/null 2>&1` | discard everything the command prints |
| `>ignoreError` | `2>/dev/null` | discard only the error stream (stderr) |
| `>ignoreOutput` | `>/dev/null` | discard only the normal output (stdout) |

```json
...
  {
    "name": "reschedule",
    "execute": [
      "aux4 cron remove --name job >ignore; aux4 cron add --name job --every '1 day'"
    ],
    ...
  }
...
```

The `remove` may fail if the job was never scheduled — `>ignore` hides its output and error so the line reads cleanly and continues to `add`.

They are only recognized as standalone tokens, so a real redirect such as `2>errors.log`, or a file literally named `>ignore.log`, is left untouched.

## Confirm Executor

The _confirm executor_ prompts a question to the user and requires their confirmation (yes or no).

```json
...
  {
    "name": "user-action",
    "execute": [
      "confirm:Are you sure",
      "log:The user is confident, we can execute"
    ],
    ...
  }
...
```

When you run the command, it will prompt the message.

```bash
> aux4 user-action
```

```text
? Are you sure? [y/N] |
```

```text
Are you sure: y
The user is confident, we can execute it
```

In case the user says `no`.

```text
✗ Are you sure:
User aborted
```

## Exit Executor

The _exit executor_ stops the command immediately with a given exit code. It has the `exit:` prefix and the form `exit:<code>:<message>`.

```text
exit:<code>:<message>
```

- A **non-zero** code stops the command and prints the message in red to standard error.
- A **zero** code is a clean early stop: it prints the message to standard output and exits successfully.
- The message is optional (`exit:1` exits silently), and a non-numeric argument becomes the message with the default code `1` (`exit:something failed`). Colons in the message are preserved, and variables are interpolated.

It is most useful together with the [when executor](#when-executor) to guard a command:

```json
...
  {
    "name": "send",
    "execute": [
      "when:${email}==:exit:1:email is required",
      "log:sending to ${email}"
    ],
    ...
  }
...
```

```bash
> aux4 send
```

```text
email is required
```

The command stops with exit code `1` and never reaches the `log:` step. When `--email` is provided, the condition is false and execution continues.

## JSON Executor

The _JSON executor_ will parse the output of the instruction to [JSON](https://json.org) and store it in
the [response variable](/variables#response-variable), which you can use as an object or array.

```json
...
  {
    "name": "command",
    "execute": [
      "json:cat addresses.json",
      "echo '${response[0].state} ${response[0].zipCode}'"
    ],
    ...
  }
...
```

This executor doesn't print any output.

## Each Executor

The _each executor_ will iterate over the output of the previous instruction. If the [response variable](/variables#response-variable)
is a string, it will iterate over the lines. If the [response variable](/variables#response-variable) is an array, it will
iterate over the elements of that array.

```json
...
  {
    "name": "command",
    "execute": [
      "json:cat addresses.json",
      "each:echo '${item.state} ${item.zipCode}'"
    ],
    ...
  }
...
```

If the response is a string, it will iterate over the lines. You can also use the `index` variable to get the index of the item.

```json
...
  {
    "name": "command",
    "execute": [
      "cat addresses.txt",
      "each:echo ${index} ${item}"
    ],
    ...
  }
...
```

In case some of the lines you are iterating over will produce some error, you can use the `ignoreErrors` variable to ignore them.

```json
...
  {
    "name": "command",
    "execute": [
      "set:ignoreErrors=true",
      "cat list.txt",
      "each:cat ${item}"
    ],
    ...
  }
```

## Range Executor

The _range executor_ generates an array of numbers and stores it in the [response variable](/variables#response-variable). It's especially useful with the [each executor](#each-executor) for iterating a fixed number of times.

- `range:N` — generates `[0, 1, ..., N-1]`
- `range:X-Y` — generates `[X, X+1, ..., Y]`

It supports variable interpolation.

```json
...
  {
    "name": "repeat",
    "execute": [
      "range:${n}",
      "each:echo step ${item}"
    ],
    "help": {
      "text": "iterate N times",
      "variables": [
        {
          "name": "n",
          "text": "number of iterations"
        }
      ]
    }
  }
...
```

```bash
> aux4 repeat --n 3
```

```text
step 0
step 1
step 2
```

With a start-end range:

```json
...
  {
    "name": "ports",
    "execute": [
      "range:8080-8083",
      "each:echo checking port ${item}"
    ],
    "help": {
      "text": "check port range"
    }
  }
...
```

```bash
> aux4 ports
```

```text
checking port 8080
checking port 8081
checking port 8082
checking port 8083
```

## Profile Executor

The _profile executor_ will redirect the following arguments to another profile. As we mentioned before, it's possible to
have multiple profiles, but when you run _aux4_ it will list only the commands from the `main` profile. So you have to create
a command that will be the trigger to switch to another profile.

- Main profile (lines 3-25)
  - test command (lines 6-14)
  - ops command (lines 15-23)
- Test profile (lines 26-39)
  - run command (lines 29-37)
- Ops profile (lines 40-53)
  - deploy command (lines 43-51)

```json
{
  "profiles": [
    {
      "name": "main",
      "commands": [
        {
          "name": "test",
          "execute": [
            "profile:test"
          ],
          "help": {
            "text": "manage the tests"
          }
        },
        {
          "name": "ops",
          "execute": [
            "profile:ops"
          ],
          "help": {
            "text": "manage the ops"
          }
        }
      ]
    },
    {
      "name": "test",
      "commands": [
        {
          "name": "run",
          "execute": [
            "log:run tests"
          ],
          "help": {
            "text": "execute the tests"
          }
        }
      ]
    },
    {
      "name": "ops",
      "commands": [
        {
          "name": "deploy",
          "execute": [
            "log:deploy the app"
          ],
          "help": {
            "text": "deploy the application"
          }
        }
      ]
    }
  ]
}
```

If you run `aux4` it will just list the commands from `main`.

```bash
> aux4
```

```text
test
manage the tests

ops
manage the ops
```

When you switch the profile, it behaves the same as running aux4 but pointing to that profile instead of the `main` profile.

If you run `aux4 test`, it will switch to the `test` profile. Since it doesn't have a following argument, it will list the commands
of the `test` profile.

```bash
> aux4 test
```

```text
run
execute the tests
```

However, if you run `aux4 test run` it will execute the `run` command from the `test` profile.

```bash
> aux4 test run
```

```text
run tests
```

## Stdin Executor

The _stdin executor_ will send the Standard Input (stdin) to the command-line instruction.

```json
...
  {
    "name": "get-first-item",
    "execute": [
      "stdin:jq '.[0]'"
    ],
    ...
  }
...
```

When you execute you can redirect the output to the _aux4_ command.

```bash
> cat array.json | aux4 get-first-item
```

```json
{
  "name": "john",
  "age": 30
}
```

## File Executor

The _file executor_ writes content to a file. It has the `file:` prefix with the format `file:<path>:<content>`.

```json
...
  {
    "name": "save-config",
    "execute": [
      "file:config.txt:host=${host}\nport=${port}"
    ],
    ...
  }
...
```

To **append** to a file instead of overwriting, prefix the content with `+`:

```json
...
  {
    "name": "log-event",
    "execute": [
      "file:events.log:+${timestamp} ${event}"
    ],
    ...
  }
...
```

Both the path and content support variable interpolation. If the file doesn't exist, it will be created. Append mode (`+`) also creates the file if it doesn't exist.

This executor doesn't print anything to the console output.

## Comment

You can add comments to your execute instructions by starting the line with `#`. Comments are ignored and not executed.

```json
...
  {
    "name": "hello",
    "execute": [
      "# This is a comment and should be ignored",
      "log:hello world",
      "# Another comment - this is also ignored",
      "log:goodbye"
    ],
    ...
  }
...
```

Comments are useful for documenting your command logic. Even if a comment contains variables like `$name`, it will still be ignored.

## Alias Executor

The _alias executor_ is used when you want to share the standard input/output/error, and the parameters with that instruction.
For all the executors, the instructions will be executed in an isolated environment (with its own input/output/error), but the
alias will execute it using the same environment you're using to execute the _aux4_ command.

Here are some examples. First we can use the same we used in the previous example for the [stdin executor](#stdin-executor).

```json
...
  {
    "name": "get-first-item",
    "execute": [
      "alias:jq '.[0]'"
    ],
    ...
  }
...
```

You also can redirect the input for that.

```bash
> cat array.json | aux4 get-first-item
```

```json
{
  "name": "john",
  "age": 30
}
```

In the next example, let's say we have two commands, one of them uses [confirm executor](#confirm-executor) (line 13).
Note that this line uses three variables: `name`, `env`, and `time`.

The `env` variable is set in the line 5, by the [set executor](#set-executor).
The `time` variable is set in the line 6 by passing it as a [parameter](/variables#parameter).
The `name` variable is not defined anywhere, so it's expected as a parameter of `execute` command.

```json
...
  {
    "name": "execute",
    "execute": [
      "set:env=dev",
      "alias:aux4 confirm --time today"
    ],
    ...
  },
  {
    "name": "confirm",
    "execute": [
      "confirm:${name} are you sure you want to run it in ${env} ${time}",
      "log:confirmed"
    ],
    "help": {
      ...
      "variables": [
        {
          "name": "name",
          "text": "the name"
        },
        {
          "name": "env",
          "text": "the environment"
        },
        {
          "name": "time",
          "text": "the execution time"
        }
      ]
    }
    ...
  }
...
```

When we run the `execute` command passing the name as parameter. You can see all the parameters are passed to the `confirm` command.

```bash
> aux4 execute --name David
```

```text
? David are you sure you want to run it in dev today? [y/N] |
```

However, if you don't pass the `name` as parameter, the `confirm` command will prompt the user to input it.

```bash
> aux4 execute
```

```text
✔ name the name: |
```

## aux4 Executor

The _aux4 executor_ runs another aux4 command in-process without spawning a new shell. Use the `aux4:` prefix or `aux4 ` prefix. The command runs in the `main` profile.

```json
...
  {
    "name": "deploy",
    "execute": [
      "aux4:config get --name production",
      "log:Deploying to ${response.host}"
    ],
    ...
  }
...
```

This is faster than using the command-line executor to call `aux4` because it doesn't create a new process.

## When Executor

The _when executor_ conditionally executes a command only if the condition matches. Unlike `if()` which always runs one branch (`&&` or `||`), `when:` silently skips when the condition is not met.

```text
when:<condition>:<command>
```

### Equality

Use `==` and `!=` to compare values:

```json
...
  {
    "name": "greet",
    "execute": [
      "when:${env}==prod:log:Running in production",
      "when:${env}!=prod:log:Running in ${env}"
    ],
    ...
  }
...
```

```bash
> aux4 greet --env prod
```

```text
Running in production
```

```bash
> aux4 greet --env dev
```

```text
Running in dev
```

### Truthy Check

When no operator is used, the condition checks if the value is non-empty:

```json
...
  {
    "name": "check",
    "execute": [
      "when:${value}:log:Value is ${value}",
      "log:done"
    ],
    ...
  }
...
```

```bash
> aux4 check
```

```text
done
```

```bash
> aux4 check --value hello
```

```text
Value is hello
done
```

### Numeric Comparison

Use `>`, `<`, `>=`, `<=` for numeric comparisons:

```json
...
  {
    "name": "check-count",
    "execute": [
      "when:${count}>5:echo more than 5",
      "when:${count}<=5:echo 5 or less"
    ],
    ...
  }
...
```

```bash
> aux4 check-count --count 10
```

```text
more than 5
```

### Logical Operators

Use `&&` (AND) and `||` (OR) to combine conditions:

```json
...
  {
    "name": "check-range",
    "execute": [
      "when:${age}>=18 && ${age}<65:echo working age",
      "when:${age}<18:echo minor",
      "when:${age}>=65:echo senior"
    ],
    ...
  }
...
```

```bash
> aux4 check-range --age 30
```

```text
working age
```

```json
...
  {
    "name": "check-mode",
    "execute": [
      "when:${mode}==debug || ${verbose}==true:log:Debug enabled"
    ],
    ...
  }
...
```

### Nested Executors

The command after the condition can use any executor prefix:

```json
...
  {
    "name": "setup",
    "execute": [
      "when:${mode}==set:set:greeting=hello world",
      "when:${mode}==set:log:${greeting}"
    ],
    ...
  }
...
```

```bash
> aux4 setup --mode set
```

```text
hello world
```

---

## See Also

- [Variables](/variables)
- [Functions](/functions)
- [Config](/config)
- [Commands](/commands)
- [Hooks](/hooks)
- [Render](/render)
- [Testing](/testing)
- [Package Manager](/package-manager)
- [Packaging](/packaging)
- [Docker](/docker)
