{"id":15748193,"url":"https://github.com/binaryphile/concorde","last_synced_at":"2026-02-26T16:07:07.048Z","repository":{"id":143324929,"uuid":"97623795","full_name":"binaryphile/concorde","owner":"binaryphile","description":"Bash scripting in my own particular...[sigh] \"Idiom, sir?\" Idiom!","archived":false,"fork":false,"pushed_at":"2022-09-12T11:24:27.000Z","size":358,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-01T08:51:20.636Z","etag":null,"topics":["bash"],"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":"2017-07-18T17:02:56.000Z","updated_at":"2022-03-17T20:20:53.000Z","dependencies_parsed_at":"2023-05-14T21:15:15.634Z","dependency_job_id":null,"html_url":"https://github.com/binaryphile/concorde","commit_stats":null,"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/binaryphile/concorde","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Fconcorde","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Fconcorde/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Fconcorde/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Fconcorde/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/binaryphile","download_url":"https://codeload.github.com/binaryphile/concorde/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/binaryphile%2Fconcorde/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29863877,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-26T08:51:08.701Z","status":"ssl_error","status_checked_at":"2026-02-26T08:50:19.607Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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"],"created_at":"2024-10-04T05:40:51.235Z","updated_at":"2026-02-26T16:07:07.022Z","avatar_url":"https://github.com/binaryphile.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"Message For You, Sir [![Build Status](https://travis-ci.org/binaryphile/concorde.svg?branch=master)](https://travis-ci.org/binaryphile/concorde)\n====================\n\nBash scripting in my own particular...\\[sigh\\]...\n\nConcorde: \"Idiom, sir?\"\n\nIdiom!\n\nConcorde is a toolkit for writing bash scripts and libraries.\n\nFeatures\n========\n\n-   an [enhanced-getopt style] option parser: `parse_options`\n\n-   [array] and [hash] utility functions (hashes, a.k.a. \"[associative\n    arrays][hash]\")\n\n-   smarter versions of `source`, a.k.a. the `.` operator: `require` and\n    `require_relative`\n\n-   support for test frameworks, such as [shpec]: `sourced`\n\n-   automatic ruby-style tracebacks on errors: [`strict_mode`] with\n    tracebacks (but no change to `IFS`)\n\n-   [namespaces] to isolate library variables from one another\n\n-   python-style [selective importation] of functions from libraries:\n    `bring`\n\n-   [keyword arguments] for functions\n\n-   command [macros] which avoid common pitfalls with system commands\n\nRequirements\n============\n\n-   [GNU `readlink`] on your PATH - for Mac users, [`greadlink`] is also\n    acceptable\n\n-   `sed` on your PATH\n\n-   bash 4.3 or 4.4 - tested with:\n\n    -   4.3.11\n\n    -   4.3.33\n\n    -   4.3.42\n\n    -   4.4.12\n\nReserved Global Variables\n=========================\n\nConcorde reserves a few global variables for its own use. They begin\nwith `__` (double-underscore)\n\n-   `__` - double-underscore itself\n\n-   `__ns` - short for \"namespace\"\n\n-   `__errmsg` - an error message passed by `raise`\n\nAny script or library used with concorde cannot change the purpose of\nthese variables.\n\nInstallation\n============\n\nClone or download this repository, then put its `lib` directory in your\nPATH, or copy `lib/concorde.bash` into a PATH directory.\n\nUse `source concorde.bash` in your scripts.\n\nUsage\n=====\n\nConsult the API specification below for full details.\n\nA Sample Script Template\n------------------------\n\n``` bash\n#!/usr/bin/env bash\n\nsource concorde.bash\n\nget \u003c\u003c'EOS'\n  Usage:  script [options] \u003cpositional_arguments\u003e...\n\n    Options:\n      -o \u003cvalue\u003e, --option=\u003cvalue\u003e    a value to pass into the script\n      -f                              a flag that is true when given\nEOS\nprintf -v usage '\\n%s\\n' \"$__\"\n\nscript_main () {\n  $(grab 'option_var f_flag' from \"$1\")   # make locals of the options\n  shift                                   # make ready to process args\n\n  do_something_with \"$option_var\"         # use the option value\n  (( f_flag )) \u0026\u0026 do_something_with_flag  # test if -f was supplied\n\n  # process the positional arguments\n  while (( $# )); do                      # true while there are args\n    case $1 in\n      alternative_1 ) do_alternative_1            ;;\n      alternative_2 ) do_alternative_2            ;;\n      * ) $(raise \"Error: unknown argument '$1'\") ;;\n    esac\n    shift                                 # move to next argument\n  done\n}\n\n[other functions...]\n\nsourced \u0026\u0026 return       # stop here when testing the script\nstrict_mode on          # stop on errors and issue a traceback\n\n# define command-line options\n# short   long      var name    help\n# -----   ----      --------    ----\nget \u003c\u003c'EOS'\n  -o      --option  option_var  \"a value to pass into the script\"\n  -f      ''        ''          \"a flag that is true when given\"\nEOS\n\n! (( $# ))                \u0026\u0026 die \"$usage\"\n$(parse_options __ \"$@\")  || die \"$usage\"\nscript_main     __ \"$@\"   || die \"$usage\"\n```\n\nRead the rest of the usage section for a full explanation of the\nfeatures used above, or look at the [tutorial] for a walkthrough which\ndevelops a script from the ground up.\n\nFunctions Which Return Boolean Values\n-------------------------------------\n\nFunctions used for their truth value are typically used in expressions\nin order to trigger actions.\n\nFor example the `sourced` function typically is used like so:\n\n``` bash\nsourced \u0026\u0026 return\n```\n\nThese functions use the normal bash return code mechanism where `0` is\nsuccess and any other value is failure.\n\nFunctions Which Return Strings\n------------------------------\n\nBash's typical mechanism for storing strings generated by a function is\nto use [command substitution].\n\nFor example, the result of an `echo` command might be stored like so:\n\n``` bash\n# this is not how concorde returns strings\nmy_value=$(echo \"the value\")\n```\n\nConcorde doesn't use this method as it is prone to capturing unexpected\noutput and also requires an unnecessary subshell.\n\nAny concorde function which returns a string value does so in the global\nvariable `__` (double-underscore).\n\nBecause any function is allowed to overwrite `__` at any time, you want\nto save that value before calling any other functions like so:\n\n``` bash\nget \u003c\u003c\u003c\"the value\"\nmy_value=$__\n```\n\n`get` is a concorde function which stores a string from `stdin` and\n`\u003c\u003c\u003c` feeds it the supplied string.\n\n`__` must be treated much the same as the `$?` return code, since every\nsuccessive command may change it.\n\nNote that because `__` is a global, it is discarded by the subshells\nwhich are employed by pipelines. Therefore you cannot use pipelines to\nreturn strings from concorde functions. For example, this will not work:\n\n``` bash\n# doesn't work\necho \"the value\" | get\nmy_value=$__\n```\n\nBecause `__`'s value is ephemeral, it can be used to hold interim values\nand feed the output of one operation to the next:\n\n``` bash\nget \u003c\u003c\u003c\"the value\"\nmy_function_that_returns_a_string \"$__\"\nfinal_value=$__\n```\n\nNote that `__` is always a string value. Your functions should be\ncareful not to store an actual array or hash in it, for example:\n\n``` bash\n# don't do this\n__=( \"array item\" )\n```\n\nThis is because some of concorde's features rely on `__`'s type to be\nstring. Since bash automatically converts a string variable to an array\nor hash when assigned, doing so can interfere with concorde.\n\nDealing with Hashes and Arrays as Parameters\n--------------------------------------------\n\nBash can pass string values to functions, but is not able to pass arrays\nnor hashes as individual parameters to a function.\n\nIf an array needs to be treated as a parameter to a function, typical\nbash practice is to either pass the expanded array as multiple\narguments, or to use the shortcut of not passing it at all and instead\njust refer to the global variable itself.\n\nAnother approach is to use named references ([`declare -n`] or\n[`${!reference}`]) instead of using a normal local variable.\n\nFor a variety of reasons, each of these approaches is problematic.\n\nThe workaround employed by concorde is to convert arrays and hashes to\nstrings (serialize them) when crossing function boundaries, whether as\narguments or return values. This gives you full control of your variable\nnamespace since you aren't using outer-scope variables.\n\nAnd while bash is not good at passing arrays (hashes especially), it is\ngood at passing strings, so why not use that.\n\nBy the same token, concorde's functions are written to expect the string\nrepresentations of arrays and hashes, when those argument types are\ncalled for. While there are a couple of concorde functions which\nactually do operate on real (non-string) arrays/hashes, that is clearly\nnoted in the API documentation for them.\n\nAlthough bash doesn't have a general-purpose string literal\nrepresentation for an array, it does define such a format in its [array\nassignment] statements. You can see an example by running\n`declare -p \u003cvariable_name\u003e`.\n\nConcorde borrows the same format for the array literals expected by\nconcorde's functions, with minor changes.\n\n### Passing an Array or Hash\n\nFor example, to call a function `my_function` which expects a single\narray argument, you might define the array, then use concorde's `repr`\nfunction to generate the string format:\n\n``` bash\nmy_ary=( \"first item\" \"second item\" )\nrepr my_ary\nmy_function \"$__\"\n```\n\nNote that `repr` takes the name of the array as an argument and returns\nthe string representation in `__`.\n\nThe same method works for a hash.\n\n### Receiving an Array\n\nTo write a function which receives such an argument, you use concorde's\n`local_ary` function:\n\n``` bash\nmy_func () {\n  $(local_ary input_ary=$1)\n  local item\n\n  for item in \"${input_ary[@]}\"; do\n    echo \"$item\"\n  done\n}\n```\n\n`ary` is short for \"array\".\n\n`local_ary` creates a local array variable, in this case `input_ary`,\nand gives it the contents provided in `$1`. For the rest of the function\nyou use `input_ary` like a normal array, because it is one.\n\nNote that the `$()` command substitution operator around `local_ary` is\nnecessary. Without it, `local_ary` can't create a local variable in the\nscope of the caller.\n\nTo receive a hash instead of an array, simply use the `local_hsh`\nfunction instead of `local_ary`.\n\n### Passing Arrays/Hashes by Name\n\nBoth `local_ary` and `local_hsh` will allow you to pass them the name of\nthe variable holding the array representation instead of the\nrepresentation itself. They will detect the variable name and expand it.\nIn general, you should pass variable names to them instead of\n[expansions] wherever possible.\n\nLet's look at an example. The following lines prepare an array\nrepresentation in `__`:\n\n``` bash\narray=( \"item one\" )\nrepr array\n```\n\nConcorde's `member_of` function takes an array representation, along\nwith an array item we're looking for, and returns a boolean indicating\nwhether the item was found in the array. Instead of using the array\nexpansion `$__`, you can give it the name of the array variable instead\n(`__`):\n\n``` bash\nmember_of __ \"item one\" \u0026\u0026 put \"'item one' is in the array\"\n```\n\nConcorde supports passing by variable name for array and hash\nrepresentations, but not for regular string variables. You still have to\nuse expansions to pass regular strings:\n\n``` bash\nvalue=\"item one\"\n# passing \"value\" doesn't expand it to \"item one\", so doesn't work:\nmember_of __ value\n```\n\n### Just Passing Through\n\nOf course, if your function only needs to receive an array/hash in order\nto pass it to another function, you don't need to convert the string\nrepresentation into its actual array form, you can simply receive and\npass the string representation (note the call by variable name):\n\n``` bash\nmy_function () {\n  local array_representation=$1\n\n  another_function array_representation\n}\n```\n\n### A Caveat\n\nThe recommended way to use `local_ary` and `local_hsh` (and functions\nthat employ them) is to always pass array parameters by name.\n\nThe caveat introduced by the pass-by-name functionality is that if you\npass an array which happens to contain only one item, and that one item\nis the name of a variable, it will be mistaken for a variable holding an\narray representation itself and expanded, when that is not what you\nintended.\n\nThis is not a problem for hashes, only arrays.\n\nBe careful to avoid this situation or you will get unexpected behavior.\nThe recommended way to avoid it is to always pass array representations\nby variable name. If you do pass a literal, however, ensure that it is\nnot a single-item array that is also the name of a variable.\n\n### Passing by Literal\n\nYou may also construct your own literals for arrays or hashes, but the\ntwo each follow their own, slightly different, rule.\n\n#### Arrays (Not Hashes)\n\nThe array syntax consists of whitespace-separated items. Whitespace\nincludes spaces, tabs and newlines; the normal values in the field\nseparator variable `IFS`.\n\nIndividual array items which contain whitespace must either be quoted or\nescaped. Here is a comparison of regular [array assignment][array\nassignment] and the equivalent literals used by concorde for both quoted\nand escaped forms:\n\n``` bash\n# actual arrays and equivalent representations\narray1=( 'an item' 'another item' )\nrepresentation1=\"'an item' 'another item'\"\n\narray2=( an\\ item  another\\ item )\nrepresentation2=\"an\\ item  another\\ item\"\n```\n\nEither form shown above, quoted or escaped, is acceptable.\n\nNotice that the representations above are simply the string form of what\nappears between the parentheses in array declarations. In fact, an array\nrepresentation should be usable in the statement:\n\n``` bash\neval \"array=( $representation )\"\n```\n\nFor the most part, an array representation is equivalent to the portion\ninside the parentheses of `declare -p`'s output, minus the bracketed\nindices.\n\n`repr` returns the escaped form, rather than quoted, and without\nindices. Therefore concorde can't preserve the indexing of [sparse\narrays], since those require preservation of indices.\n\nThe following are both examples of equivalent array literals:\n\n``` bash\n# newlines separating items (items containing spaces require quotes or will be split)\nmy_literal='\none\ntwo\n\"three and four\"\n'\n\nanother_literal='one two \"three and four\"'\n```\n\n### Hashes\n\nHashes, like arrays, are similar to the portion inside the parentheses\nof `declare -p`'s output. Unlike arrays, however, hash literals must\ninclude indices. Unlike the regular form of hash declarations though,\nconcorde's indices are not in brackets. They are more like keyword\nparameters in other languages. For example:\n\n``` bash\nmy_literal=\"one=1 two=2 three_and_four='3 and 4'\"\n```\n\nIn this case, quoted items are quoted after the index and equals sign\n(as in `'3 and 4'`). Escaping works as well.\n\n`repr` generates this format when invoked on a hash.\n\nNotably, the following does *not* work on a hash representation:\n\n``` bash\n# does NOT work\neval \"declare -A hash=( $representation )\"\n```\n\nThat's because of the missing brackets on indices.\n\nBecause the indices do not have brackets, concorde also doesn't support\nhash indices with spaces. In general, concorde only supports hash\nindices which are also usable as variable names. That is, keys which are\ncomposed only of alphanumeric and underscore characters, and don't start\nwith a number.\n\n### Passing Arrays as Multiple Arguments\n\n`local_ary` is also geared to accept multiple arguments as an array.\nThis can be useful when converting positional arguments (`$@`) into a\nnamed array:\n\n``` bash\nmy_function () {\n  $(local_ary my_ary=\"$@\")\n  local item\n\n  for item in \"${my_ary[@]}\"; do\n    do_something_with \"$item\"\n  done\n}\n```\n\n### Passing Hashes as Multiple Arguments (a.k.a. [Keyword Arguments][keyword arguments])\n\n`local_hsh` can do the same thing with multiple arguments:\n\n``` bash\nmy_function () {\n  $(local_hsh my_hsh=\"$@\")\n  local key\n\n  for key in \"${!my_hsh[@]}\"; do\n    do_something_with \"${my_hsh[$key]}\"\n  done\n}\n```\n\nCalling a function like this looks familiar from other languages:\n\n``` bash\nmy_function one=1 two=2 three_and_four=\"3 and 4\"\n```\n\nLanguages such as python and ruby allow you to specify named arguments\nvia keywords like the above.\n\nRequired (non-keyword) arguments must always be passed before keyword\narguments, as positional arguments. Optional arguments may then be\npassed last as keyword arguments.\n\nOptional arguments have their default values defined by the function.\n\nHere is an example of how such a function is implemented:\n\n``` bash\nmy_function () {\n  local required_arg=$1; shift\n  local optional_arg=\"default value\"\n  $(grab optional_arg from \"$@\")\n\n  do_something_with \"$required_arg\"\n  do_something_with \"$optional_arg\"\n}\n```\n\nAny required arguments are stored and `shift`ed out of the positional\narguments.\n\nThen the optional values are `grab`bed by name from the residual\narguments, which must all be keywords at that point.  Grab just passes\nthem to `local_hsh` internally to create a true hash from them, then\nextracts `optional_arg` from the hash into a local variable.  More on\n`grab` later.\n\nThis is what it looks like calling `my_function`:\n\n``` bash\nmy_function \"required value\" optional_arg=\"optional value\"\n```\n\n`optional_arg=...` can be left off, in which case the function will use\nits default value.\n\n### Newline-delimited Array Literals, or Nested Arrays\n\nYou can construct an array representation with another array nested\ninside fairly easily, but it requires a different type of array\nrepresentation on the outside.\n\nLet's start with a function which expects a nested array as its only\nargument:\n\n``` bash\nmy_function () {\n  $(local_nry outer_ary=$1)\n  local item\n  local row\n\n  for row in \"${outer_ary[@]}\"; do\n    $(local_ary inner_ary=$row)\n    for item in \"${inner_ary[@]}\"; do\n      echo \"$item\"\n    done\n  done\n}\n```\n\nYou've seen `local_ary` so far, but `local_nry` is new.\n\n`local_nry` introduces the idea of a newline-delimited array\nrepresentation. Like `local_ary`, it creates a local array (named\n`outer_ary`), but expects a slightly different input than `local_ary`\nwould. `local_nry` expects a multiline array literal, separated only by\nnewlines, not spaces or tabs such as `local_ary`.\n\nIn fact, there are two differences between the two functions. One is\nthat `local_ary` separates items on tabs and spaces in addition to\nnewlines, while `local_nry` only separates on newlines. The other is\nthat `local_nry` escapes all of the items in each row, so they can be\npassed unchanged to `local_ary` when you call it.\n\nThat means each row of the newline-array representation can contain a\nstandard array representation, so long as they don't contain newlines,\nsince `local_nry` parses those.\n\nIf the inner arrays need to hold newlines, the newlines must appear in\nan [ANSI C-like string]. Normal quotes won't suffice.\n\nFor example: `$'a multiline\\nstring value'` is an ANSI C-like string\nwhich has a protected newline in it. The newline will not be parsed as a\nseparator by `local_nry`, but *will* then be turned into a regular\nnewline by the call to `local_ary`.\n\nThe function above creates the outer array from the newline-delimited\nrepresentation, then interprets each row as a regular array\nrepresentation. That makes a nested array.\n\nHere's how you would call such a function (note that it is `get`ting a\nquoted [heredoc]):\n\n``` bash\nget \u003c\u003c'EOS'\n  \"first array, item one\"  $'first array\\nitem two with newline'\n  \"second array, item one\" \"second array, item two\"\nEOS\nmy_func __\n```\n\nIts output would be:\n\n``` bash\nfirst array, item one\nfirst array\nitem two with newline\nsecond array, item one\nsecond array, item two\n```\n\nIf using an unquoted [heredoc] (no quotes around our `EOS` tag), the\ndollar-sign needs to be escaped to delay expansion:\n\n``` bash\nget \u003c\u003cEOS\n  \"first array, item one\"  \\$'first array\\nitem two with newline'\n  \"second array, item one\" \"second array, item two\"\nEOS\n```\n\nWorking With Strings\n--------------------\n\nConcorde includes several functions for working with strings.\n\n### Getting a Heredoc\n\n[Heredocs][heredoc] are multiline strings which bash reads without\nrequiring quotes. Instead, bash uses a user-specified tag to delimit the\nbeginning and end of the string. Here's an example, where the tag is the\nstring `EOS`:\n\n``` bash\nread -rd '' value \u003c\u003c'EOS'\n  a multiline\n  string value\nEOS\necho \"$value\"\n```\n\nThis gives the output:\n\n``` bash\na multiline\n  string value\n```\n\n`EOS` is simply the terminal tag chosen by the user to end the string.\nThe terminal tag must appear after the last line of the string, by\nitself. Bash automatically strips leading whitespace from the first line\nof content and trailing whitespace from the last line of content.  This\nis what causes the peculiar indentation of the above output.\n\nThe quotes around the initial `\u003c\u003c'EOS'` tell bash not to expand any\nvariables appearing in the string. The quotes can be left off if you\n*want* the dollar-sign expansion of a variable to take place in the\nstring.\n\nConcorde's `get` function reads such a string into the `__` variable.\nYou can use either a quoted or non-quoted heredoc.\n\n### Getting a Heredoc with Sensible Indentation\n\nIn other languages, some heredoc implementations allow you to strip\nleading indentation of a block of text so that:\n\n``` bash\nget \u003c\u003c'EOS'\n  a multiline\n  string value\nEOS\necho \"$__\"\n```\n\nyields the output without indentation:\n\n``` bash\na multiline\nstring value\n```\n\nConcorde's `get` will do this, provided that the first line of a string\nhas indentation. `get` strips all matching indentation from the rest of\nthe lines in the string.\n\nThe indentation of a line needs to match precisely, character for\ncharacter, in order to be stripped. Lines which start with non-matching\ncharacters are simply left alone and not altered.\n\nThis behavior works for most needs. If you happen to need leading\nindentation which is not stripped, you can either place no indentation\non just the first line, then add it yourself later, or you can use the\n`get_raw` function which does no stripping at all.\n\n### Splitting a String\n\n`part` takes string and delimiter arguments and returns an array\nrepresentation of the split string (minus delimiters) in `__`. To create\nan array from PATH, for example, you might use:\n\n``` bash\nsplit_path () {\n  local item\n\n  part \"$PATH\" on :\n  $(local_ary path_ary=__)\n  for item in \"${path_ary}\"; do\n    put \"item is: $item\"\n  done\n}\n```\n\n`path` has a dummy argument, `on`, as it's second argument. A few\nconcorde functions do this for readability. It's an affectation of my\nown, although dummy arguments to other concorde functions actually work\nas a flag in some cases.\n\nNote in the code above that like any concorde function which accepts a\nstring argument, `\"$PATH\"` must be expanded as it is being passed to\n`part`.  String arguments can't be passed by variable name.\n\nThe same is not true of the array result from `part`, which can be\npassed to `local_ary` by name (`__`), since `local_ary` is expecting an\narray representation.\n\nWorking with Hashes and Variables\n---------------------------------\n\nHashes can act as mini-namespaces for storing variables you wish to work\nwith.\n\nRather than forcing you to work inside the hash, however, it's\nfrequently simpler to extract variables into the local namespace from\nthe hash. It can be easier to read and reason about local variable names\nthan hash reference notation. For example, which is easier to read?:\n\n``` bash\ndeclare -A number_hsh=( [zero]=0 [one]=1 )\nput \"Zero is ${number_hsh[zero]} and one is ${number_hsh[one]}.\"\n```\n\nor:\n\n``` bash\n$(grab 'zero one' from 'zero=0 one=1')    # extract zero and one\nput \"Zero is $zero and one is $one.\"\n```\n\nWhile this example is contrived, the point is that it is frequently\nnicer to avoid using bash's verbose hash syntax in favor of short and\nreadable variable names. `grab` makes that easier.\n\n`grab` is capable of extracting a single key, a list of keys or all keys\nfrom a hash, and can do so from a variable name or a literal:\n\n``` bash\n# single key\nmyh='zero=0 one=1 two=2'\n$(grab zero from myh)\n\n# list of keys\n$(grab 'zero one two' from myh)\n\n# all keys\n$(grab '*' from myh)\n\n# grab from a literal\n$(grab zero from 'zero=0 one=1')\n```\n\n`grab` creates local variables from the keys and values in the provided\nhash. It requires `$()` command substitution because it creates\nvariables in the local scope.\n\nThe asterisk needs to be quoted in the \"all keys\" example to prevent\n[globbing].\n\n`grab`'s first argument, the key(s), is never expanded as a variable\nname so that you can reliably hand it a single key name. This makes the\nargument a list, rather than an array representation. A list is simply a\nstring of whitespace-separated items which are key identifiers.\n\n`grab` only supports hashes with keys that are formatted the same as\nvariable identifiers, namely, composed of alphanumeric and underscore\ncharacters, and not starting with a number.\n\nThere is also the dummy `from` argument for readability, as is my wont.\n\n### Creating Hashes from Variables\n\n\nParsing Options\n---------------\n\n  [enhanced-getopt style]: https://linux.die.net/man/1/getopt\n  [array]: http://wiki.bash-hackers.org/syntax/arrays\n  [hash]: http://wiki.bash-hackers.org/syntax/arrays#associative_bash_4\n  [shpec]: https://github.com/rylnd/shpec/tree/0.2.2\n  [`strict_mode`]: http://redsymbol.net/articles/unofficial-bash-strict-mode/\n  [namespaces]: https://en.wikipedia.org/wiki/Namespace#Computer-science_considerations\n  [selective importation]: http://www.diveintopython.net/object_oriented_framework/importing_modules.html\n  [keyword arguments]: https://en.wikipedia.org/wiki/Named_parameter\n  [macros]: https://en.wikipedia.org/wiki/Macro_instruction\n  [GNU `readlink`]: https://linux.die.net/man/1/readlink\n  [`greadlink`]: https://apple.stackexchange.com/questions/69223/how-to-replace-mac-os-x-utilities-with-gnu-core-utilities/88812\n  [tutorial]: share/doc/tutorial.md\n  [command substitution]: http://wiki.bash-hackers.org/syntax/expansion/cmdsubst\n  [`declare -n`]: http://wiki.bash-hackers.org/commands/builtin/declare#nameref\n  [`${!reference}`]: http://wiki.bash-hackers.org/syntax/pe#indirection\n  [array assignment]: http://wiki.bash-hackers.org/syntax/arrays#storing_values\n  [expansions]: http://wiki.bash-hackers.org/syntax/pe#simple_usage\n  [sparse arrays]: http://wiki.bash-hackers.org/syntax/arrays#indexing\n  [ANSI C-like string]: http://wiki.bash-hackers.org/syntax/quoting#ansi_c_like_strings\n  [heredoc]: http://wiki.bash-hackers.org/syntax/redirection#here_documents\n  [globbing]: http://wiki.bash-hackers.org/syntax/expansion/globs\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinaryphile%2Fconcorde","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbinaryphile%2Fconcorde","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbinaryphile%2Fconcorde/lists"}