{"id":13395248,"url":"https://github.com/ssledz/bash-fun","last_synced_at":"2025-03-13T20:31:59.126Z","repository":{"id":150931122,"uuid":"100421527","full_name":"ssledz/bash-fun","owner":"ssledz","description":"Functional programming in bash","archived":false,"fork":false,"pushed_at":"2019-10-07T08:27:31.000Z","size":65,"stargazers_count":328,"open_issues_count":3,"forks_count":21,"subscribers_count":10,"default_branch":"master","last_synced_at":"2024-07-31T17:23:43.372Z","etag":null,"topics":["bash","functional-programming"],"latest_commit_sha":null,"homepage":"http://ssledz.github.io/presentations/bash-fun.html#/","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ssledz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-08-15T21:38:57.000Z","updated_at":"2024-07-26T11:02:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"8cba3aa6-570a-4e05-949d-e077f5b7bddc","html_url":"https://github.com/ssledz/bash-fun","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssledz%2Fbash-fun","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssledz%2Fbash-fun/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssledz%2Fbash-fun/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssledz%2Fbash-fun/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ssledz","download_url":"https://codeload.github.com/ssledz/bash-fun/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243478343,"owners_count":20297238,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bash","functional-programming"],"created_at":"2024-07-30T17:01:47.660Z","updated_at":"2025-03-13T20:31:58.830Z","avatar_url":"https://github.com/ssledz.png","language":"Shell","funding_links":[],"categories":["Shell"],"sub_categories":[],"readme":"# Introduction\n\n[Introduction to fun.sh library](http://ssledz.github.io/presentations/bash-fun.html#/)\n\n# Quick start\n\n```bash\n#!/bin/bash\n. \u003c(test -e fun.sh || curl -Ls https://raw.githubusercontent.com/ssledz/bash-fun/master/src/fun.sh \u003e fun.sh; cat fun.sh)\n\nseq 1 4 | sum\n```\n\n# Functions overview\n|||||||\n|------|------|------|------|------|------|\n|**append**|**buff**|**call**|**catch**|**curry**|**div**|\n|**drop**|**dropw**|**factorial**|**filter**|**foldl**|**foldr**|\n|**isint**|**isempty**|**isfile**|**isnonzerofile**|**isreadable**|**iswritable**|\n|**isdir**|**join**|**lambda**|**last**|**lhead**|**list**|\n|**ltail**|**lzip**|**map**|**maybe**|**maybemap**|**maybevalue**|\n|**mod**|**mul**|**not**|**ntup**|**ntupl**|**ntupr**|\n|**ntupx**|**peek**|**plus**|**prepend**|**product**|**ret**|\n|**res**|**revers**|**revers_str**|**scanl**|**splitc**|**strip**|\n|**stripl**|**stripr**|**sub**|**sum**|**take**|**try**|\n|**tup**|**tupl**|**tupr**|**tupx**|**unlist**|**λ**|\n|**with_trampoline**|\n\n## *list/unlist*\n\n```bash\n$ list 1 2 3\n1\n2\n3\n\n$ list 1 2 3 4 5 | unlist\n1 2 3 4 5\n```\n\n## *take/drop/ltail/lhead/last*\n\n```bash\n$ list 1 2 3 4 | drop 2\n3\n4\n\n$ list 1 2 3 4 5 | lhead\n1\n\n$ list 1 2 3 4 | ltail\n2\n3\n4\n\n$ list 1 2 3 4 5 | last\n5\n\n$ list 1 2 3 4 5 | take 2\n1\n2\n```\n\n## *join*\n\n```bash\n$ list 1 2 3 4 5 | join ,\n1,2,3,4,5\n\n$ list 1 2 3 4 5 | join , [ ]\n[1,2,3,4,5]\n```\n\n## *map*\n\n```bash\n$ seq 1 5 | map λ a . 'echo $((a + 5))'\n6\n7\n8\n9\n10\n\n$ list a b s d e | map λ a . 'echo $a$(echo $a | tr a-z A-Z)'\naA\nbB\nsS\ndD\neE\n\n$ list 1 2 3 | map echo\n1\n2\n3\n\n$ list 1 2 3 | map 'echo $ is a number'\n1 is a number\n2 is a number\n3 is a number\n\n$ list 1 2 3 4 | map 'echo \\($,$\\) is a point'\n(1,1) is a point\n(2,2) is a point\n(3,3) is a point\n(4,4) is a point\n```\n\n## *flat map*\n\n```bash\n$ seq 2 3 | map λ a . 'seq 1 $a' | join , [ ]\n[1,2,1,2,3]\n\n$ list a b c | map λ a . 'echo $a; echo $a | tr a-z A-z' | join , [ ]\n[a,A,b,B,c,C]\n```\n\n## *filter*\n\n```bash\n$ seq 1 10 | filter λ a . '[[ $(mod $a 2) -eq 0 ]] \u0026\u0026 ret true || ret false'\n2\n4\n6\n8\n10\n```\n\n## *foldl/foldr*\n\n```bash\n$ list a b c d | foldl λ acc el . 'echo -n $acc-$el'\na-b-c-d\n\n$ list '' a b c d | foldr λ acc el .\\\n    'if [[ ! -z $acc ]]; then echo -n $acc-$el; else echo -n $el; fi'\nd-c-b-a\n```\n\n```bash\n$ seq 1 4 | foldl λ acc el . 'echo $(($acc + $el))'\n10\n```\n\n```bash\n$ seq 1 4 | foldl λ acc el . 'echo $(mul $(($acc + 1)) $el)'\n64 # 1 + (1 + 1) * 2 + (4 + 1) * 3 + (15 + 1) * 4 = 64\n\n$ seq 1 4 | foldr λ acc el . 'echo $(mul $(($acc + 1)) $el)'\n56 # 1 + (1 + 1) * 4 + (8 + 1) * 3 + (27 + 1) * 2 = 56\n```\n\n## *tup/tupx/tupl/tupr*\n\n```bash\n$ tup a 1\n(a,1)\n\n$ tup 'foo bar' 1 'one' 2\n(foo bar,1,one,2)\n\n$ tup , 1 3\n(u002c,1,3)\n```\n\n```bash\n$ tupl $(tup a 1)\na\n\n$ tupr $(tup a 1)\n1\n\n$ tup , 1 3 | tupl\n,\n\n$ tup 'foo bar' 1 'one' 2 | tupl\nfoo bar\n\n$ tup 'foo bar' 1 'one' 2 | tupr\n2\n```\n\n```bash\n$ tup 'foo bar' 1 'one' 2 | tupx 2\n1\n\n$ tup 'foo bar' 1 'one' 2 | tupx 1,3\nfoo bar\none\n\n$ tup 'foo bar' 1 'one' 2 | tupx 2-4\n1\none\n2\n```\n\n## *ntup/ntupx/ntupl/ntupr*\n\n```bash\n$ ntup tuples that $(ntup safely nest)\n(dHVwbGVzCg==,dGhhdAo=,KGMyRm1aV3g1Q2c9PSxibVZ6ZEFvPSkK)\n\necho '(dHVwbGVzCg==,dGhhdAo=,KGMyRm1aV3g1Q2c9PSxibVZ6ZEFvPSkK)' | ntupx 3 | ntupr\nnest\n\n$ ntup 'foo,bar' 1 one 1\n(Zm9vLGJhcgo=,MQo=,b25lCg==,MQo=)\n\n$ echo '(Zm9vLGJhcgo=,MQo=,b25lCg==,MQo=)' | ntupx 1\nfoo,bar\n```\n\n```bash\n$ ntupl $(ntup 'foo bar' 1 one 2)\nfoo bar\n\n$ ntupr $(ntup 'foo bar' 1 one 2)\n2\n```\n\n## *buff*\n\n```bash\n$ seq 1 10 | buff λ a b . 'echo $(($a + $b))'\n3\n7\n11\n15\n19\n\n$ seq 1 10 | buff λ a b c d e . 'echo $(($a + $b + $c + $d + $e))'\n15\n40\n```\n\n## *lzip*\n\n```bash\n$ list a b c d e f | lzip $(seq 1 10)\n(a,1)\n(b,2)\n(c,3)\n(d,4)\n(e,5)\n(f,6)\n```\n\n```bash\n$ list a b c d e f | lzip $(seq 1 10) | last | tupr\n6\n```\n\n## *curry*\n\n```bash\nadd2() {\n    echo $(($1 + $2))\n}\n```\n\n```bash\n$ curry inc add2 1\n```\n\n```bash\n$ inc 2\n3\n\n$ seq 1 3 | map λ a . 'inc $a'\n2\n3\n4\n```\n\n## *peek*\n\n```bash\n$ list 1 2 3 \\\n    | peek lambda a . echo 'dbg a : $a' \\\n    | map lambda a . 'mul $a 2' \\\n    | peek lambda a . echo 'dbg b : $a' \\\n    | sum\n\ndbg a : 1\ndbg a : 2\ndbg a : 3\ndbg b : 2\ndbg b : 4\ndbg b : 6\n12\n```\n\n```bash\n$ a=$(seq 1 4 | peek lambda a . echo 'dbg: $a' | sum)\n\ndbg: 1\ndbg: 2\ndbg: 3\ndbg: 4\n\n$ echo $a\n\n10\n```\n\n## *maybe/maybemap/maybevalue*\n\n```bash\n$ list Hello | maybe\n(Just,Hello)\n\n$ list \"   \" | maybe\n(Nothing)\n\n$ list Hello | maybe | maybemap λ a . 'tr oH Oh \u003c\u003c\u003c$a'\n(Just,hellO)\n\n$ list \"   \" | maybe | maybemap λ a . 'tr oH Oh \u003c\u003c\u003c$a'\n(Nothing)\n\n$ echo bash-fun rocks | maybe | maybevalue DEFAULT\nbash-fun rocks\n\n$ echo | maybe | maybevalue DEFAULT\nDEFAULT\n\n```\n\n## *not/isint/isempty*\n\n```bash\n$ isint 42\ntrue\n\n$ list blah | isint\nfalse\n\n$ not true\nfalse\n\n$ not isint 777\nfalse\n\n$ list 1 2 \"\" c d 6 | filter λ a . 'isint $a'\n1\n2\n6\n\n$ list 1 2 \"\" c d 6 | filter λ a . 'not isempty $a'\n1\n2\nc\nd\n6\n```\n\n## *isfile/isnonzerofile/isreadable/iswritable/isdir*\n\n```bash\n$ touch /tmp/foo\n\n$ isfile /tmp/foo\ntrue\n\n$ not iswritable /\ntrue\n\n$ files=\"/etc/passwd /etc/sudoers /tmp /tmp/foo /no_such_file\"\n\n$ list $files | filter λ a . 'isfile $a'\n/etc/passwd\n/etc/sudoers\n/tmp/foo\n\n$ list $files | filter λ a . 'isdir $a'\n/tmp\n\n$ list $files | filter λ a . 'isreadable $a'\n/etc/passwd\n/tmp\n/tmp/foo\n\n$ list $files | filter λ a . 'iswritable $a'\n/tmp\n/tmp/foo\n\n$ list $files | filter λ a . 'isnonzerofile $a'\n/etc/passwd\n/etc/sudoers\n/tmp\n\n$ list $files | filter λ a . 'not isfile $a'\n/tmp\n/no_such_file\n```\n\n## *try/catch*\n\n```bash\n$ echo 'expr 2 / 0' | try λ _ . 'echo 0'\n0\n\n$ echo 'expr 2 / 0' | try λ status . 'echo $status'\n2\n\n$ echo 'expr 2 / 2' | try λ _ . 'echo 0'\n1\n```\n\n```bash\ntry λ  _ . 'echo some errors during pull; exit 1' \u003c \u003c(echo git pull)\n```\n\n```bash\n$ echo 'expr 2 / 0' \\\n    | LANG=en catch λ cmd status val . 'echo cmd=$cmd,status=$status,val=$val'\ncmd=expr 2 / 0,status=2,val=(expr:,division,by,zero)\n```\n\n```bash\n$ echo 'expr 2 / 2' | catch λ _ _ val . 'tupl $val'\n1\n```\n\n## *scanl*\n\n```bash\n$ seq 1 5 | scanl lambda acc el . 'echo $(($acc + $el))'\n1\n3\n6\n10\n15\n```\n\n```bash\n$ seq 1 5 | scanl lambda a b . 'echo $(($a + $b))' | last\n15\n```\n\n## *with_trampoline/res/call*\n\n```bash\nfactorial() {\n    fact_iter() {\n        local product=$1\n        local counter=$2\n        local max_count=$3\n        if [[ $counter -gt $max_count ]]; then\n            res $product\n        else\n            call fact_iter $(echo $counter\\*$product | bc) $(($counter + 1)) $max_count\n        fi\n    }\n\n    with_trampoline fact_iter 1 1 $1\n}\n```\n\n```bash\n$ time factorial 30 | fold -w 70\n265252859812191058636308480000000\n\nreal    0m1.854s\nuser    0m0.072s\nsys     0m0.368s\n```\n\n```bash\ntime factorial 60 | fold -w 70\n8320987112741390144276341183223364380754172606361245952449277696409600\n000000000000\n\nreal    0m3.635s\nuser    0m0.148s\nsys     0m0.692s\n```\n\n```bash\n$ time factorial 90 | fold -w 70\n1485715964481761497309522733620825737885569961284688766942216863704985\n393094065876545992131370884059645617234469978112000000000000000000000\n\nreal    0m4.371s\nuser    0m0.108s\nsys     0m0.436s\n```\n\n# Examples\n\n```bash\nprocessNames() {\n\n  uppercase() {\n     local str=$1\n     echo $(tr 'a-z' 'A-Z' \u003c\u003c\u003c ${str:0:1})${str:1}\n  }\n\n  list $@ \\\n    | filter λ name . '[[ ${#name} -gt 1 ]] \u0026\u0026 ret true || ret false' \\\n    | map λ name . 'uppercase $name' \\\n    | foldl λ acc el . 'echo $acc,$el'\n\n}\n\nprocessNames adam monika s slawek d daniel Bartek j k\n```\n\n```bash\nAdam,Monika,Slawek,Daniel,Bartek\n```\n\n# Running tests\n\n```bash\ncd test\n./test_runner\n```\n\n# Contribution guidelines\n\nFeel free to ask questions in chat, open issues, or contribute by creating pull requests.\n\nIn order to create a pull request\n* checkout master branch\n* introduce your changes \u0026 bump version\n* submit pull request\n\n# Resources\n* [Inspiration](https://quasimal.com/posts/2012-05-21-funsh.html)\n* [Functional Programming in Bash](https://medium.com/@joydeepubuntu/functional-programming-in-bash-145b6db336b7)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssledz%2Fbash-fun","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssledz%2Fbash-fun","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssledz%2Fbash-fun/lists"}