Pipelines Without Pipes

The pipeline program constructs a pipeline from its arguments. The first argument is the separator. The program splits the command line on separator arguments and constructs a pipeline from the components.

Here’s what it looks like in use:

Image

What good does this do? If you dynamically assemble a pipeline in a shell program, you must pass it through shell parsing in order to execute it. This brings all of the difficulties of argument quoting into play. The pipeline construction does this in a safe way. The caller can manipulate the entire pipeline as a set of ordinary arguments without worrying about interpolation problems.

The underlying code looks like this:

pipeline_cmd() {
  local prefix="$1"
  local sep="$2"
  shift 2

  local cmd=''
  local i=3
  local p='${1}'
  for a in "$@"
  do
    if test "$a" = "${sep}"
    then
      cmd="${cmd} |"
      p='${1}'
    else
      cmd="${cmd} \"${p}\${$i}\""
      p=''
    fi
    i=$((i + 1))
  done

  printf '%s\n' "${cmd}"
}

# pipeline prefix sep prog1 [sep prog2 ...]
pipeline() {
  eval "$(pipeline_cmd "$@")"
}

The pipeline program calls the pipeline function with an empty first argument. You can find the code here.