{"id":15748109,"url":"https://github.com/binaryphile/rubsh","last_synced_at":"2026-05-10T19:21:16.272Z","repository":{"id":143324976,"uuid":"61414579","full_name":"binaryphile/rubsh","owner":"binaryphile","description":"Enhanced data types for bash, inspired by Ruby - lovingly pronounced \"rubbish\"","archived":false,"fork":false,"pushed_at":"2017-05-29T22:02:40.000Z","size":295,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-06T11:30:41.752Z","etag":null,"topics":["bash","ruby"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/binaryphile.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-06-18T03:14:16.000Z","updated_at":"2017-04-10T13:56:31.000Z","dependencies_parsed_at":"2023-05-21T17:45:48.686Z","dependency_job_id":null,"html_url":"https://github.com/binaryphile/rubsh","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Frubsh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Frubsh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Frubsh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Frubsh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/binaryphile","download_url":"https://codeload.github.com/binaryphile/rubsh/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246429447,"owners_count":20775805,"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","ruby"],"created_at":"2024-10-04T05:40:37.450Z","updated_at":"2026-05-10T19:21:11.242Z","avatar_url":"https://github.com/binaryphile.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"Rubsh [![Build Status](https://travis-ci.org/binaryphile/rubsh.svg?branch=master)](https://travis-ci.org/binaryphile/rubsh)\n=====\n\nEnhanced data types for bash, inspired by Ruby - lovingly pronounced\n\"rubbish\"\n\nFeatures\n--------\n\n-   ruby-inspired apis for common objects\n\n-   the ability to return objects, including arrays and hashes, from\n    methods\n\n-   chained method calls\n\n-   ruby-inspired block syntax for functional-style methods (.each,\n    .map)\n\n-   control over variable scope\n\n-   a near-complete lack of dollar signs and quotation marks\n\n-   a ruby-like DSL for creating classes using an actual object model\n    with inheritance\n\n-   economic use of bash's variable and function namespaces\n\n-   interoperability with standard bash syntax\n\nRequirements\n------------\n\nBash 4.3+\n\nInstallation\n------------\n\nClone and put `lib` in your path, then use `source rubsh.bash` in your\nscripts.\n\nUsage\n-----\n\n### Output\n\n    puts \"hello, world!\"\n\n    \u003e hello, world!\n\n### String Creation\n\n    String sample = \"an example\"\n    puts sample\n\n    \u003e an example\n\nWould be equivalent to `sample = String.new \"an example\"` in ruby, if\nthat were a thing.\n\n### Array Creation\n\n    Array samples = '( zero one )'\n    puts samples\n\n    \u003e ([0]=\"zero\" [1]=\"one\")\n\n### Hash Creation\n\n    declare -A sampleh\n    Hash sampleh = '( [zero]=0 [one]=1 )'\n    puts sampleh\n\n    \u003e ([one]=\"1\" [zero]=\"0\" )\n\n### Assignment\n\n    String sample = \"an example\"\n    sample = \"a new hope\"\n    puts sample\n\n    \u003e a new hope\n\nRequires that the object has been instantiated at least once so the bash\nfunction (in this case, \"sample\") exists.\n\n### Calling Methods\n\n    Array samples = '( zero one )'\n    puts samples .join -\n\n    \u003e zero-one\n\nMethod names are arguments and require a space between themselves and\nthe object name.\n\n### Method Chaining\n\n    Array samples = '( zero one )'\n    puts samples .join { - } .upcase\n\n    \u003e ZERO-ONE\n\nMethod parameters must be braced for rubsh to know where the prior\nmethod call ends.  Braces require surrounding spaces.\n\n### Blocks\n\n    declare -A sampleh\n    Hash sampleh = '( [zero]=0 )'\n    puts sampleh .map [ {k,v} '$k: $v' ]\n\n    \u003e ([0]=\"zero: 0\")\n\nBlocks use either one-line bracket syntax (shown here) or multiline\ndo/end syntax.  Parameter names are given between braces instead of\npipes.\n\nThe shown spacing is significant for both the brackets and braces.\n\nFor #map, the block body is an expression which is evaluated to a\nstring.  It uses single quotes to prevent variable expansion until\nexecution.\n\n### Object Literals\n\n    puts String \"hello, world!\" .upcase\n\n    \u003e HELLO, WORLD!\n\nObject literals instantiate an object from a literal so methods can\nimmediately be called on them, much like ruby allows literals of basic\ntypes to have methods called on them directly (e.g. \"hello,\nworld!\".upcase).\n\nThey create anonymous, single-use objects.\n\n### Class Creation\n\n    class Fruit; {\n      def tasty? \u003c\u003c'  end'\n        puts \"Heck yeah!\"\n      end\n\n      def fresh? \u003c\u003c'  end'\n        puts \"You bet.\"\n      end\n    }\n\n    Fruit .new myfruit\n    myfruit .fresh?\n\n    \u003e You bet.\n\nNote the class statement ends with a semicolon before the opening brace.\nTechnically, the braces aren't required but provide a familiar visual\ncontext.\n\n### Inheritance\n\n    class Banana : Fruit; {\n      def fresh? \u003c\u003c'  end'\n        puts \"Not so much.\"\n      end\n    }\n\n    Banana .new mybanana\n    mybanana .fresh?\n\n    \u003e Not so much.\n\n    mybanana .tasty?\n\n    \u003e Heck yeah!\n\nInheritance uses : rather than ruby's \u003c to indicate the parent class.\n\n### Introspection\n\n    puts Array .class\n\n    \u003e \"Class\"\n\n    puts Array .superclass\n\n    \u003e \"Object\"\n\n    puts Array .ancestors\n\n    \u003e ([0]=\"Array\" [1]=\"Object\")\n\n    puts Class .methods\n\n    \u003e ([0]=\"ancestors\" [1]=\"declare\" [2]=\"instance_methods\" [3]=\"new\" [4]=\"superclass\" [5]=\"class\" [6]=\"methods\")\n\n    puts Class .instance_methods false\n\n    \u003e ([0]=\"ancestors\" [1]=\"declare\" [2]=\"instance_methods\" [3]=\"new\" [4]=\"superclass\")\n\nOverview\n--------\n\nBash, while powerful, has a frequently frustrating programming model.\nAmong other things, its combination of dynamic scoping, limited data\ntypes and expansion-based command processing can make it tricky to learn\nand difficult to accomplish complex tasks.\n\nFortunately, bash does a lot for you as well. If there is system\nmanipulation to be done on Unix, it can typically be done with bash, and\nfrequently with a minimum of (albeit strange) syntax. It's using bash\nfor logic rather than system manipulation where it tends to fall down.\n\nWhile there are many ways bash could stand improvement, rubsh focuses on\none: making it easier to work with bash's built-in data types.\n\nrubsh provides an object-model for those data types, including methods\nand inheritance. rubsh doesn't offer more data types than the basic\nstring, array and hash (a.k.a. associative array) types, but it does\nprovide useful and intuitive methods for manipulating them.\n\nIt cribs as much of its organization from ruby's core libraries as\npossible, making it easier to absorb, especially if you have ruby\nexperience. But it should also be fairly easy for anyone familiar with\ngeneral object-oriented concepts.\n\n**NOTE**: So that it can use glob characters such as \"?\" in method\nnames, rubsh turns off bash globbing!  If you are using rubsh objects,\nmost of the time you won't miss it because rubsh selectively turns it on\nan off as needed.  However, commands like `echo *` will only echo \"*\",\nnot the current directory contents as you would normally expect.  If you\nneed to enable globbing, do so with `set +f` (and turn it back off with\n`set -f`).  Do not leave it on, however, or rubsh will not work\ncorrectly!\n\nHow It Works\n------------\n\nRubsh provides some basic classes:\n\n-   **String** - self-explanatory\n\n-   **Array** - some sorely-needed standard functions for manipulating\n    lists of strings\n\n-   **Hash** - associative array manipulation\n\nRubsh doesn't change the underlying data types, so for example, hashes\ncan still only store strings as values, but the methods available to\nmanipulate them are new with rubsh.\n\nEach class is actually a bash function. According to the ruby convention\nfor class names, they are all capitalized as shown above.\n\nThese classes are used to create \"object\" instances, which correspond to\nindividual variables you might use in your script. The objects\nthemselves are also simply bash functions, created for you by rubsh.\n\nThe first argument to an object (i.e. function) is usually the name of a\nmethod. While method names are just normal string arguments, for the\nsake of similarity to ruby syntax, they are supplied with a leading dot:\n\n    Array .new \u003carguments\u003e\n\nBecause Array is a function, the method name is separated by a space so\nthat it is the first argument to the function. In this case, the \\#new\nmethod creates a new object instance of the Array class. This roughly\ncorresponds to ruby's `Array.new`\n\nNaturally, in order for rubsh to work, its functions need to be the\nfirst word in the command line. This dictates some differences from\nruby's syntax.\n\nFor example, ruby would normally assign the result of \\#new to a\nvariable:\n\n    myarray = Array.new\n\nYou would then call a method such as \\#join on the myarray object.\n\nrubsh needs Array to be first on the command line, so it turns the syntax\naround a bit by necessity:\n\n    Array myarray = \u003cinitializer\u003e\n\nThis is an unorthodox call to Array\\#new, but it is still a method call.\nThis is one of the cases where rubsh does some magic to infer the method\nfrom the syntax.\n\nrubsh's Array\\#new does two things. First, it creates a bash array\nvariable named myarray. Second, it creates a bash function, also called\nmyarray.\n\nThe array variable stores the given array, just like any other bash\nvariable would. It can be used with all the usual bash functions and\nexpansions for arrays.\n\nThe function is rubsh's contribution. The myarray function represents the\nobject instance of the Array class. It's what responds to Array methods:\n\n    puts myarray .join -\n\nmyarray, the function, knows how to respond to Array's methods. When it\nneeds to determine the array on which it should operate, it uses\nmyarray, the variable. Unsurprisingly, changing the myarray variable's\ncontents (say, by normal bash assignment) changes the values used by the\nfunction.\n\nScoping\n-------\n\nThe scope of the myarray variable (local to the current function or\nglobal) depends on a couple things.\n\nIf the variable myarray existed when \\#new was called, then its existing\nscope remains in effect. This way you can directly control the scope\nwith your own declaration prior to calling \\#new.\n\nOtherwise the variable is created globally by default. This may be what\nyou want, in which case the normal invocation is fine.\n\nGlobal scoping may not always be what you want, however. You may declare\nthe variable local yourself:\n\n    local myarray\n    Array myarray = '( zero one )'\n\nHowever there is another, more compact alternative for local declaration\ninstead:\n\n    $(Array myarray := '( zero one )')\n\nThe colon-equals sign calls Array\\#declare, which generates a compound\nstatement on stdout. The statement declares the variable as local, and\nalso instantiates the object. The statement is captured and executed by\nthe bash shell substitution `$()`.\n\nOf course, bash throws one special case at us.  bash requires explicit\ndeclaration for hashes.\n\nTo create a hash variable, you should declare the variable yourself\nbefore instantiating the object.  Here is how you declare a global hash\nvariable:\n\n    declare -Ag myhash\n    Hash myhash = '( [zero]=0 )'\n\nDrop the -g and you have a locally scoped myhash (you can use the\n`local` builtin, but `declare` works too).\n\nIf you want a local hash, however, the (syntactically sugared) #declare\nmethod is the least verbose:\n\n    $(Hash myhash := '( [zero]=0 )')\n\nThis way the hash doesn't need a separate declaration at all. Bear in\nmind that this method only works for local declarations though.\n\nConclusion\n----------\n\nThere are a number of other useful features which make rubsh quite\npleasurable to work with over standard bash. I invite you to read the\ntest suite to learn more.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinaryphile%2Frubsh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbinaryphile%2Frubsh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinaryphile%2Frubsh/lists"}