# Process Management

### Process Management

A process is a running program.

When you run:

```bash
zig build-exe main.zig
./main
```

the operating system creates a process for `./main`.

A process has memory, open files, environment variables, command-line arguments, and an exit code. It may also create other processes.

#### Command-Line Arguments

Command-line arguments are text values passed to a program when it starts.

Example:

```bash
./hello Alice
```

Here, `Alice` is an argument.

A simple Zig program can read arguments through `std.process`.

```zig
const std = @import("std");

pub fn main() !void {
    var args = try std.process.argsWithAllocator(std.heap.page_allocator);
    defer args.deinit();

    _ = args.next();

    const name = args.next() orelse {
        std.debug.print("usage: hello NAME\n", .{});
        return;
    };

    std.debug.print("Hello, {s}!\n", .{name});
}
```

The first argument is usually the program name, so this line skips it:

```zig
_ = args.next();
```

Then the program reads the next argument:

```zig
const name = args.next() orelse {
    std.debug.print("usage: hello NAME\n", .{});
    return;
};
```

#### Exit Codes

A process ends with an exit code.

By convention:

```text
0 means success
nonzero means failure
```

A command-line tool should return success when it did what the user asked. It should return failure when the command was invalid, a file could not be read, a network request failed, or another real error occurred.

In simple Zig programs, returning an error from `main` usually causes the program to fail.

```zig
pub fn main() !void {
    return error.SomethingWentWrong;
}
```

For user-facing tools, you often catch errors and print a clearer message.

#### Spawning a Child Process

A process can start another process. The new process is called a child process.

For example, a build tool might run a compiler. A test runner might run test executables. A shell runs commands as child processes.

The standard library provides process APIs for this through `std.process`.

The exact child process APIs may change across Zig versions, so check your local docs with:

```bash
zig std
```

The conceptual shape is:

```zig
var child = std.process.Child.init(&.{ "echo", "hello" }, allocator);
try child.spawn();
const result = try child.wait();
```

This starts the command:

```bash
echo hello
```

and waits for it to finish.

#### Waiting for a Process

Starting a child process is only half the job.

You usually also need to wait for it.

Waiting tells you how the child process ended.

It may have exited successfully.

It may have exited with a nonzero code.

It may have been terminated by the operating system.

A command runner should inspect the result rather than assuming success.

#### Capturing Output

Sometimes you want the child process output.

For example:

```bash
git rev-parse HEAD
```

prints the current Git commit hash.

A Zig program may want to capture that output into memory.

The conceptual pattern is:

```zig
const result = try std.process.Child.run(.{
    .allocator = allocator,
    .argv = &.{ "git", "rev-parse", "HEAD" },
});
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);
```

Then you can read:

```zig
result.stdout
result.stderr
```

Standard output is normal output.

Standard error is diagnostic output.

Keep them separate when possible.

#### Environment for Child Processes

A child process normally inherits environment variables from its parent.

Sometimes you want to pass a modified environment.

Examples:

```text
PATH
HOME
APP_ENV
DATABASE_URL
```

Build systems, package managers, and test runners often control child environments carefully.

The general idea is:

```zig
child.env_map = custom_environment;
```

or use a run API that accepts environment configuration.

The details depend on the exact Zig version.

#### Working Directory for Child Processes

A child process also has a current working directory.

You might want to run a command inside a project directory:

```bash
cd my-project
zig build
```

Programmatically, that means setting the child process working directory.

Conceptually:

```zig
child.cwd = "my-project";
```

Then the command runs as though it started inside that directory.

This is important for tools that operate on projects, repositories, or generated files.

#### Do Not Build Shell Commands as Strings

This is dangerous:

```zig
const command = try std.fmt.allocPrint(
    allocator,
    "rm -rf {s}",
    .{path},
);
```

If `path` contains unexpected characters, the shell may interpret them.

Prefer passing arguments as separate values:

```zig
&.{ "rm", "-rf", path }
```

This avoids shell parsing.

The operating system receives the command and arguments directly.

This is safer and clearer.

#### A Small Argument Parser

Here is a small program that expects a file name:

```zig
const std = @import("std");

pub fn main() !void {
    var args = try std.process.argsWithAllocator(std.heap.page_allocator);
    defer args.deinit();

    _ = args.next();

    const path = args.next() orelse {
        std.debug.print("usage: show PATH\n", .{});
        return;
    };

    std.debug.print("path = {s}\n", .{path});
}
```

Run it:

```bash
./show hello.txt
```

Output:

```text
path = hello.txt
```

Run it without an argument:

```bash
./show
```

Output:

```text
usage: show PATH
```

#### A Small Command Runner

This example shows the intended structure, though exact APIs may need adjustment for your installed Zig version:

```zig
const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    const result = try std.process.Child.run(.{
        .allocator = allocator,
        .argv = &.{ "zig", "version" },
    });
    defer allocator.free(result.stdout);
    defer allocator.free(result.stderr);

    std.debug.print("stdout:\n{s}\n", .{result.stdout});

    if (result.stderr.len != 0) {
        std.debug.print("stderr:\n{s}\n", .{result.stderr});
    }
}
```

This runs:

```bash
zig version
```

and captures its output.

#### Common Mistakes

Do not assume arguments exist.

Do not treat command-line arguments as trusted input.

Do not join shell commands into one string unless you intentionally need a shell.

Do not ignore child process exit status.

Do not mix stdout and stderr without reason.

Do not forget that environment variables and working directories affect child processes.

#### The Core Pattern

For arguments:

```zig
var args = try std.process.argsWithAllocator(allocator);
defer args.deinit();

_ = args.next();
const value = args.next() orelse return error.MissingArgument;
```

For a child process:

```zig
var child = std.process.Child.init(&.{ "program", "arg" }, allocator);
try child.spawn();
const result = try child.wait();
```

For captured output:

```zig
const result = try std.process.Child.run(.{
    .allocator = allocator,
    .argv = &.{ "program", "arg" },
});
defer allocator.free(result.stdout);
defer allocator.free(result.stderr);
```

#### What You Should Remember

A process is a running program.

Command-line arguments are text passed at startup.

Exit code `0` usually means success.

Nonzero exit codes usually mean failure.

A process can start child processes.

Child processes have arguments, environment variables, working directories, stdout, stderr, and exit status.

Pass command arguments as separate values, not as one shell string.

Process management is the foundation for command-line tools, build systems, test runners, package managers, and automation programs.

