{"id":20222469,"url":"https://github.com/capr/die","last_synced_at":"2025-07-31T02:04:29.795Z","repository":{"id":12973215,"uuid":"15651810","full_name":"capr/die","owner":"capr","description":"Brazilian wax for Bash","archived":false,"fork":false,"pushed_at":"2022-01-31T12:30:08.000Z","size":13,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-01-13T23:11:03.390Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/capr.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}},"created_at":"2014-01-05T13:49:50.000Z","updated_at":"2023-05-18T01:59:32.000Z","dependencies_parsed_at":"2022-09-17T14:10:14.468Z","dependency_job_id":null,"html_url":"https://github.com/capr/die","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fdie","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fdie/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fdie/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/capr%2Fdie/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/capr","download_url":"https://codeload.github.com/capr/die/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241661624,"owners_count":19998984,"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":[],"created_at":"2024-11-14T06:56:38.123Z","updated_at":"2025-03-03T12:22:30.828Z","avatar_url":"https://github.com/capr.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"## What\n\nA set of 7 functions for flow control and progress/error reporting for shell scripts. It makes scripts **less hairy** and more illustrative of author's intent, at the same time providing a framework for allowing the user of the script to control the interaction. It has the elegance, flexibility, simplicity, expressivness, slikness, intuitiveness, and powerfullness of a Ruby web framework, without being made in Ruby even. All in 7 lines of code, for the most exigent minimalistic affectations.\n\nThis is how it works:\n\n```bash\n. die\n\nwhile [ $# -gt 0 ]; do\n  case \"$1\" in\n\t --debug) DEBUG=true ;;\n\t --quiet) QUIET=true ;;\n\t --yes)   YES=true ;;\n  esac\n  shift\ndone\n\ndebug \"script started\"            # use --debug to see the message\nsay \"Hi\"                          # suppress with --quiet\nsome_cmd || die \"can't go on\"     # laments and exits with code 1 if the command fails \nrun somecmd args                  # use --debug to see the full command and its exit code \nmust somecmd args                 # if somecmd fails, exits showing what ran and the exit code\nhold \"Look at me now\"             # asks for a keypress; suppress with --yes or --quiet\nerror \"nothing serious\"           # reports an error and continues; supress with --quiet\nsay \"Bye\"                         # suppress with --quiet\ndebug \"script ended\"              # see this with --debug\n```\n\n## Why\n\nAdding error checking and progress/error reporting to shell scripts makes them ugly to the point where is hard to see the bits that do essential work from the error checking/flow control ones. Encapsulating these cross-cutting concerns aside into a small vocabulary solves the problem at the expense of learning the vocabulary.\n\n## The Code\n\n```bash\n#!/bin/sh (source it!)\n# die: basic vocabulary for flow control and progress/error reporting\n# v1.1 | Cosmin Apreutesei (public domain) | http://github.com/capr/die\n# these functions are influenced by $QUIET, $DEBUG and $YES variables\n\nsay()   { [ \"$QUIET\" ] || echo \"$@\" \u003e\u00262; }\nerror() { say -n \"ERROR: \"; say \"$@\"; return 1; }\ndie()   { echo -n \"EXIT: \" \u003e\u00262; echo \"$@\" \u003e\u00262; exit 1; }\ndebug() { [ -z \"$DEBUG\" ] || echo \"$@\" \u003e\u00262; }\nrun()   { debug -n \"EXEC: $@ \"; \"$@\"; local ret=$?; debug \"[$ret]\"; return $ret; }\nmust()  { debug -n \"MUST: $@ \"; \"$@\"; local ret=$?; debug \"[$ret]\"; [ $ret = 0 ] || die \"$@ [$ret]\"; }\nhold()  { [ $# -gt 0 ] \u0026\u0026 say \"$@\"; [ \"$YES$QUIET\" ] \u0026\u0026 return; echo -n \"Press ENTER to continue, or ^C to quit.\"; read; }\n```\n[source](https://raw.github.com/capr/die/master/die) | [testing unit](https://raw.github.com/capr/die/master/die-test)\n\n## In English\n\n**say** `...`         --- echoes arguments to stderr, and only if `$QUIET` is not set \u003cbr\u003e\n**error** `...`       --- says `ERROR: ...` \u003cbr\u003e\n**die** `...`         --- says `EXIT: ...` and exits the current process \u003cbr\u003e\n**debug** `...`       --- says `...`, but only if `$DEBUG` is set \u003cbr\u003e\n**run** _cmd_ `...`   --- run a command and debug-say `EXEC: cmd ... [exit code]` \u003cbr\u003e\n**must** _cmd_ `...`  --- run a command and debug-say `MUST: cmd ... [exit code]`, but die if the command's exit code != 0 \u003cbr\u003e\n**hold** `...`        --- say `...` and then ask for a key press, but only if `$YES` or `$QUIET` is not set\n\n## Caveats\n\n`die` and `must` can only kill the process they were executed in. This means they won't kill the script if invoked from a subprocess. This can look counterintuitive sometimes:\n\n```bash\nsomevar=\"$(must false)\"\nfind | must false\nfind | while read f; do die \"somth bad happen\"; done\nfind | { die \"somth bad happen\"; } #the pipe created the subprocess, not the braces\n(must false)\n(false || die \"somth bad happen\")\necho \"none of these killed me\"\n```\nNote that `$somevar` was set to `\"\"`, since `must` complained on stderr. \n\nAnyway, the way to fix this is to check on the exit code of the subprocess:\n\n```bash\nsomevar=\"$(must false)\" || die \"subprocess failed, echo told us\"\nfind | must false || die \"subprocess failed, | tells us\"\nfind | while read f; do die \"somth bad happen\"; done || die \"subprocess failed, I fail\"\nfind | { die \"somth bad happen\"; } || die \"subprocess failed, I fail\"\n(must false) || die \"subprocess failed\"\n(false || die \"somth bad happen\") || die \"subprocess failed\"\necho \"can't reach here\"\n~~~\n\n`hold` won't work in a subprocess that redirects the standard input -- not only that, it will eat one line of the input as well!\n\n```bash\nfind | hold \"I can't hold you, see?\"\n{ :; } | hold \"Even when there's no input I still can't hold you\"\n~~~\n\n## Feedback\n\n  * `cosmin.apreutesei@gmail.com`\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapr%2Fdie","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcapr%2Fdie","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcapr%2Fdie/lists"}