# !exec

The `!exec` Atmos YAML function is used to execute shell scripts and assign
the results to the sections in Atmos stack manifests.

## Availability and evaluation

Supported locations:

- **CLI configuration** (`atmos.yaml`, `atmos.d`, and profiles): during configuration loading.
- **Resolved stack values**: after inheritance, merging, and component template processing.
- **Workflow prompt fields**: `default`, `prompt`, `placeholder`, and `options` where supported by the `input`, `confirm`, `choose`, `filter`, `file`, and `write` step types, before the step executes.

Automatic evaluation requires native YAML tags from edition `2026-10-20`, including unpinned projects. Quoted function strings remain literal; earlier editions retain legacy string-form behavior.

See [YAML function scope](/functions/yaml/scope) for field restrictions, import timing, and native tags versus quoted strings.

## Usage

```yaml
var1: !exec echo 42

var2: !exec get-data.sh

var3: !exec atmos terraform output <component> -s <stack> --skip-init -- -json test_label_id

var4: !exec atmos terraform output <component> -s {{ .stack }} --skip-init -- -json test_map

var5: !exec atmos terraform output <component> -s {{ .stack }} --skip-init -- -json test_list

var6: |
  !exec
    foo=0
    for i in 1 2 3; do
      foo+=$i
    done
    echo $foo
```

You can execute inline shell scripts, scripts defined in external files, or even Atmos or Terraform commands
(e.g. `!exec atmos terraform output`).

:::warning
`!exec atmos terraform output` is just as examples to show that you can get the outputs of an Atmos component using
the `!exec` YAML function, but it's not recommended to use.
Instead, use the [`!terraform.output`](/functions/yaml/terraform.output)
YAML function. It produces the same results, correctly handles complex types (lists and maps),
has a much simpler syntax, and automatically caches the results for the same component and stack (so if you are calling
it many times on the same component in the same stack, it will execute `terraform output` only once and the stack
processing will be much faster)
:::

The result of a script execution can be a simple type (string, number, or boolean), in which case Atmos
assigns it without modification.

If the result is a complex type (list, map or object), the script must return it as a JSON-encoded string.
After receiving the JSON-encoded string, Atmos automatically decodes it into the corresponding YAML complex type.

:::info
Atmos uses the [`interp`](https://pkg.go.dev/mvdan.cc/sh/v3@v3.10.0/interp) `Go` package to execute the shell scripts
in the `!exec` YAML function.

Package `interp` implements an interpreter that executes shell programs.
It aims to support POSIX and to behave like Bash, but it does not support all of its features.
:::

:::tip
You can use [Atmos Stack Manifest Templating](/templates) in the `!exec` YAML function expressions.
In stack component values, Atmos processes templates before executing the `!exec` function, allowing you to provide the parameters to
the function dynamically.
:::

## Concurrent execution

Configure [`settings.functions.exec.max_concurrency`](/cli/configuration/settings/functions/exec) in
`atmos.yaml` to allow multiple ordinary `!exec` expressions in a document to run concurrently:

```yaml
settings:
  functions:
    exec:
      max_concurrency: 4
```

Your stack entries stay unchanged:

```yaml
vars:
  network: !exec ./lookup-network
  cluster: !exec ./lookup-cluster
  registry: !exec ./lookup-registry
```

The limit defaults to `1` for serial execution and must be a positive integer. With a higher limit, calls can
finish in any order; results retain their original YAML locations and types. Atmos does not detect shell
side-effect dependencies. Users enabling concurrency are responsible for coordinating shared files and other
external state. Skipped `!exec` expressions remain unexecuted.

Commands inherit Atmos standard input. Concurrent commands share that input stream; input is not broadcast
to every command, and which command reads each part is not guaranteed. Coordinate commands that read stdin,
or set the limit to `1` when they must read serially.

:::note Type-Aware Merging
Atmos supports type-aware merging of YAML functions and concrete values, allowing them to coexist in the inheritance chain without type conflicts.
See the full explanation: [YAML Function Merging](/reference/yaml-function-merging)
:::
