├── git ├── .gitignore ├── hooks │ ├── commit-msg │ ├── pre-commit │ ├── pre-push │ ├── pre-rebase │ ├── update │ ├── applypatch-msg │ ├── post-update │ ├── pre-applypatch │ ├── prepare-commit-msg │ └── hook-template ├── run-tests ├── t │ ├── git-hooks-init.sh │ ├── git-tdd-init.sh │ ├── git-hooks-list.sh │ ├── git-tdd-status.sh │ ├── git-tdd-accept.sh │ ├── git-tdd-diff.sh │ └── git-tdd-test.sh ├── f │ ├── framework.sh │ ├── file-clauses.sh │ ├── git-hooks-clauses.sh │ ├── git-clauses.sh │ └── git-tdd-clauses.sh ├── Makefile ├── man1 │ └── git-tdd.1.ronn ├── README.md ├── cygwin-pre-commit ├── git-hooks └── git-tdd ├── maven-bash-testing ├── .gitignore ├── .java-version ├── .gitattributes ├── .mvn │ └── wrapper │ │ ├── maven-wrapper.jar │ │ ├── maven-wrapper.properties │ │ └── MavenWrapperDownloader.java ├── src │ ├── main │ │ ├── resources │ │ │ ├── META-INF │ │ │ │ └── jobs │ │ │ └── run-java.sh │ │ └── java │ │ │ └── hm │ │ │ └── binkley │ │ │ └── Main.java │ ├── test │ │ └── resources │ │ │ ├── int-t │ │ │ ├── 01-run-health-tests.sh │ │ │ ├── 02-run-resume-tests.sh │ │ │ └── 00-runner-tests.sh │ │ │ ├── t │ │ │ ├── 02-complex-task-tests.sh │ │ │ ├── 05-health-tests.sh │ │ │ ├── 06-resume-tests.sh │ │ │ ├── 00-runner-tests.sh │ │ │ ├── 04-jobs-tests.sh │ │ │ ├── 01-simple-task-tests.sh │ │ │ └── 03-help-tests.sh │ │ │ ├── test-run-java.sh │ │ │ └── test-functions.sh │ └── assembly │ │ └── testing-with-bash.xml ├── maven-version-rules.xml ├── README.md ├── mvnw.cmd ├── pom.xml └── mvnw ├── unicode ├── .gitignore ├── Makefile ├── README.md └── unicode ├── .gitignore ├── gee ├── .gitignore ├── .test-gee.url ├── Makefile ├── t │ ├── 00-first-time.sh │ ├── 01-local-git.sh │ └── 02-log-unchanged.sh ├── f │ ├── expectations.sh │ ├── clauses.sh │ └── framework.sh ├── README.md ├── gee └── test-gee ├── plain-bash-testing ├── simple-example │ ├── run-tests │ ├── Makefile │ ├── t │ │ ├── 02-ugly-fruit.sh │ │ ├── 01-bad-fruit.sh │ │ └── 00-good-fruit.sh │ └── f │ │ ├── framework.sh │ │ └── clauses.sh ├── t │ ├── 03-mistakes.sh │ ├── 01-pwds.sh │ ├── 02-specials.sh │ └── 00-returns.sh ├── f │ ├── framework.sh │ └── clauses.sh ├── Makefile ├── run-tests-expected.out ├── README.md └── run-tests ├── color ├── Makefile ├── README.md ├── run-color └── color.sh ├── .gitattributes ├── open ├── Makefile ├── starter ├── functions │ ├── Runfile │ ├── where-am-i.sh │ └── greet-greenly.sh ├── README.md └── bash-it-up.sh ├── ksh ├── README.md ├── example-rexec └── rexec.ksh ├── rls ├── run-clean ├── run-tests ├── deduplicate-path.sh ├── maven-tools ├── maven-list-repo ├── maven-clean-repo └── README.md ├── LICENSE.md ├── workaround-for-jenv-on-cygwin ├── README.md ├── fibs ├── mdv ├── jurlq ├── coverage ├── run-from-url └── run-jvm-main /git/.gitignore: -------------------------------------------------------------------------------- 1 | *.1 2 | -------------------------------------------------------------------------------- /git/hooks/commit-msg: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/pre-push: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/pre-rebase: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/update: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/applypatch-msg: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/post-update: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/pre-applypatch: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /git/hooks/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | hook-template -------------------------------------------------------------------------------- /maven-bash-testing/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /maven-bash-testing/.java-version: -------------------------------------------------------------------------------- 1 | 11 2 | -------------------------------------------------------------------------------- /unicode/.gitignore: -------------------------------------------------------------------------------- 1 | /UnicodeData.txt 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .kobalt/ 3 | /.idea/ 4 | -------------------------------------------------------------------------------- /git/run-tests: -------------------------------------------------------------------------------- 1 | ../plain-bash-testing/run-tests -------------------------------------------------------------------------------- /gee/.gitignore: -------------------------------------------------------------------------------- 1 | /.test-gee 2 | /.test-gee.etag 3 | -------------------------------------------------------------------------------- /plain-bash-testing/simple-example/run-tests: -------------------------------------------------------------------------------- 1 | ../run-tests -------------------------------------------------------------------------------- /color/Makefile: -------------------------------------------------------------------------------- 1 | all: check 2 | 3 | check: 4 | @true 5 | 6 | clean: 7 | @true 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eof=lf 2 | *.jar binary 3 | *.bat text eol=crlf 4 | *.cmd text eol=crlf 5 | -------------------------------------------------------------------------------- /maven-bash-testing/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.bat text eol=crlf 3 | *.cmd text eol=crlf 4 | -------------------------------------------------------------------------------- /gee/.test-gee.url: -------------------------------------------------------------------------------- 1 | https://raw.githubusercontent.com/binkley/shell/master/plain-bash-testing/run-tests 2 | -------------------------------------------------------------------------------- /open: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | case $# in 4 | 0 ) set -- . ;; 5 | esac 6 | 7 | exec cygstart "$@" 8 | -------------------------------------------------------------------------------- /unicode/Makefile: -------------------------------------------------------------------------------- 1 | SHELL = bash 2 | 3 | all: check 4 | 5 | check: 6 | @true 7 | 8 | clean: 9 | @rm -f UnicodeData.txt 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL = bash 2 | 3 | all: check 4 | 5 | check: 6 | @./run-tests >/dev/null 7 | 8 | clean: 9 | @./run-clean >/dev/null 10 | -------------------------------------------------------------------------------- /maven-bash-testing/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/binkley/shell/HEAD/maven-bash-testing/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /maven-bash-testing/src/main/resources/META-INF/jobs: -------------------------------------------------------------------------------- 1 | healthy-job 2 | unhealthy-job 3 | resumable-job 4 | unresumable-job 5 | takes-arg-job $1 6 | -------------------------------------------------------------------------------- /starter/functions/Runfile: -------------------------------------------------------------------------------- 1 | all: greet-greenly 2 | 3 | greet-greenly: where-am-i 4 | @echo greet-greenly 5 | 6 | where-am-i: 7 | @echo where-am-i 8 | -------------------------------------------------------------------------------- /plain-bash-testing/simple-example/Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | test: 4 | @./run-tests t 2>&1 | grep 'Summary: 2 PASSED, 2 FAILED, 1 ERRORED' >/dev/null 5 | -------------------------------------------------------------------------------- /plain-bash-testing/t/03-mistakes.sh: -------------------------------------------------------------------------------- 1 | SCENARIO "No arguments to _register" try-register-none 2 | SCENARIO "Too many arguments to _register" try-register-too-many 3 | -------------------------------------------------------------------------------- /git/t/git-hooks-init.sh: -------------------------------------------------------------------------------- 1 | SCENARIO 'HOOKS lists none installed' \ 2 | GIVEN a-cloned-repo-with-commits \ 3 | WHEN hooks-init \ 4 | THEN hooks-installed 5 | -------------------------------------------------------------------------------- /gee/Makefile: -------------------------------------------------------------------------------- 1 | SHELL=/bin/bash 2 | 3 | check: 4 | @./test-gee -i t | grep 'Summary: 6 PASSED, 0 FAILED, 0 ERRORED' >/dev/null 5 | 6 | clean: 7 | @rm -f .test-gee .test-gee.etag 8 | -------------------------------------------------------------------------------- /starter/functions/where-am-i.sh: -------------------------------------------------------------------------------- 1 | function where-am-i() { 2 | $pwd 3 | } 4 | 5 | function -where-am-i-help() { 6 | cat </dev/null 10 | echo $PWD: 11 | make -s clean 12 | ) 13 | elif [[ -f $d/mvnw ]]; then 14 | ( 15 | cd $d >/dev/null 16 | echo $PWD: 17 | ./mvnw -q clean 18 | ) 19 | fi 20 | done 21 | -------------------------------------------------------------------------------- /run-tests: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | unset CDPATH 4 | set -e 5 | 6 | for d in *; do 7 | if [[ -f $d/Makefile ]]; then 8 | ( 9 | cd $d >/dev/null 10 | echo $PWD: 11 | make -s check 12 | ) 13 | elif [[ -f $d/mvnw ]]; then 14 | ( 15 | cd $d >/dev/null 16 | echo $PWD: 17 | ./mvnw -q clean verify 18 | ) 19 | fi 20 | done 21 | -------------------------------------------------------------------------------- /git/t/git-hooks-list.sh: -------------------------------------------------------------------------------- 1 | SCENARIO 'HOOKS lists no pre-commit installed' \ 2 | GIVEN a-cloned-repo-with-commits \ 3 | WHEN hooks-init \ 4 | AND hooks-list pre-commit \ 5 | THEN output-is '' 6 | 7 | SCENARIO 'HOOKS lists one pre-commit installed' \ 8 | GIVEN a-cloned-repo-with-commits \ 9 | WHEN hooks-init \ 10 | AND add-hook pre-commit good \ 11 | AND hooks-list pre-commit \ 12 | THEN output-is 'good' 13 | -------------------------------------------------------------------------------- /deduplicate-path.sh: -------------------------------------------------------------------------------- 1 | function deduplicate-path() { 2 | local -a paths 3 | mapfile -t paths < <(echo "$PATH" | tr : '\n') 4 | local -a unique=() 5 | for p in "${paths[@]}"; do 6 | for u in "${unique[@]}"; do 7 | [[ "$p" == "$u" ]] && continue 2 # JMP back to paths loop 8 | done 9 | unique+=("$p") 10 | done 11 | 12 | # More complex than liked, but ensures no trailing colon vs printf 13 | ( 14 | IFS=: 15 | echo "${unique[*]}" 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /git/t/git-tdd-status.sh: -------------------------------------------------------------------------------- 1 | SCENARIO 'TDD status on WIP' \ 2 | GIVEN a-cloned-repo-with-commits \ 3 | WHEN tdd-init \ 4 | THEN shows-current-commit 5 | 6 | SCENARIO 'TDD status on new repo' \ 7 | GIVEN an-empty-repo-with-initial-empty-commit \ 8 | WHEN tdd-status \ 9 | THEN user-failed \ 10 | AND not-initialized 11 | 12 | SCENARIO 'TDD status on non-TDD repo' \ 13 | GIVEN a-cloned-repo-with-commits \ 14 | WHEN tdd-status \ 15 | THEN user-failed \ 16 | AND not-initialized 17 | -------------------------------------------------------------------------------- /maven-bash-testing/src/test/resources/t/05-health-tests.sh: -------------------------------------------------------------------------------- 1 | # Source me - do not execute 2 | 3 | scenario 'Simple job health' \ 4 | given_jar a.jar with_jobs 'simple-job' \ 5 | when_run -n --health 'simple-job' \ 6 | then_exit 0 \ 7 | with_out 'java -jar ./lib/a.jar --health simple-job' 8 | 9 | scenario 'Complex job health' \ 10 | given_jar a.jar with_jobs 'complex-job -Dfoo=$1 $2 arg' \ 11 | when_run -n --health 'complex-job' \ 12 | then_exit 0 \ 13 | with_out 'java -jar ./lib/a.jar --health complex-job' 14 | -------------------------------------------------------------------------------- /gee/t/00-first-time.sh: -------------------------------------------------------------------------------- 1 | # vi: ft=bash 2 | # Source me 3 | 4 | SCENARIO 'Run echo on command line' \ 5 | GIVEN first_time_in_repo \ 6 | WHEN run_echo 'Uncle Bob' with_program \ 7 | THEN exit_with 0 \ 8 | AND on_stdout 'Uncle Bob' \ 9 | AND git_log_message 'echo Uncle Bob' 10 | 11 | SCENARIO 'Run echo in pipeline' \ 12 | GIVEN first_time_in_repo \ 13 | WHEN run_echo 'Uncle Bob' having_message 'First time' in_pipe \ 14 | THEN exit_with 0 \ 15 | AND on_stdout 'Uncle Bob' \ 16 | AND git_log_message 'First time' 17 | 18 | -------------------------------------------------------------------------------- /maven-tools/maven-list-repo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | case $# in 4 | 0 ) : ${M2_REPO:=$HOME/.m2/repository} ;; 5 | 1 ) M2_REPO=$1 ;; 6 | * ) exit 2 ;; 7 | esac 8 | 9 | cd $M2_REPO 10 | 11 | while read d 12 | do 13 | group=${d%/*} 14 | group=${group%/*} 15 | artifact=${d%/*} 16 | artifact=${artifact##*/} 17 | version=${d##*/} 18 | a=($group/$artifact/$version/$artifact-$version.*) 19 | [[ ${a[0]} == "$group/$artifact/$version/$artifact-$version.*" ]] && continue 20 | echo $M2_REPO/$group/$artifact/$version ${group//\//.} $artifact $version 21 | done < <(find * -type d) 22 | -------------------------------------------------------------------------------- /maven-bash-testing/src/test/resources/t/06-resume-tests.sh: -------------------------------------------------------------------------------- 1 | # Source me - do not execute 2 | 3 | scenario 'Simple job resume' \ 4 | given_jar a.jar with_jobs 'simple-job' \ 5 | when_run -n --resume 'simple-job' \ 6 | then_exit 0 \ 7 | with_out 'java -jar ./lib/a.jar --resume simple-job' 8 | 9 | scenario 'Complex job health' \ 10 | given_jar a.jar with_jobs 'complex-job -Dfoo=$1 $2 arg' \ 11 | when_run -n --resume 'complex-job' 'first-arg' 'second-arg' \ 12 | then_exit 0 \ 13 | with_out <<'EOO' 14 | java -Dfoo=first-arg -jar ./lib/a.jar --resume complex-job second-arg arg 15 | EOO 16 | -------------------------------------------------------------------------------- /gee/t/01-local-git.sh: -------------------------------------------------------------------------------- 1 | # vi: ft=bash 2 | # Source me 3 | 4 | SCENARIO 'Run echo on command line' \ 5 | GIVEN first_time_in_repo \ 6 | AND local_git \ 7 | WHEN run_echo 'Uncle Bob' with_program \ 8 | THEN exit_with 0 \ 9 | AND on_stdout 'Uncle Bob' \ 10 | AND git_log_message 'echo Uncle Bob' 11 | 12 | SCENARIO 'Run echo in pipeline' \ 13 | GIVEN first_time_in_repo \ 14 | AND local_git \ 15 | WHEN run_echo 'Uncle Bob' having_message 'First time' in_pipe \ 16 | THEN exit_with 0 \ 17 | AND on_stdout 'Uncle Bob' \ 18 | AND git_log_message 'First time' 19 | 20 | -------------------------------------------------------------------------------- /gee/t/02-log-unchanged.sh: -------------------------------------------------------------------------------- 1 | # vi: ft=bash 2 | # Source me 3 | 4 | SCENARIO 'Run same output' \ 5 | GIVEN first_time_in_repo \ 6 | WHEN run_echo 'Uncle Bob' with_program \ 7 | AND run_echo 'Uncle Bob' having_message 'Second time' with_program \ 8 | THEN git_log_message 'echo Uncle Bob' 9 | 10 | SCENARIO 'Run same output but do log unchanged' \ 11 | GIVEN first_time_in_repo \ 12 | WHEN run_echo 'Uncle Bob' with_program \ 13 | AND run_echo 'Uncle Bob' having_message 'Second time' log_unchanged with_program \ 14 | THEN git_log_message - <$1 4 | ) 5 | 6 | function a-change-to-existing() { 7 | _a-change Bob 8 | } 9 | _register a-change-to-existing 10 | 11 | function another-change-to-existing() ( 12 | cd $repodir && 13 | echo OK >Bob 14 | ) 15 | _register another-change-to-existing 16 | 17 | function this-change() { 18 | _a-change $1 19 | } 20 | _register this-change 1 21 | 22 | function this-change-added() ( 23 | cd $repodir && 24 | git add $1 25 | ) 26 | _register this-change-added 1 27 | 28 | function this-change-added() ( 29 | cd $repodir && 30 | git add $1 31 | ) 32 | _register this-change-added 1 33 | -------------------------------------------------------------------------------- /git/man1/git-tdd.1.ronn: -------------------------------------------------------------------------------- 1 | git-tdd(1) -- TDD for Git 2 | ========================= 3 | 4 | ## SYNOPSIS 5 | 6 | ## DESCRIPTION 7 | 8 | ## OPTIONS 9 | 10 | ## EXAMPLES 11 | 12 | ## DISCUSSION 13 | 14 | ## ENVIRONMENT AND CONFIGURATION VARIABLES 15 | 16 | ## HOOKS 17 | 18 | ## FILES 19 | 20 | ## EXIT STATUS 21 | 22 | 0 - Success
23 | 2 - Usage error
24 | * - Exit status of underlying git command 25 | 26 | ## BUGS 27 | 28 | * Too much porcelain, not enough plumbing. 29 | 30 | ## COPYRIGHT 31 | 32 | Public domain 33 | 34 | ## SEE ALSO 35 | 36 | git-commit(1) 37 | git-diff(1) 38 | git-log(1) 39 | git-rebase(1) 40 | git-notes(1) 41 | git-pull(1) 42 | git-reset(1) 43 | git-stash(1) 44 | 45 | ## GIT 46 | 47 | Extension to the git(1) suite 48 | -------------------------------------------------------------------------------- /plain-bash-testing/t/00-returns.sh: -------------------------------------------------------------------------------- 1 | # vi: ft=bash 2 | # Source me 3 | 4 | SCENARIO "Normal return pass direct" normal_return 0 5 | SCENARIO "Normal return fail direct" normal_return 1 6 | SCENARIO "Normal return error direct" normal_return 2 7 | SCENARIO "Normal return pass indirect" f AND normal_return 0 8 | SCENARIO "Normal return fail indirect" f AND normal_return 1 9 | SCENARIO "Normal return error indirect" f AND normal_return 2 10 | SCENARIO "Early return pass indirect" f AND early_return 0 11 | SCENARIO "Early return fail indirect" f AND early_return 1 12 | SCENARIO "Early return error indirect" f AND early_return 2 13 | SCENARIO "Early return pass direct" early_return 0 14 | SCENARIO "Early return fail direct" early_return 1 15 | SCENARIO "Early return error direct" early_return 2 16 | -------------------------------------------------------------------------------- /starter/README.md: -------------------------------------------------------------------------------- 1 | # Starter 2 | 3 | [bash-it-up.sh](bash-it-up.sh) is a starter script for writing commands in 4 | BASH. 5 | 6 | ## Features 7 | 8 | * Full help 9 | * Long option arguments 10 | * Debugging script itself 11 | * Dry-run example 12 | * Disable color if in a pipe 13 | * Segregated tasks (sub-commands) into separate files 14 | * Sample tasks (sub-commands) with individual help 15 | * [Task dependency ordering](#task-dependency-ordering) 16 | 17 | ## Task dependency ordering 18 | 19 | For a small project, using `make` to decide which tasks to run is overkill. 20 | However, on larger project, those with multiple possibly programs to run, and 21 | dependencies—shared or common—among them, it is simpler to 22 | describe the dependencies, rather than write logic to do so. 23 | -------------------------------------------------------------------------------- /maven-bash-testing/src/test/resources/t/04-jobs-tests.sh: -------------------------------------------------------------------------------- 1 | # Source me - do not execute 2 | 3 | scenario "Short option jobs" \ 4 | given_jar a.jar with_jobs 'simple-job' \ 5 | also_jar b.jar with_jobs 'trivial-job' \ 6 | when_run '-j' \ 7 | then_exit 0 \ 8 | with_out <<'EOO' 9 | simple-job 10 | trivial-job 11 | EOO 12 | 13 | scenario "Long option jobs" \ 14 | given_jar a.jar with_jobs 'simple-job' \ 15 | also_jar b.jar with_jobs 'trivial-job' \ 16 | when_run '--jobs' \ 17 | then_exit 0 \ 18 | with_out <<'EOO' 19 | simple-job 20 | trivial-job 21 | EOO 22 | 23 | scenario "Complex jobs help" \ 24 | given_jar a.jar with_jobs 'complex-job -Dfile=$1 $2' \ 25 | when_run '--jobs' \ 26 | then_exit 0 \ 27 | with_out <<'EOO' 28 | complex-job -Dfile=$1 $2 29 | EOO 30 | -------------------------------------------------------------------------------- /maven-bash-testing/src/test/resources/t/01-simple-task-tests.sh: -------------------------------------------------------------------------------- 1 | # Source me - do not execute 2 | 3 | scenario "Simple job in simple jar" \ 4 | given_jar some.jar with_jobs 'simple-job' \ 5 | when_run -n 'simple-job' \ 6 | then_exit 0 \ 7 | with_out 'java -jar ./lib/some.jar simple-job' 8 | 9 | scenario "Simple job in complex jar" \ 10 | given_jar some.jar with_jobs <<'EOT' \ 11 | when_run -n 'simple-job' \ 12 | then_exit 0 \ 13 | with_out 'java -jar ./lib/some.jar simple-job' 14 | simple-job 15 | complex-job -flag -Dfile=$1 16 | EOT 17 | 18 | scenario "Simple job in simple jar of multiple" \ 19 | given_jar a.jar with_jobs 'simple-job' \ 20 | also_jar b.jar with_jobs 'complex-job -flag -Dfile=$1' \ 21 | when_run -n 'simple-job' \ 22 | then_exit 0 \ 23 | with_out 'java -jar ./lib/a.jar simple-job' 24 | -------------------------------------------------------------------------------- /maven-bash-testing/maven-version-rules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | (?i).*Alpha(?:-?\d+)? 10 | (?i).*Beta(?:-?\d+)? 11 | (?i).*-B(?:-?\d+)? 12 | (?i).*RC(?:-?\d+)? 13 | (?i).*CR(?:-?\d+)? 14 | (?i).*M(?:-?\d+)? 15 | 16 | 17 | -------------------------------------------------------------------------------- /plain-bash-testing/simple-example/f/framework.sh: -------------------------------------------------------------------------------- 1 | # vi: ft=bash 2 | # Source me 3 | 4 | printf -v pcheckmark "\xE2\x9C\x93" 5 | readonly pcheckmark 6 | readonly ppass="$pgreen$pcheckmark$preset" 7 | printf -v pballotx "\xE2\x9C\x97" 8 | readonly pballotx 9 | readonly pfail="$pred$pballotx$preset" 10 | printf -v pinterrobang "\xE2\x80\xBD" 11 | readonly pinterrobang 12 | readonly perror="$pboldred$pinterrobang$preset" 13 | 14 | function _print_result { 15 | local -r exit_code=$1 16 | $_quiet && return $exit_code 17 | case $exit_code in 18 | 0 ) echo -e "$ppass $scenario_name" ;; 19 | 1 ) echo -e "$pfail $scenario_name - wanted: $expected_color, got $actual_color" ;; 20 | * ) echo -e "$perror $scenario_name - exit: $exit_code" ;; 21 | esac 22 | } 23 | 24 | function THEN { 25 | "$@" 26 | } 27 | 28 | function WHEN { 29 | "$@" 30 | } 31 | 32 | function GIVEN { 33 | "$@" 34 | } 35 | 36 | function SCENARIO { 37 | local -r scenario_name="$1" 38 | shift 39 | _start "$@" 40 | } 41 | -------------------------------------------------------------------------------- /maven-bash-testing/src/assembly/testing-with-bash.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | testing-with-bash 7 | 8 | dir 9 | 10 | false 11 | 12 | 13 | ${project.build.directory}/classes/run-java.sh 14 | 0755 15 | 16 | 17 | lib 18 | 19 | ${project.build.directory}/${project.build.finalName}.${project.packaging} 20 | 21 | 0644 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /plain-bash-testing/simple-example/f/clauses.sh: -------------------------------------------------------------------------------- 1 | function color_of { 2 | local -r fruit="$1" 3 | case $fruit in 4 | avacado ) echo greenish ;; 5 | orange ) echo orange ;; 6 | rambutan ) echo red ;; 7 | strawberry ) echo red ;; 8 | * ) echo "$0: Unknown fruit: $fruit" >&2 9 | return 2 ;; # Not exit! 10 | esac 11 | } 12 | 13 | # For GIVEN - cannot fail or error, so do not `_register` 14 | function a_rome_apple { 15 | local -r expected_color="red" 16 | "$@" 17 | } 18 | 19 | # For WHEN 20 | function fruit_is { 21 | local -r fruit="$1" 22 | local actual_color 23 | case $fruit in 24 | avacado ) actual_color="greenish" ;; 25 | orange ) actual_color="orange" ;; 26 | rambutan ) actual_color="red" ;; 27 | strawberry ) actual_color="red" ;; 28 | * ) echo "$0: Unknown fruit: $fruit" >&2 29 | return 2 ;; # Not exit! 30 | esac 31 | } 32 | _register fruit_is 1 33 | 34 | # For THEN 35 | function the_colors_agree { 36 | [[ "$actual_color" == "$expected_color" ]] 37 | } 38 | _register the_colors_agree 39 | -------------------------------------------------------------------------------- /plain-bash-testing/run-tests-expected.out: -------------------------------------------------------------------------------- 1 | Script t/00-returns.sh: 2 | . Normal return pass direct ✓ 3 | . Normal return fail direct (normal_return) ✗ 4 | . Normal return error direct (normal_return; exit 2) ‽ 5 | . Normal return pass indirect ✓ 6 | . Normal return fail indirect (normal_return) ✗ 7 | . Normal return error indirect (normal_return; exit 2) ‽ 8 | . Early return pass indirect ✓ 9 | . Early return fail indirect (f) ✗ 10 | . Early return error indirect (f; exit 2) ‽ 11 | . Early return pass direct ✓ 12 | . Early return fail direct () ✗ 13 | . Early return error direct (; exit 2) ‽ 14 | 4 errored, 4 failed, 4 passed 15 | Script t/01-pwds.sh: 16 | . Change directory ✓ 17 | . Check directory cd ✓ 18 | . Push directory ✓ 19 | . Check directory pushd ✓ 20 | 4 passed 21 | Script t/02-specials.sh: 22 | . Terminal variadic with none ✓ 23 | . Terminal variadic with one ✓ 24 | . Interior variadic ✓ 25 | . Dynamic scope ✓ 26 | . Check exit ✓ 27 | 5 passed 28 | Script t/03-mistakes.sh: 29 | . No arguments to _register ✓ 30 | . Too many arguments to _register ✓ 31 | 2 passed 32 | Summary: 15 PASSED, 4 FAILED, 4 ERRORED 33 | -------------------------------------------------------------------------------- /git/f/git-hooks-clauses.sh: -------------------------------------------------------------------------------- 1 | # WHEN 2 | function hooks-list { 3 | local hook=$1 4 | list_output="$(cd $repodir \ 5 | && $git_hooks list $1)" 6 | } 7 | _register hooks-list 1 8 | 9 | function hooks-init() ( 10 | cd $repodir \ 11 | && $git_hooks init 12 | ) 13 | _register hooks-init 1 14 | 15 | function add-hook { 16 | local hook=$1 17 | local script=$2 18 | case $script in 19 | good ) ln -s /usr/bin/true $repodir/.git/hooks/$hook.d/$script ;; 20 | bad ) ln -s /usr/bin/false $repodir/.git/hooks/$hook.d/$script ;; 21 | esac 22 | } 23 | _register add-hook 2 24 | 25 | # THEN 26 | function output-is { 27 | _expected="$1" 28 | _actual="$list_output" 29 | [[ "$_expected" == "$_actual" ]] 30 | } 31 | _register output-is 1 32 | 33 | function hooks-installed { 34 | (cd $repodir/.git/hooks \ 35 | && [[ -f hooks ]] \ 36 | && [[ -r hooks ]] \ 37 | && [[ -x hooks ]] \ 38 | && for h in *.sample 39 | do 40 | [[ -d ${h%.sample}.d ]] 41 | [[ -h ${h%.sample} ]] 42 | done) 43 | } 44 | _register hooks-installed 45 | -------------------------------------------------------------------------------- /gee/f/expectations.sh: -------------------------------------------------------------------------------- 1 | # vi: ft=bash 2 | # Source me 3 | 4 | function exit_with { 5 | local actual=$? 6 | local expected=$1 7 | 8 | (( expected == actual )) 9 | } 10 | _register exit_with 1 11 | 12 | function on_stderr { 13 | local expected 14 | case $1 in 15 | - ) read -d '' -r expected || true ;; 16 | * ) expected="$1" ;; 17 | esac 18 | shift 19 | 20 | local actual="$(<$stderr)" 21 | 22 | [[ "$expected" == "$actual" ]] 23 | } 24 | _register on_stderr 1 25 | 26 | function on_stdout { 27 | local expected 28 | case $1 in 29 | - ) read -d '' -r expected || true ;; 30 | * ) expected="$1" ;; 31 | esac 32 | 33 | local actual="$(<$stdout)" 34 | 35 | [[ "$expected" == "$actual" ]] 36 | } 37 | _register on_stdout 1 38 | 39 | function git_log_message { 40 | local expected 41 | case $1 in 42 | - ) read -d '' -r expected || true ;; 43 | * ) expected="$1" ;; 44 | esac 45 | 46 | [[ -z $local_git ]] && local -r gee_g='./gee -g' 47 | local actual="$($gee_g git log -1 --pretty=format:%B)" 48 | 49 | [[ "$expected" == "$actual" ]] 50 | } 51 | _register git_log_message 1 52 | -------------------------------------------------------------------------------- /maven-tools/maven-clean-repo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | last_group= 4 | last_artifact= 5 | version=() 6 | paths=() 7 | 8 | while read path group artifact version 9 | do 10 | if [[ $group == $last_group && $artifact == $last_artifact ]] 11 | then 12 | versions+=($version) 13 | paths+=($path) 14 | else 15 | # TODO: How to sort versions by Maven rules, so can automate? 16 | if (( 1 < ${#paths[*]})) 17 | then 18 | x=$((${#paths[*]} - 1)) 19 | echo "Duplicate versions for $last_group:$last_artifact. Keep which?" 20 | select keep in SKIP ${versions[*]} 21 | do 22 | case $keep in 23 | SKIP ) ;; 24 | * ) for i in $(seq 0 $((${#versions[*]} - 1))) 25 | do 26 | if [[ $keep != ${versions[i]} ]] 27 | then 28 | rm -rf ${paths[i]} 29 | fi 30 | done 31 | esac 32 | break 33 | done &2 ; exit 2 ;; 17 | esac 18 | done 19 | shift $(( OPTIND - 1 )) 20 | 21 | case $# in 22 | 1 ) hostname=$1 ;; 23 | * ) usage >&2 ; exit 2 ;; 24 | esac 25 | 26 | case $debug in 27 | 0 ) debug=false ;; 28 | 1 ) debug=true ;; 29 | * ) debug=true ; set -x ;; 30 | esac 31 | 32 | . ./rexec.ksh 33 | 34 | script_name=My-Remote-Script 35 | 36 | tmpout=${TMPDIR-/tmp}/$progname.$RANDOM 37 | tmperr=${TMPDIR-/tmp}/$progname.$RANDOM 38 | trap 'rm -f $tmpout $tmperr' EXIT 39 | 40 | rscript $script_name $hostname Katy <<'EOS' >$tmpout 2>$tmperr 41 | echo $#: $1 42 | trap 'echo In script trap' EXIT 43 | echo STDERR >&2 44 | fail 3 45 | EOS 46 | 47 | case $? in 48 | 3 ) ;; 49 | * ) echo "$0: Did not pass through exit code" >&2 ; exit 1 ;; 50 | esac 51 | 52 | case "$(<$tmpout)" in 53 | '1: Katy' ) ;; 54 | * ) echo "$0: Did not pass through arguments" >&2 ; exit 1 ;; 55 | esac 56 | 57 | case "$(grep -v '^+' <$tmperr)" in 58 | STDERR ) grep '^+' <$tmperr ;; 59 | * ) echo "$0: Did not print to stderr:" >&2 ; cat $tmperr >&2 ; exit 1 ;; 60 | esac 61 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # License # 2 | 3 | This is free and unencumbered software released into the public domain. 4 | 5 | Anyone is free to copy, modify, publish, use, compile, sell, or 6 | distribute this software, either in source code form or as a compiled 7 | binary, for any purpose, commercial or non-commercial, and by any 8 | means. 9 | 10 | In jurisdictions that recognize copyright laws, the author or authors 11 | of this software dedicate any and all copyright interest in the 12 | software to the public domain. We make this dedication for the benefit 13 | of the public at large and to the detriment of our heirs and 14 | successors. We intend this dedication to be an overt act of 15 | relinquishment in perpetuity of all present and future rights to this 16 | software under copyright law. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 21 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 22 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 24 | OTHER DEALINGS IN THE SOFTWARE. 25 | 26 | For more information, please refer to . 27 | 28 | The source code is available at: . 29 | -------------------------------------------------------------------------------- /plain-bash-testing/f/clauses.sh: -------------------------------------------------------------------------------- 1 | function make_exit { 2 | local -r e=$1 3 | shift 4 | (exit $e) 5 | "$@" 6 | } 7 | 8 | function check_exit { 9 | (( $? == $1 )) 10 | } 11 | _register check_exit 1 12 | 13 | function check_d { 14 | [[ $PWD == $1 ]] 15 | } 16 | _register check_d 1 17 | 18 | function push_d { 19 | pushd $1 >/dev/null 20 | } 21 | _register push_d 1 22 | 23 | function change_d { 24 | cd $1 25 | } 26 | _register change_d 1 27 | 28 | function terminal_variadic { 29 | : 30 | } 31 | _register terminal_variadic 1 32 | 33 | function interior_variadic { 34 | for arg 35 | do 36 | case $arg in 37 | - ) shift ; break ;; 38 | * ) shift ;; 39 | esac 40 | done 41 | } 42 | _register interior_variadic 43 | 44 | function early_return { 45 | return $1 46 | } 47 | _register early_return 1 48 | 49 | function normal_return { 50 | (exit $1) 51 | } 52 | _register normal_return 1 53 | 54 | function dynamic_scope { 55 | [[ "$bob" == nancy ]] 56 | } 57 | _register dynamic_scope 58 | 59 | function f { 60 | local -r bob=nancy 61 | } 62 | _register f 63 | 64 | function try-register-none { 65 | ( _register 2>/dev/null ) 66 | (( 2 == $? )) 67 | } 68 | _register try-register-none 69 | 70 | function try-register-too-many { 71 | ( _register f 0 extra-arg 2>/dev/null ) 72 | (( 2 == $? )) 73 | } 74 | _register try-register-too-many 75 | -------------------------------------------------------------------------------- /gee/f/clauses.sh: -------------------------------------------------------------------------------- 1 | # vi: ft=bash 2 | # Source me 3 | 4 | function first_time_in_repo { 5 | local stack=($(caller 0)) 6 | local -r previous=${stack[1]} 7 | 8 | case $previous in 9 | GIVEN ) ;; 10 | * ) _bad_syntax before GIVEN ;; 11 | esac 12 | 13 | local -r repo_dir=$tmpdir/git 14 | git init $repo_dir >$stdout 2>$stderr 15 | cp gee $repo_dir 16 | cd $repo_dir >/dev/null 17 | git add gee 18 | git commit gee -m 'First commit' >/dev/null 19 | } 20 | _register first_time_in_repo 21 | 22 | function _gee { 23 | # TODO: More elegant approach than multi-step and tmp file 24 | local -r tmp_stderr=$tmpdir/tmp_stderr 25 | ./gee $local_git $log_unchanged "${message[@]}" "$@" >$stdout 2>$tmp_stderr 26 | grep '^+' $tmp_stderr >&2 27 | grep -v '^+' $tmp_stderr >$stderr 28 | rm -f $tmp_stderr 29 | } 30 | 31 | function local_git { 32 | local -r local_git=-l 33 | } 34 | _register local_git 35 | 36 | function having_message { 37 | local -r message=(-m "$1") 38 | } 39 | _register having_message 1 40 | 41 | function log_unchanged { 42 | local -r log_unchanged=-u 43 | } 44 | _register log_unchanged 45 | 46 | function run_echo { 47 | local command=(echo "$1") 48 | } 49 | _register run_echo 1 50 | 51 | function with_program { 52 | _gee "${command[@]}" 53 | } 54 | _register with_program 55 | 56 | function in_pipe { 57 | "${command[@]}" | _gee 58 | } 59 | _register in_pipe 60 | -------------------------------------------------------------------------------- /ksh/rexec.ksh: -------------------------------------------------------------------------------- 1 | _transfer_exit_code() { 2 | while read line 3 | do 4 | case $line in 5 | ^[0-9] | ^[1-9][0-9] | ^11[0-9] | ^12[0-7] ) return ${line#^} ;; 6 | * ) printf '%s\n' "$line" ;; 7 | esac 8 | done 9 | return 1 # ksh93e lacks pipefail; we get here when 'rscript' failed 10 | } 11 | 12 | rscript() { 13 | case $# in 14 | 0 | 1 ) 15 | echo "$progname: BUG: Usage: rscript SCRIPT-NAME HOSTNAME [ARGS]..." >&2 ;; 16 | * ) script_name=$1 ; shift 17 | hostname=$1 ; shift ;; 18 | esac 19 | # Trace callers script if we ourselves are being traced 20 | if ${debug-false} 21 | then 22 | _set_x='set -x' 23 | else 24 | case $- in 25 | *x* ) _set_x='set -x' ;; 26 | esac 27 | fi 28 | 29 | rexec $hostname /usr/bin/ksh93 -s "$@" <&2 ; return 2 ;; 33 | esac 34 | done 35 | shift $((OPTIND - 1)) 36 | 37 | case $# in 38 | 1 ) ;; 39 | * ) echo "Usage: $FUNCNAME [-hv] VERSION" >&2 ; return 2 ;; 40 | esac 41 | 42 | if ! [[ ${java_v[$1]+foo} ]] 43 | then 44 | echo "$FUNCNAME: No such Java version: $1. Try $FUNCNAME -h" >&2 45 | return 2 46 | fi 47 | 48 | export JAVA_HOME='C:\Program Files\Java\jdk'${java_v[$1]} 49 | for v in ${!java_v[@]} 50 | do 51 | case $v in 52 | $1 ) ;; 53 | * ) export PATH="${PATH//${java_v[$v]}/${java_v[$1]}}" ;; 54 | esac 55 | done 56 | 57 | if $verbose 58 | then 59 | echo "$FUNCNAME: Updated JAVA_HOME and PATH for JDK to $v at $JAVA_HOME" 60 | fi 61 | } 62 | -------------------------------------------------------------------------------- /maven-tools/README.md: -------------------------------------------------------------------------------- 1 | # Maven tools 2 | 3 | A collection of helper scripts for Maven. 4 | 5 | * [Kotlin runner](#kotlin-runner) - Run a single-jar Kotlin project 6 | 7 | ## Kotlin runner 8 | 9 | [`run-kotlin-main`](run-kotlin-main) runs a single-jar Kotlin project, 10 | properly picking the JVM class name for a given simplified Kotlin name 11 | (class names are automatically titlecased, and "Kt" appended if needed): 12 | 13 | - `./run-kotlin-main [-X ARGUMENTS]` -- runs an executable jar 14 | - `./run-kotlin-main CLASS [ARGUMENTS]` runs `main` from "CLASS" 15 | 16 | Further, if the single jar is out-of-date w.r.t. the source tree, 17 | automatically runs `./mvnw package` before executing. 18 | 19 | **NB** — To accommodate both arbitrary options and arguments to 20 | `main`, and treating the first argument as an alternative classname for 21 | `main`, use the `--executable` (`-X`) flag as needed: 22 | 23 | 1. If there are no flags or arguments to the script, treat the single jar as 24 | executable 25 | 2. If there are flags to the script, use a `--` to stop further processing the 26 | command line; however the first remaining argument will be treated as a 27 | classname with remaining flags or arguments passed to `main` 28 | 3. The `-X` flag stops all processing, treating the single jar as executable, 29 | and passes remaining flags or arguments to the jar 30 | 31 | Use the `--debug` (`-d`) flag to debug the script: 32 | `./run-kotlin-main -d [-X ARGUMENTS]` or 33 | `./run-kotlin-main -d -- CLASS [ARGUMENTS]`. 34 | 35 | See [Apache Maven Assembly Plugin](https://maven.apache.org/plugins/maven-assembly-plugin/) 36 | for instructions on single-jar projects. 37 | 38 | ### Tests 39 | 40 | There are no tests. 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # binkley's Shell Scripts and Programs 2 | 3 | ## Contents 4 | 5 | Helpful shell scripts and programs. 6 | 7 | * [color](color/README.md) - Working with 8- and 24-bit color in the terminal 8 | * [coverage](coverage) - Check JVM Maven project coverage with JaCoCo and 9 | Pitest 10 | * [fibs](fibs) - Matrix math with Fibonacci Q-matrix 11 | * [gee](gee/README.md) - Git and tee mashup 12 | * [git](git/README.md) - Git help and extensions 13 | * [jurlq](jurlq) - Mash-up of curl and jq ("xpath" for JSON) (Deprecated: use 14 | HTTPie) 15 | * [ksh](ksh) - KSH-specific scripting 16 | * [maven-bash-testing](maven-bash-testing/README.md) - Maven driving Bash 17 | * [maven-tools](maven-tools) - Helper scripts for maven repos 18 | * [mdv](mdv) - Markdown viewer 19 | * [open](open) - Launch UI programs from the command line 20 | * [plain-bash-testing](plain-bash-testing/README.md) - Bash test framework 21 | * [rls](rls) - Handle GPG from command line for other programs 22 | * [run-jvm-main](run-jvm-main) - Run executable jar including auto-rebuild 23 | * [svn-recommit](svn-recommit/README.md) - Reedit an SVN commit message 24 | * [starter](starter/README.md) - Starter script for writing commands in BASH 25 | * [unicode](unicode/README.md) - Work with UNICODE data 26 | * [workaround-for-jenv-on-cygwin](workaround-for-jenv-on-cygwin) - Until jenv.be supports Cygwin 27 | 28 | ## Building 29 | 30 | ``` 31 | $ make 32 | ``` 33 | 34 | On success exits 0 and prints no output. On failures, should be noisy and 35 | in color (when run on a suitable terminal). However, parse errors by BASH will not be in color: it is too early to have enabled color support. 36 | 37 | ## Tools in this directory 38 | 39 | * `fibs` - Shows off the Fibonacci Q matrix with Bash integer math 40 | * `mdv` - Markdown viewer 41 | 42 | ## TODO 43 | 44 | * Top-level make does not know about working offline 45 | 46 | ## Resources 47 | 48 | * [_Advanced Bash-Scripting Guild_](http://www.tldp.org/LDP/abs/html/) 49 | -------------------------------------------------------------------------------- /git/README.md: -------------------------------------------------------------------------------- 1 | # Git 2 | 3 | Git help and extensions 4 | 5 | * [cygwin-pre-commit](cygwin-pre-commit) - Pre-commit hook for Cygwin line 6 | endings and execute bits 7 | * [git-hooks](git-hooks) - TDD extension for hook management 8 | * [git-tdd](git-tdd) - TDD extension for git focused on frequent commits 9 | 10 | ## `cygwin-pre-commit` 11 | 12 | Working on Cygwin presents some challenges when sharing code, e.g., GitHub and 13 | competitors. This _pre-commit_ hook handles most issues most times: 14 | 15 | * De-dosify non-DOS text files 16 | * De-unixfy DOS text files 17 | * Fix the execute bit on scripts 18 | * Leave binaries and symlinks alone 19 | 20 | ## `git hooks` 21 | 22 | ## `git tdd` 23 | 24 | ### Rationale 25 | 26 | * Always test code! Do not commit unless tests pass. 27 | * Test frequently, so commit _locally_ frequently. 28 | * Share code (push) when something is ready to share, not partway done. 29 | * Avoid _partial commits_: they are a common cause of "works for me". 30 | * Get in a good command line cycle: edit, tests pass, commit, repeat. 31 | 32 | ### Concepts 33 | 34 | _WIP_ - The "work in progress" is a local commit holding all files when tests 35 | have passed. Each passing test cycle amends the WIP commit. 36 | 37 | _Accepting_ - When work is ready to share, "accepting" runs tests one last 38 | time (possibly more complete or expensive tests) before pushing, and starting 39 | a new, empty WIP commit. 40 | 41 | ### Subcommands 42 | 43 | * `tdd init` - First time setup, needed for other subcommands. Repo should 44 | have at least one commit prior (even if an empty commit). 45 | * `tdd status` - Shows which files are committed to WIP, which are untested. 46 | * `tdd test` - Runs tests and commits locally to WIP if passing. 47 | * `tdd accept` - Runs tests and pushes if passing. Starts a new WIP. 48 | 49 | ## TODO 50 | 51 | * Fix broken tests in `git` because of colorized output 52 | * Investigate `git tdd init` when there is something already stashed 53 | * Add manpages 54 | -------------------------------------------------------------------------------- /fibs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function usage() { 4 | echo "Usage: $0 n" 5 | } 6 | 7 | int_re='-?(0|[1-9][0-9]*)' 8 | 9 | case $# in 10 | 1) if [[ $1 =~ $int_re ]]; then 11 | let n=$1 12 | else 13 | usage >&2 14 | exit 2 15 | fi ;; 16 | *) 17 | usage >&2 18 | exit 2 19 | ;; 20 | esac 21 | 22 | function row() { 23 | local offset 24 | let offset=$((2 * $1)) 25 | local a=(${!2}) 26 | local i 27 | 28 | for i in 0 1; do 29 | echo ${a[i + offset]} 30 | done 31 | } 32 | 33 | function col() { 34 | local offset 35 | let offset=$1 36 | local a=(${!2}) 37 | local j 38 | 39 | for j in 0 2; do 40 | echo ${a[j + offset]} 41 | done 42 | } 43 | 44 | function dot() { 45 | local a=(${!1}) 46 | local b=(${!2}) 47 | 48 | let dot=0 49 | local i 50 | for i in 0 1; do 51 | let dot=$((dot + a[i] * b[i])) 52 | done 53 | echo $dot 54 | } 55 | 56 | function mult() { 57 | local a=(${!1}) 58 | local b=(${!2}) 59 | 60 | local i 61 | for i in 0 1; do 62 | local row=($(row $i a[@])) 63 | local j 64 | for j in 0 1; do 65 | local col=($(col $j b[@])) 66 | dot row[@] col[@] 67 | done 68 | done 69 | } 70 | 71 | function format() { 72 | local a=(${!1}) 73 | local f0 74 | printf -v f0 "%'d" ${a[1]} 75 | local len0=${#f0} 76 | local f1 77 | printf -v f1 "%'d" ${a[3]} 78 | local len1=${#f1} 79 | 80 | printf "[%'${len0}d %'${len1}d\n %'${len0}d %'${len1}d]\n" ${a[@]} 81 | } 82 | 83 | fib0=(0 1 1 1) 84 | if ((0 > n)); then 85 | let start=-1 86 | let step=-1 87 | m=(-1 1 1 0) 88 | else 89 | let start=1 90 | let step=1 91 | m=(${fib0[@]}) 92 | fi 93 | fibn=(${fib0[@]}) 94 | 95 | format fib0[@] 96 | for n in $(seq $start $step $1); do 97 | fibn=($(mult fibn[@] m[@])) 98 | format fibn[@] 99 | done 100 | -------------------------------------------------------------------------------- /git/f/git-clauses.sh: -------------------------------------------------------------------------------- 1 | # For GIVEN - cannot fail or error, so do not `_register` 2 | # Global (not local) so visible to later clauses 3 | 4 | readonly git_tdd=$PWD/git-tdd 5 | readonly git_hooks=$PWD/git-hooks 6 | 7 | function an-empty-repo-with-initial-empty-commit() { 8 | _tmpdir repodir 9 | git init --quiet $repodir >/dev/null || return $? 10 | (cd $repodir && 11 | git commit --quiet --allow-empty -m Initial >/dev/null) 12 | } 13 | _register an-empty-repo-with-initial-empty-commit 14 | 15 | function a-cloned-repo-with-commits() { 16 | _tmpdir upstream 17 | git init --bare --quiet $upstream >/dev/null || return $? 18 | _tmpdir repodir 19 | git clone --quiet $upstream $repodir >/dev/null 2>&1 || return $? 20 | (cd $repodir && 21 | git commit --quiet --allow-empty -m Initial >/dev/null && 22 | git push --quiet -u origin master >/dev/null && 23 | touch Bob && 24 | git add Bob && 25 | git commit --quiet -a -m 'First Bob' && 26 | git push --quiet -u origin master) >/dev/null 27 | } 28 | _register a-cloned-repo-with-commits 29 | 30 | function a-local-repo-with-commits() { 31 | _tmpdir repodir 32 | git init --quiet $repodir >/dev/null || return $? 33 | (cd $repodir && 34 | git commit --quiet --allow-empty -m Initial >/dev/null && 35 | touch Bob && 36 | git add Bob && 37 | git commit --quiet -a -m 'First Bob') >/dev/null 38 | } 39 | _register a-local-repo-with-commits 40 | 41 | function the-repo-is-clean() ( 42 | cd $repodir && 43 | git diff-index --quiet HEAD 44 | ) 45 | _register the-repo-is-clean 46 | 47 | # TODO: This assumes a remote upstream, not valid locally 48 | # TODO: Overcomplex, pull out 2nd test 49 | function changes-committed() { 50 | [[ -z "$(cd $repodir && git status --porcelain)" ]] && 51 | ((1 == $(cd $repodir && 52 | git log --format=%H origin/master..HEAD -- | wc -l))) 53 | } 54 | _register changes-committed 55 | 56 | function pushed-with() { 57 | local message="$1" 58 | [[ "$message" == "$(cd $repodir && git log --format=%s HEAD^^..HEAD^ --)" ]] 59 | } 60 | _register pushed-with 1 61 | -------------------------------------------------------------------------------- /git/t/git-tdd-test.sh: -------------------------------------------------------------------------------- 1 | SCENARIO 'TDD first test' \ 2 | GIVEN a-cloned-repo-with-commits \ 3 | WHEN tdd-init \ 4 | AND a-change-to-existing \ 5 | AND tdd-test \ 6 | THEN happy-path \ 7 | AND runs-test-command \ 8 | AND changes-committed \ 9 | AND work-in-progress 1 10 | 11 | SCENARIO 'TDD two tests' \ 12 | GIVEN a-cloned-repo-with-commits \ 13 | WHEN tdd-init \ 14 | AND a-change-to-existing \ 15 | AND tdd-test \ 16 | AND this-change Fred \ 17 | AND this-change-added Fred \ 18 | AND tdd-test \ 19 | THEN happy-path \ 20 | AND runs-test-command \ 21 | AND changes-committed \ 22 | AND work-in-progress 2 23 | 24 | SCENARIO 'TDD test without pull' \ 25 | GIVEN a-cloned-repo-with-commits \ 26 | WHEN tdd-init \ 27 | AND a-change-to-existing \ 28 | AND pull-before-test-disabled \ 29 | AND tdd-test \ 30 | THEN happy-path \ 31 | AND changes-persist \ 32 | AND work-in-progress 1 33 | 34 | SCENARIO 'TDD test without pull on new change' \ 35 | GIVEN a-cloned-repo-with-commits \ 36 | WHEN tdd-init \ 37 | AND pull-before-test-disabled \ 38 | AND this-change Fred \ 39 | AND this-change-added Fred \ 40 | AND tdd-test \ 41 | THEN happy-path \ 42 | AND this-change-persists Fred \ 43 | AND work-in-progress 1 44 | 45 | SCENARIO 'TDD test without remote should not pull' \ 46 | GIVEN a-local-repo-with-commits \ 47 | WHEN tdd-init \ 48 | AND this-change Fred \ 49 | AND this-change-added Fred \ 50 | AND tdd-test \ 51 | THEN happy-path \ 52 | AND this-change-persists Fred \ 53 | AND work-in-progress 1 54 | 55 | SCENARIO 'TDD test with no changes' \ 56 | GIVEN a-cloned-repo-with-commits \ 57 | WHEN tdd-init \ 58 | AND tdd-test \ 59 | THEN happy-path \ 60 | AND work-in-progress 0 61 | 62 | SCENARIO 'TDD test fails should revert' \ 63 | GIVEN a-cloned-repo-with-commits \ 64 | WHEN tdd-init \ 65 | AND revert-on-fail-enabled \ 66 | AND a-change-to-existing \ 67 | AND failing-tests \ 68 | AND tdd-test \ 69 | THEN sad-path \ 70 | AND the-repo-is-clean 71 | -------------------------------------------------------------------------------- /git/cygwin-pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Helper for Windows/Cygwin to keep UNIX-style text files. 4 | 5 | set -e 6 | set -o pipefail 7 | 8 | # Inside a commit, silent; from command line, use a flag to silence 9 | if [[ -n "$GIT_AUTHOR_NAME" ]]; then 10 | silent=true 11 | else 12 | silent=false 13 | fi 14 | 15 | function echo() { 16 | $silent || builtin echo "$@" 17 | } 18 | 19 | function show-help() { 20 | echo "Usage: ${0##*/} [-h|--help][-s|--silent] [FILE]..." 21 | } 22 | 23 | function unchanged() { 24 | git diff-index --quiet HEAD -- "$1" 25 | } 26 | 27 | function prep() { 28 | local bit='' 29 | local file="$(file "$1")" 30 | if [[ $file =~ 'directory' ]]; then 31 | bit='/' 32 | elif [[ $file =~ 'symbolic link' ]]; then 33 | bit='@' 34 | elif [[ $file =~ executable || $file =~ 'batch file' ]]; then 35 | bit='*' 36 | chmod a+x "$1" 37 | git update-index --chmod=+x "$1" 38 | else 39 | chmod a-x "$1" 40 | git update-index --chmod=-x "$1" 41 | fi 42 | if [[ $file =~ text ]]; then 43 | if [[ $file =~ DOS ]]; then 44 | unix2dos -q "$1" 45 | git add "$1" 46 | else 47 | dos2unix -q "$1" 48 | git add "$1" 49 | fi 50 | else 51 | bit='^' 52 | fi 53 | unchanged "$1" && bit="$bit#" 54 | echo "$1$bit" 55 | } 56 | 57 | silent_opt=$silent 58 | while getopts :-: opt; do 59 | [[ - == $opt ]] && opt=${OPTARG%%=*} OPTARG=${OPTARG#*-} 60 | case "$opt" in 61 | h | help) 62 | show-help 63 | exit 0 64 | ;; 65 | s | silent) silent_opt=true ;; 66 | *) 67 | show-help >&2 68 | exit 1 69 | ;; 70 | esac 71 | done 72 | shift $((OPTIND - 1)) 73 | silent=$silent_opt 74 | 75 | case $# in 76 | 0) set - . ;; 77 | esac 78 | 79 | # Git is happier working from the root 80 | cd $(git rev-parse --show-toplevel) 81 | 82 | git status -z "$@" | tr '\0' '\n' | while read -r S f; do 83 | case $S in 84 | '??') echo "# Unknown: $f" ;; 85 | C) 86 | echo "$f" 87 | read 88 | ;; # Skip next line 89 | D) echo "$f" ;; 90 | R*) 91 | read -r g 92 | prep "$f" 93 | ;; 94 | *) prep "$f" ;; 95 | esac 96 | done 97 | -------------------------------------------------------------------------------- /gee/README.md: -------------------------------------------------------------------------------- 1 | # gee - Git and Tee mashup, or committing `` 2 | 3 | This automates a common command-line pattern: 4 | 5 | ``` 6 | $ some command and args | tee some.out 7 | ``` 8 | 9 | Commits `some.out` to a hidden git repo so you can diff against previous 10 | program runs, etc. 11 | 12 | ## Example and motivation 13 | 14 | A challenging example: 15 | 16 | ``` 17 | $ gee -o verify.out mvn clean verify 18 | # Edit, edit, edit 19 | $ gee -o verify.out mvn clean verify 20 | # See how the build changed 21 | $ gee -g git diff HEAD^ verify.out 22 | ``` 23 | 24 | Note that in a case like maven, I need to do some additional filtering or 25 | [change the output format](https://maven.apache.org/maven-logging.html) to 26 | remove timestamps from the output and avoid spurious differences. 27 | 28 | ## Usage and help 29 | 30 | ``` 31 | Usage: gee [-F FILE|-hl|-m MESSAGE|-o FILE][-u] [PROGRAM ...] 32 | Usage: gee [-h] -g PROGRAM [...] 33 | Commit standard input or PROGRAM output, and also copy to standard output. 34 | 35 | With no PROGRAM, read standard input and name default out file 'gee.out', 36 | otherwise name default out file 'PROGRAM.out'. 37 | 38 | With '-l', commit output to local git, not '.gee' directory. 39 | 40 | With '-g', run PROGRAM in the '.gee' repository and do not commit output. 41 | 42 | -h, --help print help and exit normally 43 | -l, --local use local .git, not .gee 44 | -F, --file FILE take the commit message from FILE 45 | -g, --in-repository run PROGRAM in .gee 46 | -m, --message MESSAGE commit using MESSAGE. Multiple -m options are 47 | catenated as separate paragraphs 48 | -o, --out-file FILE tee output to FILE relative to .gee 49 | -u, --log-unchanged update commit even when there are no changes 50 | 51 | Examples: 52 | make test | gee -o test.out -m 'make test' 53 | gee make test 54 | gee -g git diff HEAD^ 55 | ``` 56 | 57 | ## Tests 58 | 59 | Run `make`. 60 | 61 | This runs `test-gee -i -- -c t` Interestingly, this is not a 62 | test script! It's a copy of 63 | [run-from-url](https://github.com/binkley/shell/blob/master/run-from-url) 64 | which runs a cached copy of 65 | [run-tests](https://github.com/binkley/shell/blob/master/plain-bash-testing/run-tests). 66 | This keeps the test script always up to date. 67 | -------------------------------------------------------------------------------- /maven-bash-testing/src/main/java/hm/binkley/Main.java: -------------------------------------------------------------------------------- 1 | package hm.binkley; 2 | 3 | import joptsimple.OptionParser; 4 | import joptsimple.OptionSet; 5 | import joptsimple.OptionSpecBuilder; 6 | 7 | import java.io.PrintStream; 8 | 9 | import static java.lang.String.format; 10 | import static java.lang.System.err; 11 | import static java.lang.System.exit; 12 | 13 | public final class Main { 14 | private static void printUsage(final String program, 15 | final PrintStream printer) { 16 | printer.println( 17 | format("Usage: %s [--health][--resume] job-name [args]", 18 | program)); 19 | } 20 | 21 | public static void main(final String... args) { 22 | final OptionParser parser = new OptionParser(); 23 | final OptionSpecBuilder healthFlag = parser.accepts("health"); 24 | final OptionSpecBuilder resumeFlag = parser.accepts("resume"); 25 | 26 | final OptionSet options = parser.parse(args); 27 | final String[] realArgs = options.nonOptionArguments(). 28 | toArray(new String[0]); 29 | 30 | final String jobName; 31 | switch (realArgs.length) { 32 | case 1: 33 | jobName = realArgs[0]; 34 | break; 35 | default: 36 | printUsage("test-program", err); 37 | exit(2); 38 | return; 39 | } 40 | 41 | switch (jobName) { 42 | case "healthy-job": 43 | case "unhealthy-job": 44 | case "resumable-job": 45 | case "unresumable-job": 46 | case "takes-arg-job": 47 | break; 48 | default: 49 | err.println(format("test-program: Not a job: %s", jobName)); 50 | exit(2); 51 | } 52 | 53 | if (options.has(healthFlag)) { 54 | switch (jobName) { 55 | case "unhealthy-job": 56 | err.println(format("%s: I am sad", jobName)); 57 | exit(1); 58 | default: 59 | exit(0); 60 | } 61 | } 62 | 63 | if (options.has(resumeFlag)) { 64 | switch (jobName) { 65 | case "unresumable-job": 66 | err.println(format("%s: Resume not supported", jobName)); 67 | exit(2); 68 | default: 69 | exit(0); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /git/git-hooks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export PS4='+${BASH_SOURCE}:${LINENO}: ${FUNCNAME[0]:+${FUNCNAME[0]}(): } ' 4 | set -e 5 | set -o pipefail 6 | 7 | unset CDPATH # Trouble-maker 8 | 9 | progname=${0##*/} 10 | trap 'r=$? ; (( 0 == $r )) || echo "$progname: Failed" ; exit $r' EXIT 11 | cd $(git rev-parse --show-toplevel)/.git/hooks 12 | 13 | function usage() { 14 | echo "Usage: $progname " 15 | } 16 | 17 | function help() { 18 | usage 19 | } 20 | 21 | function required() { 22 | cat >&2 <\` to set $1 for this repository. 25 | EOM 26 | exit 2 27 | } 28 | 29 | function config() { 30 | local opts=(--get) 31 | while ((0 < $#)); do 32 | case $1 in 33 | --bool) opts=("${opts[@]}" $1) ;; 34 | -*) 35 | echo "$progname: BUG" >&2 36 | exit 3 37 | ;; 38 | *) break ;; 39 | esac 40 | shift 41 | done 42 | local config=hooks.$1 43 | git config "${opts[@]}" $config 2>/dev/null || echo "${2-required $config}" 44 | } 45 | 46 | function init() { 47 | while ((0 < $#)); do 48 | case $1 in 49 | -*) 50 | echo "$progname: BUG" >&2 51 | exit 3 52 | ;; 53 | *) break ;; 54 | esac 55 | shift 56 | done 57 | cat >hooks <<'EOS' 58 | #!/bin/bash 59 | hook=${0##*/} 60 | hookd=$0.d 61 | [[ -d $hookd ]] || exit 0 62 | for h in $hookd/* 63 | do 64 | [[ -x $h ]] || continue 65 | $h "$@" || exit $? 66 | done 67 | EOS 68 | chmod a+rx hooks 69 | for h in *.sample; do 70 | mkdir ${h%.sample}.d 71 | ln -s hooks ${h%.sample} 72 | done 73 | } 74 | 75 | function list() { 76 | while ((0 < $#)); do 77 | case $1 in 78 | -*) 79 | usage >&2 80 | exit 2 81 | ;; 82 | *) break ;; 83 | esac 84 | shift 85 | done 86 | case $# in 87 | 1) local hook=$1 ;; 88 | *) 89 | usage >&2 90 | exit 2 91 | ;; 92 | esac 93 | cd $hook.d 94 | exec ls 95 | } 96 | 97 | while ((0 < $#)); do 98 | case $1 in 99 | --help) exec man ${0##*/} ;; 100 | -*) 101 | usage >&2 102 | exit 2 103 | ;; 104 | *) break ;; 105 | esac 106 | done 107 | 108 | case $# in 109 | 0) 110 | usage >&2 111 | exit 2 112 | ;; 113 | esac 114 | 115 | case $1 in 116 | init | list) "$@" ;; 117 | *) 118 | usage >&2 119 | exit 2 120 | ;; 121 | esac 122 | -------------------------------------------------------------------------------- /maven-bash-testing/src/test/resources/t/03-help-tests.sh: -------------------------------------------------------------------------------- 1 | # Source me - do not execute 2 | 3 | scenario "Short option help" \ 4 | given_jar a.jar with_jobs 'simple-job' \ 5 | also_jar b.jar with_jobs 'trivial-job' \ 6 | when_run '-h' \ 7 | then_exit 0 \ 8 | with_out <<'EOO' 9 | Usage: ./run-java.sh [-J-jvm_flag ...][-d|--debug][-h|--help][--health][-j|--jobs][-n|--dry-run][--resume][-v|--verbose] [--] [-job_flag ...] [job_arg ...] 10 | 11 | Flags: 12 | -J-* JVM flags prefixed with J 13 | -d, --debug Print debug output while running 14 | -h, --help Print help and exit normally 15 | --health Check job health and exit 16 | -j, --jobs List jobs and exit normally 17 | -n, --dry-run Do nothing (dry run); echo actions 18 | --resume Resume previously failed job 19 | -v, --verbose Verbose output 20 | 21 | Jobs: 22 | * a.jar: 23 | - simple-job 24 | * b.jar: 25 | - trivial-job 26 | EOO 27 | 28 | scenario "Long option help" \ 29 | given_jar a.jar with_jobs 'simple-job' \ 30 | also_jar b.jar with_jobs 'trivial-job' \ 31 | when_run '--help' \ 32 | then_exit 0 \ 33 | with_out <<'EOO' 34 | Usage: ./run-java.sh [-J-jvm_flag ...][-d|--debug][-h|--help][--health][-j|--jobs][-n|--dry-run][--resume][-v|--verbose] [--] [-job_flag ...] [job_arg ...] 35 | 36 | Flags: 37 | -J-* JVM flags prefixed with J 38 | -d, --debug Print debug output while running 39 | -h, --help Print help and exit normally 40 | --health Check job health and exit 41 | -j, --jobs List jobs and exit normally 42 | -n, --dry-run Do nothing (dry run); echo actions 43 | --resume Resume previously failed job 44 | -v, --verbose Verbose output 45 | 46 | Jobs: 47 | * a.jar: 48 | - simple-job 49 | * b.jar: 50 | - trivial-job 51 | EOO 52 | 53 | scenario "Complext jobs help" \ 54 | given_jar a.jar with_jobs 'complex-job -Dfile=$1 $2' \ 55 | when_run '--help' \ 56 | then_exit 0 \ 57 | with_out <<'EOO' 58 | Usage: ./run-java.sh [-J-jvm_flag ...][-d|--debug][-h|--help][--health][-j|--jobs][-n|--dry-run][--resume][-v|--verbose] [--] [-job_flag ...] [job_arg ...] 59 | 60 | Flags: 61 | -J-* JVM flags prefixed with J 62 | -d, --debug Print debug output while running 63 | -h, --help Print help and exit normally 64 | --health Check job health and exit 65 | -j, --jobs List jobs and exit normally 66 | -n, --dry-run Do nothing (dry run); echo actions 67 | --resume Resume previously failed job 68 | -v, --verbose Verbose output 69 | 70 | Jobs: 71 | * a.jar: 72 | - complex-job $2 73 | EOO 74 | -------------------------------------------------------------------------------- /maven-bash-testing/src/test/resources/test-run-java.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function setup_colors() 4 | { 5 | [[ -t 1 ]] || return 6 | local -r ncolors=$(tput colors) 7 | [[ -n "$ncolors" && $ncolors -ge 8 ]] || return 8 | pblack=$(tput setaf 0) 9 | pred=$(tput setaf 1) 10 | pgreen=$(tput setaf 2) 11 | pyellow=$(tput setaf 3) 12 | pblue=$(tput setaf 4) 13 | pmagenta=$(tput setaf 5) 14 | pcyan=$(tput setaf 6) 15 | pwhite=$(tput setaf 7) 16 | prev=$(tput rev) 17 | pbold=$(tput bold) 18 | preset=$(tput sgr0) 19 | } 20 | 21 | function setup_diff() 22 | { 23 | shopt -s expand_aliases 24 | if $color 25 | then 26 | alias diff='git diff --color-words --word-diff=plain' 27 | else 28 | alias diff='git diff --no-color --word-diff=plain' 29 | fi 30 | } 31 | 32 | function print_usage() 33 | { 34 | cat < [test_scripts] 36 | EOU 37 | } 38 | 39 | function enable_debug() 40 | { 41 | export debug=true 42 | export PS4='+${BASH_SOURCE}:${LINENO}:${FUNCNAME[0]:+${FUNCNAME[0]}():} ' 43 | set -o pipefail 44 | set -o xtrace 45 | echo "$0: Called with $@" 46 | } 47 | 48 | color=false 49 | quiet=false 50 | while getopts :cdq-: opt 51 | do 52 | [[ - == $opt ]] && opt=${OPTARG%%=*} OPTARG=${OPTARG#*=} 53 | case $opt in 54 | c | color ) color=true ; setup_colors ;; 55 | d | debug ) enable_debug "$@" ;; 56 | q | quiet ) quiet=true ;; 57 | * ) print_usage >&2 ; exit 3 ;; 58 | esac 59 | done 60 | shift $((OPTIND - 1)) 61 | 62 | case $# in 63 | 0 ) print_usage >&2 ; exit 3 ;; 64 | * ) script=$1 ; shift ;; 65 | esac 66 | 67 | setup_diff 68 | 69 | rootdir=$(dirname $0) 70 | 71 | . $rootdir/test-functions.sh 72 | 73 | for t in "$@" 74 | do 75 | if [[ -d "$t" ]] 76 | then 77 | set -- $t/*.sh 78 | else 79 | set -- $t 80 | fi 81 | done 82 | 83 | let passed=0 failed=0 errored=0 84 | for t in "$@" 85 | do 86 | if ! $quiet 87 | then 88 | echo "${pbold}Script${preset} ${t##*/}:" 89 | fi 90 | . $t 91 | done 92 | 93 | if ! $quiet 94 | then 95 | (( 0 == passed )) && ppassed= || ppassed=${pgreen} 96 | (( 0 == failed )) && pfailed= || pfailed=${pred} 97 | (( 0 == errored )) && perrored= || perrored=${pred} 98 | cat <&2 ;; 7 | ${0##*/}: WARNING: BASH $BASH_VERSION does not display UNICODE characters 8 | EOW 9 | esac 10 | 11 | url=http://www.unicode.org/Public/UNIDATA/UnicodeData.txt 12 | unidata=UnicodeData.txt 13 | 14 | function print_help { 15 | local -r width=$(( $(tput cols) - 2 )) 16 | cat <&2 ; exit 2 41 | $program: $file: No such file or directory 42 | EOE 43 | fi 44 | 45 | if [[ ! -r $file ]] 46 | then 47 | cat <&2 ; exit 2 48 | $program: $file: Permission denied 49 | EOE 50 | fi 51 | 52 | if [[ -d $file ]] 53 | then 54 | cat <&2 ; exit 2 55 | $program: $file: Is a directory 56 | EOE 57 | fi 58 | } 59 | 60 | 61 | offline=false 62 | while getopts :U:hou:v-: opt 63 | do 64 | [[ - == $opt ]] && opt=${OPTARG%%=*} OPTARG=${OPTARG%*=} 65 | case $opt in 66 | U | unidata-url ) url=$OPTARG ;; 67 | h | help ) print_help ; exit 0 ;; 68 | o | offline ) offline=true ;; 69 | u | unidata ) unidata=$OPTARG ;; 70 | v | verbose ) verbose=true ;; 71 | : ) cat <&2 ; exit 2 ;; 72 | ${0##*/}: option requires an argument -- '$OPTARG' 73 | Try '${0##*/} --help' for more information. 74 | EOE 75 | ? ) cat <&2 ; exit 2 ;; 76 | ${0##*/}: invalid option -- '$OPTARG' 77 | Try '${0##*/} --help' for more information. 78 | EOE 79 | esac 80 | done 81 | shift $((OPTIND - 1)) 82 | 83 | case $# in 84 | 0 ) cat <&2 ; exit 2 ;; 85 | ${0##*/}: missing operand 86 | Try '${0##*/} --help' for more information. 87 | EOE 88 | esac 89 | 90 | trap 'rm -f $tmp' EXIT 91 | readonly tmp=$(mktemp 2>/dev/null || mktemp -t ${0##*/}) 92 | 93 | # Check lastest UNICODE data 94 | if $offline 95 | then 96 | : 97 | elif curl --fail -L -z $unidata -O -R $url >/dev/null 2>$tmp 98 | then 99 | : 100 | else 101 | readonly exit_code=$? 102 | cat <&2 15 | exit 3 16 | } 17 | 18 | function _print_result { 19 | local -r exit_code=$1 20 | local stack=($(caller 1)) 21 | local -r previous=${stack[1]} 22 | 23 | if (( 0 == exit_code )) 24 | then 25 | if ! $quiet 26 | then 27 | echo -e "$pgreen$pcheckmark$preset $description" 28 | fi 29 | elif (( 1 == exit_code )) 30 | then 31 | : ${expected:=} ${actual:=} 32 | echo "$expected}" >$tmpdir/expected 33 | (( 1 < $(wc -l $tmpdir/expected | cut -d' ' -f1))) \ 34 | && local -r expected_sep=$'\n' || local -r expected_sep=' ' 35 | echo "$actual}" >$tmpdir/actual 36 | (( 1 < $(wc -l $tmpdir/actual | cut -d' ' -f1))) \ 37 | && local -r actual_sep=$'\n' || local -r actual_sep=' ' 38 | $color && local -r color_flag=always || color_flag=never 39 | cat </dev/null' RETURN 88 | local -r old_pwd=$PWD 89 | cd $tmpdir >/dev/null 90 | echo ">> Dropping into shell (exited $exit_code): $scenario" 91 | $SHELL -i 92 | fi 93 | } 94 | 95 | function SCENARIO { 96 | trap 'rm -rf $tmpdir' RETURN 97 | local -r tmpdir=$(mktemp -d 2>/dev/null || mktemp -d -t ${0##*/}) 98 | local -r stdout=$tmpdir/stdout 99 | touch $stdout 100 | local -r stderr=$tmpdir/stderr 101 | touch $stderr 102 | 103 | local scenario=${FUNCNAME} 104 | for arg 105 | do 106 | case $arg in 107 | *\'* ) scenario="$scenario \"$arg\"" ;; 108 | *' '* | *\"* ) scenario="$scenario '$arg'" ;; 109 | * ) scenario="$scenario $arg" ;; 110 | esac 111 | done 112 | readonly scenario 113 | 114 | local -r description="$1" 115 | shift 116 | 117 | local __tallied=false 118 | case $1 in 119 | GIVEN ) 120 | pushd $PWD >/dev/null 121 | "$@" 122 | __tally $? 123 | popd >/dev/null ;; 124 | * ) _bad_syntax after GIVEN ;; 125 | esac 126 | 127 | local exit_code=$? 128 | 129 | # (( 0 != exit_code )) && _maybe_debug_if_not_passed 130 | 131 | return $exit_code 132 | } 133 | -------------------------------------------------------------------------------- /color/run-color: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Suppress various Shellcheck warnings. Shellcheck is recommended, and 4 | # IntelliJ will download it for you. See https://www.shellcheck.net/ 5 | # shellcheck disable=SC2059,SC2209,SC2214,SC2215 6 | 7 | # Better debugging output when using `bash -x