> ## Documentation Index
> Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Bash Coprocess

A Bash coprocess is an asynchronous background process spawned by the shell with a bidirectional inter-process communication (IPC) channel automatically established. Initiated using the `coproc` reserved word, it connects the standard input (stdin) and standard output (stdout) of the executed command to file descriptors accessible within the parent shell's execution context.

## Syntax

A coprocess can be declared anonymously (using the default name) or with an explicit identifier.

```bash theme={"dark"}

# Default anonymous coprocess
coproc command [args]


# Named coprocess using a compound command
coproc NAME { command [args]; }
```

*Note: When declaring a named coprocess, a compound command (such as `{ ...; }`) must be used. If a simple command is provided (e.g., `coproc NAME echo "data"`), Bash parses the intended `NAME` as the command itself and the subsequent words as its arguments.*

## State and File Descriptors

When a coprocess is invoked, Bash allocates a two-way pipe and populates specific variables in the parent shell to manage the IPC state. If no explicit `NAME` is provided, Bash defaults to the identifier `COPROC`.

1. **File Descriptor Array (`NAME`)**: Bash creates an indexed array containing two file descriptors.
   * `${NAME[0]}`: Connected to the standard output of the coprocess. The parent shell reads from this descriptor.
   * `${NAME[1]}`: Connected to the standard input of the coprocess. The parent shell writes to this descriptor.
2. **Process ID (`NAME_PID`)**: Bash stores the Process ID (PID) of the spawned background process in a variable suffixed with `_PID`.

## I/O Operations and Asynchronous Hazards

Interaction with the coprocess relies on standard Bash file descriptor redirection.

**Writing to the coprocess:**
To send data to the coprocess, redirect standard output to the file descriptor stored in index 1.

```bash theme={"dark"}
echo "input data" >&"${NAME[1]}"
```

**Reading from the coprocess:**
To retrieve data from the coprocess, redirect standard input from the file descriptor stored in index 0.

```bash theme={"dark"}
read -r response <&"${NAME[0]}"
```

**The "Ambiguous Redirect" Hazard:**
Because the coprocess runs asynchronously, it may terminate before the parent shell executes an I/O operation. When a coprocess terminates, Bash automatically and immediately closes the file descriptors and unsets the `NAME` array. If the parent shell attempts to evaluate an I/O redirection like `>&"${NAME[1]}"` after the background process has finished and the array has been unset, the expansion yields an empty string. This randomly results in an `ambiguous redirect` error if the coprocess finishes faster than the parent shell expects. Robust implementations must verify the existence of the file descriptor or the PID before attempting I/O.

## Lifecycle and Termination

The coprocess lifecycle is tied to the execution of its underlying command and the state of its file descriptors.

* **EOF Handling**: To signal the coprocess that no more input will be sent, the parent shell must explicitly close the write file descriptor. This sends an End-Of-File (EOF) to the coprocess's stdin. Because the `{varname}>&-` syntax requires a simple shell identifier and does not support array elements, the file descriptor must be closed dynamically.

  *Critical Safety Pitfall:* Due to the asynchronous cleanup mentioned above, if the coprocess terminates early, the `NAME` array is unset. If you use `eval "exec ${NAME[1]}>&-"`, the empty expansion results in `exec >&-`, which silently closes the parent shell's own standard output. This must be mitigated by using parameter expansion modifiers (like `:?`) or explicit checks:

```bash theme={"dark"}
# Method 1: Using eval with a strict safety check
eval "exec ${NAME[1]:?Coprocess already terminated}>&-"

# Method 2: Checking if the variable is set before assignment
if [[ -n "${NAME[1]:-}" ]]; then
    fd=${NAME[1]}
    exec {fd}>&-
fi
```

* **Automatic Cleanup**: When the coprocess terminates, Bash automatically closes both file descriptors and unsets the `NAME` array and `NAME_PID` variable from the parent shell's environment.
* **Concurrency Limit**: Bash officially supports only one active coprocess at a time. Attempting to spawn a second coprocess before the first terminates will generate a warning (`warning: execute_coproc: coproc [PID:NAME] already exists`) and may result in orphaned file descriptors.

## The Buffering Caveat

Because the IPC channel relies on standard pipes, the standard I/O streams of the command executing inside the coprocess are subject to block buffering by the C library (libc) when not connected to a pseudo-terminal (PTY).

If the coprocess command does not explicitly flush its output buffers, the parent shell will hang indefinitely on a `read` operation, resulting in a deadlock. This mechanical limitation requires the internal command to either natively support line-buffering or be wrapped in utilities that manipulate stream buffering behavior (e.g., `stdbuf -i0 -o0`).

<div
  style={{ 
display: "flex", 
justifyContent: "space-between", 
alignItems: "center", 
maxWidth: "754px", 
padding: "1rem 0",
marginBottom: "24px"
}}
>
  <span style={{ fontWeight: "bold", fontSize: "1.25rem", color: "var(--tw-prose-headings)", fontFamily: "Inter, ui-sans-serif, system-ui, sans-serif" }}>Tired of Poor Bash Skills? Fix That With Deep Grasping!</span>

  <a
    href="https://syntblaze.com"
    target="_blank"
    style={{ 
  marginLeft: "24px",
  textDecoration: "none", 
  backgroundColor: "#007AFF",
  color: "#ffffff", 
  padding: "6px 16px", 
  borderRadius: "16px",
  fontSize: "0.9rem",
  fontWeight: "600",
  textAlign: "center",
  transition: "background-color 0.2s ease"
}}
  >
    Learn More
  </a>
</div>

<div style={{ display: "flex", gap: "12px", flexWrap: "wrap" }}>
  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/skill-tracking.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=b9b0305c93bb501c9e767b5c76c88835" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/skill-tracking.png" />

  <img src="https://mintcdn.com/syntblazellc/23tyuOzaWS88qFlc/images/nuggets.png?fit=max&auto=format&n=23tyuOzaWS88qFlc&q=85&s=c86c80197299762989e9b882419b2109" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/nuggets.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/bite-sized-exercises.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=a65f9a38c37ff28ab73ed783c53c60e3" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/bite-sized-exercises.png" />
</div>

<div style={{ display: "flex", gap: "12px", flexWrap: "wrap", marginTop: "12px" }}>
  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/mastery-chain.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=748a1763454713e679260fbb95f154a2" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/mastery-chain.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/element-previews.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=242f61448ff5dd6deaaab2dccc13b507" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/element-previews.png" />

  <img src="https://mintcdn.com/syntblazellc/-L0ums_2lctDSZ1l/images/element-explanations.png?fit=max&auto=format&n=-L0ums_2lctDSZ1l&q=85&s=cf0fc1c31f9cd0fc26716781be05fbc9" style={{ width: "30%", minWidth: 60 }} width="621" height="1344" data-path="images/element-explanations.png" />
</div>
