Here's an interesting bit of...abuse :) Since Bash is effectively stringly-typed, it can be used as a functional programming language, with pipes similar to function composition.
e.g.: wtfp.sh
#!/usr/bin/env bash
map() {
local fn="$1"
local input
while read -r input; do
"${fn}" "${input}"
done
}
reduce() {
local fn="$1"
local init="$2"
local input
local output="${init}"
while read -r input; do
output="$("${fn}" "${output}" "${input}")"
done
echo "${output}"
}
filter() {
local fn="$1"
local input
while read -r input; do
"${fn}" "${input}" && echo "${input}"
done
}
add() {
echo $(( $1 + $2 ))
}
increment() {
echo $(( $1 + 1 ))
}
square() {
echo $(( $1 * $1 ))
}
even() {
return $(( $1 % 2 ))
}
sum() {
reduce add 0
}
map increment | map square | filter even | sum
This is the pipe mill[1] pattern. As you say, you can use it to mimic function composition from a functional paradigm. It can make for some elegant solutions to dealing with streams of data.
The issue is that pipe mills are very slow.
$ mill() { while read -r line; do echo $line; done }
$ export FIVE_MEGS=$(( 5 * 1024 ** 2 ))
$ time yes | mill | pv -S -s "$FIVE_MEGS" > /dev/null
5.00MiB 0:00:23 [ 221KiB/s] [============>] 100%
real 0m23.084s
user 0m14.121s
sys 0m26.780s
$ time yes | pv -S -s "$FIVE_MEGS" > /dev/null
5.00MiB 0:00:00 [6.55GiB/s] [============>] 100%
real 0m0.005s
user 0m0.000s
sys 0m0.006s
Even Python loops are faster.
$ export PYLOOP="from sys import stdin
for line in stdin:
print(line)"
$ time yes | python3 -c "$PYLOOP" | pv -S -s "$FIVE_MEGS" > /dev/null
5.00MiB 0:00:00 [67.1MiB/s] [============>] 100%
real 0m0.082s
user 0m0.071s
sys 0m0.019s
e.g.: wtfp.sh
...then: