bash-fun

repository·master·Indexed 43 days ago

https://github.com/ssledz/bash-fun

A library of functional programming primitives for Bash, providing tools such as map, filter, fold, and tuple manipulation to write more expressive shell scripts. Includes utilities for list slicing, joining, error handling with try and catch, and a maybe pattern for optional values.

Tokens
1.9K
Snippets
12
Records
12
Agent score
6%

What's inside bash-fun

  1. Install and load bash-fun

    master

    To use bash-fun, you need to source the fun.sh file into your script. You can use a one-liner that checks for the local file and downloads it via curl if it's missing:

    #!/bin/bash
    . <(test -e fun.sh || curl -Ls https://raw.githubusercontent.com/ssledz/bash-fun/master/src/fun.sh > fun.sh; cat fun.sh)
    #!/bin/bash
    . <(test -e fun.sh || curl -Ls https://raw.githubusercontent.com/ssledz/bash-fun/master/src/fun.sh > fun.sh; cat fun.sh)
    
    seq 1 4 | sum
  2. Error handling with try and catch

    master

    Use try to execute a command and capture its exit status, and catch to handle errors with detailed context.

    • try λ [args] . 'command': Executes a command. The lambda receives the exit status.
    • catch λ cmd status val . 'command': Provides the command, its exit status, and the output value to a handler.
    # Try capturing exit status
    $ echo 'expr 2 / 0' | try λ status . 'echo $status'
    2
    
    # Catching errors with context
    $ echo 'expr 2 / 0' \
        | LANG=en catch λ cmd status val . 'echo cmd=$cmd,status=$status,val=$val'
    cmd=expr 2 / 0,status=2,val=(expr:,division,by,zero)
    $ echo 'expr 2 / 0' | try λ status . 'echo $status'
    $ echo 'expr 2 / 0' | LANG=en catch λ cmd status val . 'echo cmd=$cmd,status=$status,val=$val'
  3. Slice and select list elements

    master

    The following functions allow you to select specific parts of a list:

    • take N: Returns the first N elements.
    • drop N: Returns all elements except the first N.
    • lhead: Returns the first element.
    • ltail: Returns all elements except the first.
    • last: Returns the last element.
    $ list 1 2 3 4 | drop 2
    3
    4
    
    $ list 1 2 3 4 5 | lhead
    1
    
    $ list 1 2 3 4 | ltail
    2
    3
    4
    
    $ list 1 2 3 4 5 | last
    5
    
    $ list 1 2 3 4 5 | take 2
    1
    2
    $ list 1 2 3 4 | drop 2
    $ list 1 2 3 4 5 | lhead
    $ list 1 2 3 4 | ltail
    $ list 1 2 3 4 5 | last
    $ list 1 2 3 4 5 | take 2
  4. Filter list elements

    master

    The filter function keeps only the elements for which the provided function returns a truthy value. Use ret true or ret false within lambdas to control filtering.

    $ seq 1 10 | filter λ a . '[[ $(mod $a 2) -eq 0 ]] && ret true || ret false'
    2
    4
    6
    8
    10
    $ seq 1 10 | filter λ a . '[[ $(mod $a 2) -eq 0 ]] && ret true || ret false'
  5. Transform lists with map

    master

    The map function applies a function (often a lambda λ) to every element in a list. You can pass a raw command string or a lambda expression.

    # Using lambda for arithmetic
    $ seq 1 5 | map λ a . 'echo $((a + 5))'
    6
    7
    8
    9
    10
    
    # Using a simple command
    $ list 1 2 3 | map echo
    1
    2
    3
    
    # Using a command with positional arguments
    $ list 1 2 3 | map 'echo $ is a number'
    1 is a number
    2 is a number
    3 is a number
    $ seq 1 5 | map λ a . 'echo $((a + 5))'
    $ list 1 2 3 | map echo
    $ list 1 2 3 | map 'echo $ is a number'
  6. List and Unlist data

    master

    Use list to format input into a list (one item per line) and unlist to convert a list back into a single space-separated line.

    $ list 1 2 3
    1
    2
    3
    
    $ list 1 2 3 4 5 | unlist
    1 2 3 4 5
    $ list 1 2 3
    $ list 1 2 3 4 5 | unlist
  7. File and type predicates

    master

    A collection of utility functions for checking file properties and data types:

    • isint: Returns true if input is an integer.
    • isempty: Returns true if input is empty.
    • isfile: Checks if path is a file.
    • isdir: Checks if path is a directory.
    • isreadable: Checks if path is readable.
    • iswritable: Checks if path is writable.
    • isnonzerofile: Checks if file exists and is not empty.
    • not: Inverts a boolean.
    $ isint 42
    true
    
    $ list /etc/passwd /tmp /no_such_file | filter λ a . 'isfile $a'
    /etc/passwd
    /tmp
    
    $ list 1 2 "" c d 6 | filter λ a . 'not isempty $a'
    1
    2
    c
    d
    6
    $ isint 42
    $ list /etc/passwd /tmp /no_such_file | filter λ a . 'isfile $a'
    $ list 1 2 "" c d 6 | filter λ a . 'not isempty $a'
  8. Reduce lists with foldl and foldr

    master

    Folding functions reduce a list to a single value using an accumulator.

    • foldl λ acc el . '...': Left fold (starts from the first element).
    • foldr λ acc el . '...': Right fold (starts from the last element).
    # Left fold example
    $ list a b c d | foldl λ acc el . 'echo -n $acc-$el'
    a-b-c-d
    
    # Right fold example
    $ list '' a b c d | foldr λ acc el . 'if [[ ! -z $acc ]]; then echo -n $acc-$el; else echo -n $el; fi'
    d-c-b-a
    
    # Arithmetic foldl
    $ seq 1 4 | foldl λ acc el . 'echo $(($acc + $el))'
    10
    $ list a b c d | foldl λ acc el . 'echo -n $acc-$el'
    $ list '' a b c d | foldr λ acc el . 'if [[ ! -z $acc ]]; then echo -n $acc-$el; else echo -n $el; fi'
    $ seq 1 4 | foldl λ acc el . 'echo $(($acc + $el))'
  9. Create and manipulate tuples

    master

    Tuples are used to group related values.

    • tup: Creates a tuple from arguments.
    • tupl: Returns the first element of a tuple.
    • tupr: Returns the last element of a tuple.
    • tupx: Selects specific elements from a tuple using indices (supports single index, comma-separated, or ranges).
    • ntup: Creates nested tuples (encoded).
    • ntupl/ntupr: Accesses elements of nested tuples.
    • ntupx: Accesses elements of nested tuples.
    # Basic tuple creation
    $ tup a 1
    (a,1)
    
    # Accessing elements
    $ tup 'foo bar' 1 'one' 2 | tupl
    foo bar
    $ tup 'foo bar' 1 'one' 2 | tupr
    2
    
    # Selecting elements with tupx
    $ tup 'foo bar' 1 'one' 2 | tupx 1,3
    foo bar
    one
    
    # Nested tuples
    $ ntup 'foo bar' 1 one 2 | ntupl
    foo bar
    $ tup a 1
    $ tup 'foo bar' 1 'one' 2 | tupl
    $ tup 'foo bar' 1 'one' 2 | tupr
    $ tup 'foo bar' 1 'one' 2 | tupx 1,3
    $ ntup 'foo bar' 1 one 2 | ntupl
  10. Join list elements with a separator

    master

    Use join to concatenate list elements using a specified separator. You can also provide optional wrapping characters like brackets.

    $ list 1 2 3 4 5 | join ,
    1,2,3,4,5
    
    $ list 1 2 3 4 5 | join , [ ]
    [1,2,3,4,5]
    $ list 1 2 3 4 5 | join ,
    $ list 1 2 3 4 5 | join , [ ]
  11. Handle optional values with maybe

    master

    The maybe family of functions provides a way to handle potentially empty or invalid data using a Just or Nothing pattern.

    • maybe: Wraps a value in (Just, value) or returns (Nothing) if the input is empty/whitespace.
    • maybemap: Applies a function to the value inside a Just tuple, or returns (Nothing) if the input was empty.
    • maybevalue: Extracts the value from a Just tuple, or returns a DEFAULT value if it is Nothing.
    $ list Hello | maybe
    (Just,Hello)
    
    $ list "   " | maybe
    (Nothing)
    
    $ list Hello | maybe | maybemap λ a . 'tr oH Oh <<<$a'
    (Just,hellO)
    
    $ echo bash-fun rocks | maybe | maybevalue DEFAULT
    bash-fun rocks
    
    $ echo | maybe | maybevalue DEFAULT
    DEFAULT
    $ list Hello | maybe
    $ list "   " | maybe
    $ list Hello | maybe | maybemap λ a . 'tr oH Oh <<<$a'
    $ echo bash-fun rocks | maybe | maybevalue DEFAULT
  12. Debug pipelines with peek

    master

    The peek function allows you to inspect the data flowing through a pipeline without modifying it. It takes a lambda that is executed for every item in the stream.

    $ list 1 2 3 \
        | peek lambda a . echo 'dbg a : $a' \
        | map lambda a . 'mul $a 2' \
        | peek lambda a . echo 'dbg b : $a' \
        | sum
    
    dbg a : 1
    dbg a : 2
    dbg a : 3
    dbg b : 2
    dbg b : 4
    dbg b : 6
    12
    $ list 1 2 3 | peek lambda a . echo 'dbg a : $a' | map lambda a . 'mul $a 2' | peek lambda a . echo 'dbg b : $a' | sum