Shell Pipe Wrapper Functions
Tags: shell, bash, linux, functions
Published: 2021-02-07 23:32:38

# Shell Pipe Wrapper Functions

Create pipe functions from any builtin command, function, or executable program in the Linux shell.

- [Shell Pipe Wrapper Functions](#shell-pipe-wrapper-functions)
  - [Disclaimer](#disclaimer)
  - [Piping Output In The Linux Command Line](#piping-output-in-the-linux-command-line)
    - [Pipe Efficiency](#pipe-efficiency)
    - [Pipe Availability](#pipe-availability)
  - [Pipe Wrapper Functions](#pipe-wrapper-functions)
    - [Pipe Wrapper Logic](#pipe-wrapper-logic)
  - [Examples](#examples)
    - [Pipe Wrapper Function Simple Example](#pipe-wrapper-function-simple-example)
      - [Simple Example STAT Program](#simple-example-stat-program)
        - [STAT Program Function Wrapper](#stat-program-function-wrapper)
        - [STAT Program Function Example](#stat-program-function-example)
    - [Pipe Wrapper Function Advanced Example](#pipe-wrapper-function-advanced-example)
      - [Advanced Example RM Program](#advanced-example-rm-program)
        - [RM Program Function Wrapper](#rm-program-function-wrapper)
        - [RM Program Function Example](#rm-program-function-example)
  - [Conclusion](#conclusion)

## Disclaimer

![Intermediate](https://img.shields.io/static/v1.svg?label=Skill&message=Intermediate&color=ffc40d)![Beginner Friendly](https://img.shields.io/static/v1.svg?label=But&message=Beginner%20Friendly&color=00a300)

For those who mention `xargs`this an alternative method that makes extra logic processing simpler... And I don't like using `xargs`.

This post assumes that you are familiar with the Linux command line, commands, shell functions, std(in|out), and piping but it should not be too difficult to understand if you are new.

## Piping Output In The Linux Command Line

### Pipe Efficiency

Piping output is not always the best or fastest way to do things and sometimes can be quite redundant if the command in question has it's own methods of processing output (e.g. `find -exec {} \;`) as each part of a pipeline is a subshell, but for many things this inefficiency is negligible and sometimes a pipe is almost necessary or, at least, the best way to process output.

### Pipe Availability

We all have more than likely used a pipe `|` in the Linux shell environment to pipe stdout to another builtin command, function, or executable and eventually we realize that not everything can be piped to.

In order to pipe output to a command the command must process `stdin` (as you would user input) and not all programs do so.

## Pipe Wrapper Functions

In this post I will show you how I write a sort of *generic* shell function for any command, function, or binary file program that doesn't already process stdin and can be used almost completely in place of or with the original command.

This will not be a 100% tutorial, but the examples will have `documenting comments` and I will explain as I go along.

### Pipe Wrapper Logic

The basic logic is as follows:

1. Test if the file descriptor (`FD`) is greater than 0 (check if there is stdin already in the piped subshell):
   1. Process the input in whatever way, usually `while` reading the input passing the input with pass arguments to the command, function, or file.
2. If the function has not (`else`) received `stdin`:
   1. Only pass arguments to the command, function, or file.

## Examples

These examples all share the same simple construct/concept while only varying on logic that we need inside the `if [ ! -t 0 ]; then` block and more often than not, only in the `while` block.

Most of the time we will only need the basics of what is written here and not extra logic/expression.

```Shell
function command_pipe {
    if [ ! -t 0 ]; then
        local input
        # input logic here
        # while read or otherwise to create $input
        command "$@" "$input"
    else
        command "$@"
    fi
}
```

### Pipe Wrapper Function Simple Example

Most common usage.

#### Simple Example STAT Program

This wraps the `stat` command to be able to accept standard input, especially from the `stdout` of a *piped* command at the same time allowing you to still pass normal arguments to the command. 

##### STAT Program Function Wrapper

```Shell
function stat_pipe {
    if [ ! -t 0 ]; then # Test if there is input
        local input
        while read -r input; do # read each input 
            if  [[ -f "$input" ]] || # this is extra logic
                [[ -d "$input" ]]; then # more extra logic
                stat "$@" "$input" # process input with command and arguments
            fi
        done
    else stat "$@"; fi # if no input then run the command as usual with arguments
}
```

##### STAT Program Function Example

Print my `.bash_func` files and get the file sizes

```Shell
 $ printf '%s\n' .bash_func*
.bash_funcs
.bash_funcs_nocomments
.bash_funcs.old
 $ printf '%s\n' .bash_func* | stat_pipe -c %s
90329
53140
59990
 $ stat_pipe -c %s .bash_func*
90329
53140
59990
```

### Pipe Wrapper Function Advanced Example

Something a little more advanced.

#### Advanced Example RM Program

A wrapper for the `rm` command that accepts more user input other than the initial input to decide if you want to delete a file or folder or not; if the `else`  block is executed (not from pipe) then it runs the command as normal.

You should, of course, be careful with `rm` and therefore I added the extra test logic to add extra security.

This example is dependant on *Bash* as the shell.

##### RM Program Function Wrapper

```Shell
function rm_pipe {
    if [[ ! -t 0 ]]; then
        local input input_user
        while read -r input; do
            if  [[ -f "$input" ]] ||
                [[ -d "$input" ]]; then
                printf 'Would you like to delete: %s: (y/[N])?\n' "$input"
                read -u 1 input_user # '-u 1' read from keyboard rather than stdin; defaults to No.
                if [[ "$input_user" =~ ^([yY]|[yY][eE][sS])$ ]]; then
                    rm "$@" "$input" # if user input is yes then process...
                fi
            fi
        done
    else rm "$@"; fi
}
```

##### RM Program Function Example

```Shell
 $ cd fake
 $ printf '%s\n' *
a
b
c
d
 $ printf '%s\n' * | rm_pipe -rf
Would you like to delete: a: (y/[N])?
y 
Would you like to delete: b: (y/[N])?

Would you like to delete: c: (y/[N])?
no
Would you like to delete: d: (y/[N])?
yes
 $ printf '%s\n' *
b
c
 $
```

## Conclusion

This makes almost any builtin command, function, or executable file program accessible to pipe in the Linux command line. I make these types of functions for many things and they end up saving lots of time and effort so I don't always have to write extra array creating logic first and/or the function does my logic for me.