A collection of functional programming tools for the shell.
This project provides higher order functions like map
, filter
and foldl
as simple command-line tools.
Following the UNIX philosophy, these commands are designed to be composed via pipes. A
large collection of functions such as basename
, replace
, contains
or is_dir
are provided as
arguments to these commands.
If you want to try it out on your own, run:
git clone https://github.com/sharkdp/shell-functools /tmp/shell-functools
export PATH="$PATH:/tmp/shell-functools/ft"
The map
command takes a function argument and applies it to every line of input:
> ls
document.txt
folder
image.jpg
> ls | map abspath
/tmp/demo/document.txt
/tmp/demo/folder
/tmp/demo/image.jpg
The filter
command takes a function argument with a Bool
ean return type. It applies that function to each input line and shows only those that returned true
:
> find
.
./folder
./folder/me.jpg
./folder/subdirectory
./folder/subdirectory/song.mp3
./document.txt
./image.jpg
> find | filter is_file
./folder/me.jpg
./folder/subdirectory/song.mp3
./document.txt
./image.jpg
The foldl
command takes a function argument and an initial value. The given function must be a binary function with two arguments, like add
or append
. The foldl
command then applies this function iteratively by keeping an internal accumulator:
Add up the numbers from 0 to 100:
> seq 100 | foldl add 0
5050
Multiply the numbers from 1 to 10:
> seq 10 | foldl mul 1
3628800
Append the numbers from 1 to 10 in a string:
> seq 1 10 | map append " " | foldl append ""
1 2 3 4 5 6 7 8 9 10
All of these commands can be composed by using standard UNIX pipes:
> find
.
./folder
./folder/me.jpg
./folder/subdirectory
./folder/subdirectory/song.mp3
./document.txt
./image.jpg
> find | filter is_file | map basename | map append ".bak"
me.jpg.bak
song.mp3.bak
document.txt.bak
image.jpg.bak
The --column
/ -c
option can be used to apply a given function to a certain column in the input line (columns are separated by tabs). Column arrays can be created by using functions such as duplicate
, split sep
or split_ext
:
> ls | filter is_file | map split_ext
document txt
image jpg
> ls | filter is_file | map split_ext | map -c1 to_upper
DOCUMENT txt
IMAGE jpg
> ls | filter is_file | map split_ext | map -c1 to_upper | map join .
DOCUMENT.txt
IMAGE.jpg
Here is a more complicated example:
> find -name '*.jpg'
./folder/me.jpg
./image.jpg
> find -name '*.jpg' | map duplicate
./folder/me.jpg ./folder/me.jpg
./image.jpg ./image.jpg
> find -name '*.jpg' | map duplicate | map -c2 basename
./folder/me.jpg me.jpg
./image.jpg image.jpg
> find -name '*.jpg' | map duplicate | map -c2 basename | map -c2 prepend "thumb_"
./folder/me.jpg thumb_me.jpg
./image.jpg thumb_image.jpg
> find -name '*.jpg' | map duplicate | map -c2 basename | map -c2 prepend "thumb_" | map run convert
Running 'convert' with arguments ['./folder/me.jpg', 'thumb_me.jpg']
Running 'convert' with arguments ['./image.jpg', 'thumb_image.jpg']
Get the login shell of user shark
:
> cat /etc/passwd | map split : | filter -c1 equal shark | map index 6
/usr/bin/zsh
You can call ft-functions
, to get an overview of all available arguments to map
, filter
, etc.:
abspath :: Path → Path
dirname :: Path → Path
basename :: Path → Path
is_dir :: Path → Bool
is_file :: Path → Bool
is_link :: Path → Bool
exists :: Path → Bool
has_ext ext :: Path → Bool
strip_ext :: Path → String
replace_ext new_ext :: Path → Path
split_ext :: Path → Array
non_empty :: * → Bool
nonempty :: * → Bool
add num :: Int → Int
sub num :: Int → Int
mul num :: Int → Int
eq other :: * → Bool
equal other :: * → Bool
equals other :: * → Bool
ne other :: * → Bool
not_equal other :: * → Bool
not_equals other :: * → Bool
ge i :: Int → Bool
greater_equal i :: Int → Bool
greater_equals i :: Int → Bool
gt i :: Int → Bool
greater i :: Int → Bool
greater_than i :: Int → Bool
le i :: Int → Bool
less_equal i :: Int → Bool
less_equals i :: Int → Bool
lt i :: Int → Bool
less i :: Int → Bool
less_than i :: Int → Bool
append suffix :: String → String
strip :: String → String
substr start end :: String → String
take count :: String → String
to_lower :: String → String
to_upper :: String → String
replace old new :: String → String
prepend prefix :: String → String
capitalize :: String → String
drop count :: String → String
duplicate :: String → Array
contains substring :: String → Bool
starts_with pattern :: String → Bool
startswith pattern :: String → Bool
len :: String → Int
length :: String → Int
at idx :: Array → String
index idx :: Array → String
join separator :: Array → String
split separator :: String → Array
const value :: * → *
run command :: Array → !
id :: * → *
identity :: * → *