Implement piping between commands in backtick syntax (issue #06)
Add split_pipeline() to split command strings on unquoted | and exec_pipeline() to fork N children connected by inter-stage pipes. Only the last stage's stdout/stderr are captured; middle stages' stderr is inherited. Exit code comes from the last stage.
This commit is contained in:
@@ -1,25 +1,40 @@
|
||||
# 06 — Implement piping between commands
|
||||
|
||||
**Status:** open (post-core)
|
||||
**Blocked by:** #03, #04
|
||||
**Status:** done
|
||||
|
||||
## Syntax
|
||||
|
||||
Pipes are written inside backtick strings using `|`:
|
||||
|
||||
```lua
|
||||
local result = `ls -l` | `grep ".lua"`
|
||||
local r = `ls -l | grep ".lua" | wc -l`
|
||||
```
|
||||
|
||||
The `|` operator between backtick expressions remains Lua's bitwise OR — pipes only work *within* a single backtick command string.
|
||||
|
||||
## Implementation
|
||||
|
||||
- Connect stdout of one child to stdin of the next via `pipe()` + `dup2()`
|
||||
- Build a pipeline of N processes, all forked and connected
|
||||
- Only capture stdout/stderr of the **final** process
|
||||
- `waitpid()` all children, return exit code of last process
|
||||
Implemented in `lcmd.c` with two new functions:
|
||||
|
||||
## Challenge
|
||||
- **`split_pipeline()`** — scans the command string for `|` outside single/double quotes, splits into an array of stage strings (up to 64 stages). Reuses the same quote-tracking logic as `parse_argv()`.
|
||||
|
||||
Lua uses `|` for bitwise OR. The parser needs to disambiguate:
|
||||
- `|` between two command expressions → pipe
|
||||
- `|` between numeric expressions → bitwise OR
|
||||
- **`exec_pipeline()`** — executes a multi-stage pipeline:
|
||||
1. `parse_argv()` each stage
|
||||
2. Creates N-1 inter-stage pipes + stdout/stderr capture pipes for the last stage
|
||||
3. Forks N children with appropriate stdin/stdout wiring
|
||||
4. Captures output from the last stage only
|
||||
5. Returns `{code=last_exit_code, stdout=captured, stderr=captured}`
|
||||
|
||||
This is resolvable because the parser knows the type of each subexpression — a backtick expression is always a command.
|
||||
`luaB_command()` calls `split_pipeline()` first. Single-stage commands (no `|`) fall through to the original single-command codepath unchanged.
|
||||
|
||||
## Behaviour
|
||||
|
||||
- Exit code is from the **last** pipeline stage (like bash)
|
||||
- Only the last stage's stdout/stderr are captured in the result table
|
||||
- Middle stages' stderr is inherited (goes to terminal)
|
||||
- Quoted `|` characters (single or double quotes) are not pipe separators
|
||||
- Empty pipeline stages (e.g. `cmd1 || cmd2` or leading/trailing `|`) are errors
|
||||
|
||||
## Tests
|
||||
|
||||
See `testes/lush/piping.lua`.
|
||||
|
||||
Reference in New Issue
Block a user