# shell

import Intro from '@site/src/components/Intro'

<Intro>
The `shell` step type runs a shell command or script, letting your custom commands and workflows reach beyond Atmos to repository scripts, make targets, test commands, and any other tooling in your stack.
</Intro>

```yaml
steps:
  - name: test
    type: shell
    working_directory: !repo-root
    command: make test
```

## Fields

<dl>
  <dt>`command`</dt>
  <dd>Required. Shell command text.</dd>

  <dt>`working_directory`</dt>
  <dd>Optional directory where the command runs.</dd>

  <dt>`env`</dt>
  <dd>Optional environment variables for this step.</dd>

  <dt>`timeout`</dt>
  <dd>Optional [duration](/steps/retry#duration-values) such as `30s`, `5m`, or a bare number of seconds. When it elapses, Atmos cancels the command and fails the step with `step timed out`. The timeout bounds the whole step, including every [`retry`](/steps/retry) attempt and the wait between attempts, so retries never extend it.</dd>

  <dt>`tty`</dt>
  <dd>Allocate a pseudo-terminal for terminal-native commands.</dd>

  <dt>`interactive`</dt>
  <dd>Attach host stdin so the command can read from the terminal.</dd>
</dl>

Non-interactive shell steps can use [output modes](/steps/output). Steps with `tty` or `interactive` attach directly to the terminal and do not produce capturable stdout.

## Timeout and cancellation {#timeout-and-cancellation}

When a `shell` step times out or is canceled, for example with Ctrl-C, Atmos ends the whole process tree the command started, including grandchildren, and not only the shell. The same applies to every place Atmos runs a shell command: custom commands, workflows, the [`!exec`](/functions/yaml/exec) YAML function, and hook commands. A timeout overrun reads `step timed out`, and its explanation names the step and its type: `Step '<name>' (type shell) did not finish within its timeout of <duration>`.

A child whose stdin is a terminal, such as an editor or a prompt, stays in the terminal's foreground process group so it can keep reading the terminal. Atmos signals only that direct child. On Windows, Atmos stops only the direct child.
