HOWTO · C

How to Use the execlp Function in C

This article introduces how to use the execlp function in C

On this page

To launch a program by name from C, call execlp(file, argv0, ..., (char *)NULL). execlp searches the directories in PATH when file has no slash; a name containing a slash is treated as a relative or absolute path. A successful call replaces the current process image, so a parent that must continue should fork first.

Use execlp to Execute a New Program Using Filename in C

The exec functions provide a library interface for replacing a process image. They share the same core behavior but differ in how the program is selected and how arguments are supplied. In execlp, the p means PATH-based lookup: a bare name such as date is searched in PATH, while ./tool or /usr/bin/date is used as a path. By contrast, execve takes a path and explicit environment, and execvp accepts an argument vector while using PATH lookup.

The l in execlp indicates a list of arguments supplied as variadic parameters. After file, pass argv[0], then each argument in order, and finish with a null pointer of the correct type, commonly (char *)NULL. argv[0] conventionally identifies the program to the new process; it need not be identical to file. The sentinel is required so the function can find the end of the argument list. Do not pass an integer 0 as the variadic sentinel, because variadic arguments do not receive the same pointer conversion as a fixed-parameter null pointer constant.

This POSIX example reads a program name, launches it in a child process, and resumes the prompt after the child finishes. Press Ctrl+D at the prompt to finish input. It launches the selected program without additional command-line arguments. Compile it with a C compiler on a POSIX system, for example with cc launcher.c -o launcher. A bare command such as date is looked up through PATH; enter ./tool to select a file in the current directory.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

enum { MAXLINE = 256 };

int main(void) {
  char buf[MAXLINE];
  printf("%% ");
  fflush(stdout);

  while (fgets(buf, sizeof buf, stdin) != NULL) {
    size_t len = strlen(buf);
    if (len > 0 && buf[len - 1] == '\n') {
      buf[--len] = '\0';
    } else if (!feof(stdin)) {
      int ch;
      while ((ch = getchar()) != '\n' && ch != EOF) {}
      fprintf(stderr, "input line is too long\n");
      printf("%% ");
      fflush(stdout);
      continue;
    }

    if (len == 0) {
      printf("%% ");
      fflush(stdout);
      continue;
    }

    pid_t pid = fork();
    if (pid < 0) {
      perror("fork");
    } else if (pid == 0) {
      execlp(buf, buf, (char *)NULL);
      perror("execlp");
      _exit(127);
    } else {
      int status;
      while (waitpid(pid, &status, 0) < 0) {
        if (errno != EINTR) {
          perror("waitpid");
          break;
        }
      }
    }
    printf("%% ");
    fflush(stdout);
  }

  putchar('\n');
  return 0;
}

The prompt is interactive, so this example has no fixed transcript to reproduce. Each line is bounded by the input buffer; an overlong line is discarded before another prompt appears. An empty line simply returns to the prompt. If execlp succeeds, it does not return in the child. If it fails, perror writes the reason to standard error and the child exits with status 127. execlp treats its first argument as a file name or path and passes the remaining arguments to the new program; it does not parse that input as a shell command or interpret shell syntax such as pipes, redirection, quoting, variables, or wildcards. Shell built-ins such as cd are not executable files. One exception is a found file that produces an executable-format error: the p variants, including execlp, invoke a command interpreter for that file. This fallback does not make execlp a shell parser. To use shell command parsing, invoke a shell explicitly and pass the command string as an argument, taking care not to execute untrusted input.

To supply command-line arguments, use execvp with an argument vector. The following version accepts one line, splits it on whitespace, and launches the first word as the program. It limits the line and argument count, skips leading whitespace, and handles a blank line without accessing a missing token. It also avoids allocating a duplicate of the input: strtok edits the buffer in place, and argv points into that buffer until the child is created.

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

enum { MAXLINE = 256, MAXARGS = 48 };

int main(void) {
  char buf[MAXLINE];
  char *args[MAXARGS];

  printf("%% ");
  fflush(stdout);
  while (fgets(buf, sizeof buf, stdin) != NULL) {
    size_t len = strlen(buf);
    if (len > 0 && buf[len - 1] == '\n') {
      buf[--len] = '\0';
    } else if (!feof(stdin)) {
      int ch;
      while ((ch = getchar()) != '\n' && ch != EOF) {}
      fprintf(stderr, "input line is too long\n");
      printf("%% ");
      fflush(stdout);
      continue;
    }

    size_t count = 0;
    char *token = strtok(buf, " \t\r\n");
    while (token != NULL) {
      if (count == MAXARGS - 1) {
        fprintf(stderr, "too many arguments\n");
        count = 0;
        break;
      }
      args[count++] = token;
      token = strtok(NULL, " \t\r\n");
    }
    if (count == 0) {
      printf("%% ");
      fflush(stdout);
      continue;
    }
    args[count] = NULL;

    pid_t pid = fork();
    if (pid < 0) {
      perror("fork");
    } else if (pid == 0) {
      execvp(args[0], args);
      perror("execvp");
      _exit(127);
    } else {
      int status;
      while (waitpid(pid, &status, 0) < 0) {
        if (errno != EINTR) {
          perror("waitpid");
          break;
        }
      }
    }
    printf("%% ");
    fflush(stdout);
  }
  return 0;
}

Whitespace splitting is useful for simple input, but it is not shell parsing: quoted phrases remain split, and variables, wildcard expansion, pipelines, and redirection are not handled. Shell built-ins are also absent. For shell syntax, invoke a shell explicitly and pass its command string as an argument, taking care not to execute untrusted input. For ordinary programs, use execvp when arguments are already available as separate strings; use execlp when the argument list is fixed at the call site. In either case, a successful exec replaces the child, while a failure returns to the caller, so handle that return path and let the parent collect the child with waitpid. waitpid can be interrupted by a signal, which is why the example retries it for EINTR; a different wait error is reported and does not prevent the next prompt. The examples use a direct executable lookup and simple whitespace-delimited arguments. They do not implement a shell, expand user input, or interpret quoting rules.