├── .travis.yml
├── Changes
├── License
├── Makefile
├── Meta
├── ReadMe.pod
├── bin
└── bash+
├── doc
└── bash+.swim
├── lib
└── bash+.bash
├── man
├── man1
│ └── bash+.1
└── man3
│ └── bash+.3
└── test
├── base.t
├── die.t
├── fcopy.t
├── lib
└── foo
│ ├── bar.bash
│ └── foo.bash
├── setup
├── shellcheck.t
├── source-bash+-std.t
├── source-bash+.t
├── use.t
└── version-check.t
/.travis.yml:
--------------------------------------------------------------------------------
1 | # C language gives closest shell env.
2 | language: c
3 |
4 | script:
5 | - git submodule update --init --recursive
6 | - PROVEOPT=-v make test
7 |
--------------------------------------------------------------------------------
/Changes:
--------------------------------------------------------------------------------
1 | ---
2 | version: 0.1.0
3 | date: Sat 14 Nov 2020 10:14:14 AM EST
4 | changes:
5 | - Add tests for version-check
6 | - Improve version-check
7 | - Move PATH assignment into test/setup
8 | - Meta bashplus supports Bash 3.2
9 | ---
10 | version: 0.0.9
11 | date: Wed 11 Nov 2020 02:19:32 PM EST
12 | changes:
13 | - Apply shellcheck fixes
14 | - Modernize bash code
15 | ---
16 | version: 0.0.8
17 | date: Fri Aug 21 08:00:45 PDT 2020
18 | changes:
19 | - Support paths with spaces @admorgan++
20 | ---
21 | version: 0.0.7
22 | date: Sat Jan 23 16:28:59 PST 2016
23 | changes:
24 | - Update tooling, and copyright
25 | ---
26 | version: 0.0.6
27 | date: Fri Jan 23 21:05:15 PST 2015
28 | changes:
29 | - Update tooling, and copyright
30 | ---
31 | version: 0.0.1
32 | date: Sun Oct 27 19:07:51 PDT 2013
33 | changes:
34 | - First release.
35 |
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | (The MIT License)
2 |
3 | Copyright © 2013-2020 Ingy döt Net
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the ‘Software’), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | ifeq ($(MAKECMDGOALS),install)
2 | ifeq "$(shell bpan version 2>/dev/null)" ""
3 | $(error 'BPAN not installed. See http://bpan.org')
4 | endif
5 | endif
6 |
7 | NAME := bash+
8 | LIB := lib/$(NAME).bash
9 | DOC := doc/$(NAME).swim
10 | MAN1 := man/man1
11 | MAN3 := man/man3
12 |
13 | INSTALL_LIB ?= $(shell bpan env BPAN_LIB)
14 | INSTALL_DIR ?= test
15 | INSTALL_MAN1 ?= $(shell bpan env BPAN_MAN1)
16 | INSTALL_MAN3 ?= $(shell bpan env BPAN_MAN3)
17 |
18 | DOCKER_IMAGE := ingy/bash-testing:0.0.1
19 | DOCKER_TESTS := 5.1 5.0 4.4 4.3 4.2 4.1 4.0 3.2
20 | DOCKER_TESTS := $(DOCKER_TESTS:%=docker-test-%)
21 |
22 | default: help
23 |
24 | help:
25 | @echo 'Rules: test, install, doc'
26 |
27 | .PHONY: test
28 | test:
29 | prove $(PROVEOPT:%=% )test/
30 |
31 | test-all: test docker-test
32 |
33 | docker-test: $(DOCKER_TESTS)
34 |
35 | $(DOCKER_TESTS):
36 | $(call docker-make-test,$(@:docker-test-%=%))
37 |
38 | install:
39 | install -C -d -m 0755 $(INSTALL_LIB)/$(INSTALL_DIR)/
40 | install -C -m 0755 $(LIB) $(INSTALL_LIB)/$(INSTALL_DIR)/
41 | install -C -d -m 0755 $(INSTALL_MAN1)/
42 | install -C -d -m 0755 $(INSTALL_MAN3)/
43 | install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/
44 | install -C -m 0644 $(MAN3)/$(NAME).3 $(INSTALL_MAN3)/
45 |
46 | .PHONY: doc
47 | doc: ReadMe.pod $(MAN1)/$(NAME).1 $(MAN3)/$(NAME).3
48 |
49 | ReadMe.pod: $(DOC)
50 | swim --to=pod --complete --wrap $< > $@
51 |
52 | $(MAN1)/%.1: doc/%.swim
53 | swim --to=man $< > $@
54 |
55 | $(MAN3)/%.3: doc/%.swim
56 | swim --to=man $< > $@
57 |
58 | define docker-make-test
59 | docker run -i -t --rm \
60 | -v $(PWD):/git-subrepo \
61 | -w /git-subrepo \
62 | $(DOCKER_IMAGE) \
63 | /bin/bash -c ' \
64 | set -x && \
65 | [[ -d /bash-$(1) ]] && \
66 | export PATH=/bash-$(1)/bin:$$PATH && \
67 | bash --version && \
68 | make test \
69 | '
70 | endef
71 |
--------------------------------------------------------------------------------
/Meta:
--------------------------------------------------------------------------------
1 | =meta: 0.0.2
2 |
3 | name: bashplus
4 | version: 0.1.0
5 | abstract: Modern Bash Programming
6 | homepage: https://github.com/ingydotnet/bashplus
7 |
8 | license: MIT
9 | copyright: 2013-2020
10 | author:
11 | name: Ingy döt Net
12 | email: ingy@ingy.net
13 | github: ingydotnet
14 | twitter: ingydotnet
15 | freenode: ingy
16 | homepage: http://ingy.net
17 |
18 | requires:
19 | bash: 3.2
20 | test:
21 | cmd: make test
22 | install:
23 | cmd: make install
24 |
25 | devel:
26 | git: git@github.org/ingydotnet/bashplus
27 | irc: irc.freenode.net/bpan
28 | bug: https://github.com/ingydotnet/bashplus/issues/
29 |
--------------------------------------------------------------------------------
/ReadMe.pod:
--------------------------------------------------------------------------------
1 | =pod
2 |
3 | =for comment
4 | DO NOT EDIT. This Pod was generated by Swim v0.1.48.
5 | See http://github.com/ingydotnet/swim-pm#readme
6 |
7 | =encoding utf8
8 |
9 | =head1 Name
10 |
11 | Bash+(1) - Modern Bash Programming
12 |
13 | =for html
14 |
15 |
16 | =head1 Synopsis
17 |
18 | source bash+ :std :array
19 |
20 | use Foo::Bar this that
21 |
22 | Array.new args "$@"
23 |
24 | if args.empty?; then
25 | die "I need args!"
26 | fi
27 |
28 | Foo::Bar.new foo args
29 |
30 | this is awesome # <= this is a real command! (You just imported it)
31 |
32 | =head1 Description
33 |
34 | Bash+ is just Bash... B some libraries that can make Bash programming a
35 | lot nicer.
36 |
37 | =for comment # Installation
38 |
39 | Get the source code from GitHub:
40 |
41 | git clone git@github.com:ingydotnet/bashplus
42 |
43 | Then run:
44 |
45 | make test
46 | make install # Possibly with 'sudo'
47 |
48 | =head1 Usage
49 |
50 | For now look at some libraries the use Bash+:
51 |
52 | =over
53 |
54 | =item * L
55 |
56 | =item * L
57 |
58 | =item * L
59 |
60 | =back
61 |
62 | =head1 Status
63 |
64 | If you are interested in chatting about this, C on
65 | irc.freenode.net.
66 |
67 | =head1 Author
68 |
69 | Written by Ingy döt Net
70 |
71 | =head1 Copyright & License
72 |
73 | Copyright 2013-2020. Ingy döt Net.
74 |
75 | The MIT License (MIT).
76 |
77 | =cut
78 |
--------------------------------------------------------------------------------
/bin/bash+:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #------------------------------------------------------------------------------
3 | # Bash+ - Modern Bash Programming
4 | #
5 | # Copyright (c) 2013-2020 Ingy döt Net
6 | #------------------------------------------------------------------------------
7 |
8 | set -e
9 | shopt -s compat31 &>/dev/null || true
10 |
11 | #------------------------------------------------------------------------------
12 | # Determine how `bash+` was called, and do the right thing:
13 | #------------------------------------------------------------------------------
14 | if [[ ${BASH_SOURCE[0]} != "$0" ]]; then
15 | # 'bash+' is being sourced:
16 | [[ ${BASH_SOURCE[0]} =~ /bin/bash\\+$ ]] || {
17 | echo "Invalid Bash+ path '${BASH_SOURCE[0]}'" 2> /dev/null
18 | exit 1
19 | }
20 | source "${BASH_SOURCE[0]%/bin/*}"/lib/bash+.bash || return $?
21 | bash+:import "$@"
22 | return $?
23 |
24 | else
25 | if [[ $# -eq 1 ]] && [[ $1 == --version ]]; then
26 | echo 'bash+ version 0.0.9'
27 | else
28 | cat <<'...'
29 |
30 | Greetings modern Bash programmer. Welcome to Bash+!
31 |
32 | Bash+ is framework that makes Bash programming more like Ruby and Perl.
33 |
34 | See: https://github.com/bpan-org/bashplus
35 |
36 | If you got here trying to use bash+ in a program, you need to source it:
37 |
38 | source bash+
39 |
40 | Happy Bash Hacking!
41 |
42 | ...
43 | fi
44 | fi
45 |
--------------------------------------------------------------------------------
/doc/bash+.swim:
--------------------------------------------------------------------------------
1 | Bash+(1)
2 | ========
3 |
4 | Modern Bash Programming
5 |
6 |
7 |
8 | = Synopsis
9 |
10 | source bash+ :std :array
11 |
12 | use Foo::Bar this that
13 |
14 | Array.new args "$@"
15 |
16 | if args.empty?; then
17 | die "I need args!"
18 | fi
19 |
20 | Foo::Bar.new foo args
21 |
22 | this is awesome # <= this is a real command! (You just imported it)
23 |
24 | = Description
25 |
26 | Bash+ is just Bash... *plus* some libraries that can make Bash programming a
27 | lot nicer.
28 |
29 | ## Installation
30 |
31 | Get the source code from GitHub:
32 |
33 | git clone git@github.com:ingydotnet/bashplus
34 |
35 | Then run:
36 |
37 | make test
38 | make install # Possibly with 'sudo'
39 |
40 | = Usage
41 |
42 | For now look at some libraries the use Bash+:
43 |
44 | * https://github.com/ingydotnet/git-hub
45 | * https://github.com/ingydotnet/json-bash
46 | * https://github.com/ingydotnet/test-more-bash
47 |
48 | = Status
49 |
50 | If you are interested in chatting about this, `/join #bpan` on
51 | irc.freenode.net.
52 |
53 | = Author
54 |
55 | Written by Ingy döt Net
56 |
57 | = Copyright & License
58 |
59 | Copyright 2013-2020. Ingy döt Net.
60 |
61 | The MIT License (MIT).
62 |
--------------------------------------------------------------------------------
/lib/bash+.bash:
--------------------------------------------------------------------------------
1 | # bash+ - Modern Bash Programming
2 | #
3 | # Copyright (c) 2013-2020 Ingy döt Net
4 |
5 | set -e
6 |
7 | [[ ${BASHPLUS_VERSION-} ]] && return 0
8 |
9 | BASHPLUS_VERSION=0.1.0
10 |
11 | bash+:version-check() {
12 | local cmd want got out
13 |
14 | IFS=' ' read -r -a cmd <<< "${1:?}"
15 | IFS=. read -r -a want <<< "${2:?}"
16 | : "${want[0]:=0}"
17 | : "${want[1]:=0}"
18 | : "${want[2]:=0}"
19 |
20 | if [[ ${cmd[*]} == bash ]]; then
21 | got=("${BASH_VERSINFO[@]}")
22 | BASHPLUS_VERSION_CHECK=${BASH_VERSION-}
23 | else
24 | [[ ${#cmd[*]} -gt 1 ]] || cmd+=(--version)
25 | out=$("${cmd[@]}") ||
26 | { echo "Failed to run '${cmd[*]}'" >&2; exit 1; }
27 | [[ $out =~ ([0-9]+\.[0-9]+(\.[0-9]+)?) ]] ||
28 | { echo "Can't determine version number from '${cmd[*]}'" >&2; exit 1; }
29 | BASHPLUS_VERSION_CHECK=${BASH_REMATCH[1]}
30 | IFS=. read -r -a got <<< "$BASHPLUS_VERSION_CHECK"
31 | fi
32 | : "${got[2]:=0}"
33 |
34 | (( got[0] > want[0] || ((
35 | got[0] == want[0] && ((
36 | got[1] > want[1] || ((
37 | got[1] == want[1] && got[2] >= want[2]
38 | )) )) )) ))
39 | }
40 |
41 | bash+:version-check bash 3.2 ||
42 | { echo "The 'bashplus' library requires 'Bash 3.2+'." >&2; exit 1; }
43 |
44 | @() (echo "$@") # XXX do we want to keep this?
45 |
46 | bash+:export:std() {
47 | set -o pipefail
48 |
49 | if bash+:version-check bash 4.4; then
50 | set -o nounset
51 | shopt -s inherit_errexit
52 | fi
53 |
54 | echo use die warn
55 | }
56 |
57 | # Source a bash library call import on it:
58 | bash+:use() {
59 | local library_name=${1:?bash+:use requires library name}; shift
60 | local library_path=; library_path=$(bash+:findlib "$library_name") || true
61 | [[ $library_path ]] ||
62 | bash+:die "Can't find library '$library_name'." 1
63 |
64 | source "$library_path"
65 | if bash+:can "$library_name:import"; then
66 | "$library_name:import" "$@"
67 | else
68 | bash+:import "$@"
69 | fi
70 | }
71 |
72 | # Copy bash+: functions to unprefixed functions
73 | bash+:import() {
74 | local arg=
75 | for arg; do
76 | if [[ $arg =~ ^: ]]; then
77 | # Word splitting required here
78 | # shellcheck disable=2046
79 | bash+:import $(bash+:export"$arg")
80 | else
81 | bash+:fcopy "bash+:$arg" "$arg"
82 | fi
83 | done
84 | }
85 |
86 | # Function copy
87 | bash+:fcopy() {
88 | bash+:can "${1:?bash+:fcopy requires an input function name}" ||
89 | bash+:die "'$1' is not a function" 2
90 | local func
91 | func=$(type "$1" 3>/dev/null | tail -n+3)
92 | [[ ${3-} ]] && "$3"
93 | eval "${2:?bash+:fcopy requires an output function name}() $func"
94 | }
95 |
96 | # Find the path of a library
97 | bash+:findlib() {
98 | local library_name
99 | library_name=$(tr '[:upper:]' '[:lower:]' <<< "${1//:://}").bash
100 | local lib=${BASHPLUSLIB:-${BASHLIB:-$PATH}}
101 | library_name=${library_name//+/\\+}
102 | IFS=':' read -r -a libs <<< "$lib"
103 | find "${libs[@]}" -name "${library_name##*/}" 2>/dev/null |
104 | grep -E "$library_name\$" |
105 | head -n1
106 | }
107 |
108 | bash+:die() {
109 | local msg=${1:-Died}
110 | msg=${msg//\\n/$'\n'}
111 |
112 | printf "%s" "$msg" >&2
113 | if [[ $msg == *$'\n' ]]; then
114 | exit 1
115 | else
116 | printf "\n"
117 | fi
118 |
119 | local c
120 | IFS=' ' read -r -a c <<< "$(caller "${DIE_STACK_LEVEL:-${2:-0}}")"
121 | if (( ${#c[@]} == 2 )); then
122 | msg=" at line %d of %s"
123 | else
124 | msg=" at line %d in %s of %s"
125 | fi
126 |
127 | # shellcheck disable=2059
128 | printf "$msg\n" "${c[@]}" >&2
129 | exit 1
130 | }
131 |
132 | bash+:warn() {
133 | local msg=${1:-Warning}
134 | printf "%s" "${msg//\\n/$'\n'}\n" >&2
135 | }
136 |
137 | bash+:can() {
138 | [[ $(type -t "${1:?bash+:can requires a function name}") == function ]]
139 | }
140 |
--------------------------------------------------------------------------------
/man/man1/bash+.1:
--------------------------------------------------------------------------------
1 | .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
2 | .\"
3 | .\" Standard preamble:
4 | .\" ========================================================================
5 | .de Sp \" Vertical space (when we can't use .PP)
6 | .if t .sp .5v
7 | .if n .sp
8 | ..
9 | .de Vb \" Begin verbatim text
10 | .ft CW
11 | .nf
12 | .ne \\$1
13 | ..
14 | .de Ve \" End verbatim text
15 | .ft R
16 | .fi
17 | ..
18 | .\" Set up some character translations and predefined strings. \*(-- will
19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will
21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and
22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
23 | .\" nothing in troff, for use with C<>.
24 | .tr \(*W-
25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
26 | .ie n \{\
27 | . ds -- \(*W-
28 | . ds PI pi
29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
31 | . ds L" ""
32 | . ds R" ""
33 | . ds C` ""
34 | . ds C' ""
35 | 'br\}
36 | .el\{\
37 | . ds -- \|\(em\|
38 | . ds PI \(*p
39 | . ds L" ``
40 | . ds R" ''
41 | . ds C`
42 | . ds C'
43 | 'br\}
44 | .\"
45 | .\" Escape single quotes in literal strings from groff's Unicode transform.
46 | .ie \n(.g .ds Aq \(aq
47 | .el .ds Aq '
48 | .\"
49 | .\" If the F register is >0, we'll generate index entries on stderr for
50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
51 | .\" entries marked with X<> in POD. Of course, you'll have to process the
52 | .\" output yourself in some meaningful fashion.
53 | .\"
54 | .\" Avoid warning from groff about undefined register 'F'.
55 | .de IX
56 | ..
57 | .nr rF 0
58 | .if \n(.g .if rF .nr rF 1
59 | .if (\n(rF:(\n(.g==0)) \{\
60 | . if \nF \{\
61 | . de IX
62 | . tm Index:\\$1\t\\n%\t"\\$2"
63 | ..
64 | . if !\nF==2 \{\
65 | . nr % 0
66 | . nr F 2
67 | . \}
68 | . \}
69 | .\}
70 | .rr rF
71 | .\" ========================================================================
72 | .\"
73 | .IX Title "STDIN 1"
74 | .TH STDIN 1 "November 2020" "Generated by Swim v0.1.48" "Modern Bash Programming"
75 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes
76 | .\" way too many mistakes in technical documents.
77 | .if n .ad l
78 | .nh
79 | .SH "Name"
80 | .IX Header "Name"
81 | Bash+(1) \- Modern Bash Programming
82 | .SH "Synopsis"
83 | .IX Header "Synopsis"
84 | .Vb 1
85 | \& source bash+ :std :array
86 | \&
87 | \& use Foo::Bar this that
88 | \&
89 | \& Array.new args "$@"
90 | \&
91 | \& if args.empty?; then
92 | \& die "I need args!"
93 | \& fi
94 | \&
95 | \& Foo::Bar.new foo args
96 | \&
97 | \& this is awesome # <= this is a real command! (You just imported it)
98 | .Ve
99 | .SH "Description"
100 | .IX Header "Description"
101 | Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer.
102 | .PP
103 | Get the source code from GitHub:
104 | .PP
105 | .Vb 1
106 | \& git clone git@github.com:ingydotnet/bashplus
107 | .Ve
108 | .PP
109 | Then run:
110 | .PP
111 | .Vb 2
112 | \& make test
113 | \& make install # Possibly with \*(Aqsudo\*(Aq
114 | .Ve
115 | .SH "Usage"
116 | .IX Header "Usage"
117 | For now look at some libraries the use Bash+:
118 | .IP "\(bu" 4
119 |
120 | .IP "\(bu" 4
121 |
122 | .IP "\(bu" 4
123 |
124 | .SH "Status"
125 | .IX Header "Status"
126 | If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net.
127 | .SH "Author"
128 | .IX Header "Author"
129 | Written by Ingy döt Net
130 | .SH "Copyright & License"
131 | .IX Header "Copyright & License"
132 | Copyright 2013\-2020. Ingy döt Net.
133 | .PP
134 | The \s-1MIT\s0 License (\s-1MIT\s0).
135 |
--------------------------------------------------------------------------------
/man/man3/bash+.3:
--------------------------------------------------------------------------------
1 | .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
2 | .\"
3 | .\" Standard preamble:
4 | .\" ========================================================================
5 | .de Sp \" Vertical space (when we can't use .PP)
6 | .if t .sp .5v
7 | .if n .sp
8 | ..
9 | .de Vb \" Begin verbatim text
10 | .ft CW
11 | .nf
12 | .ne \\$1
13 | ..
14 | .de Ve \" End verbatim text
15 | .ft R
16 | .fi
17 | ..
18 | .\" Set up some character translations and predefined strings. \*(-- will
19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will
21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and
22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
23 | .\" nothing in troff, for use with C<>.
24 | .tr \(*W-
25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
26 | .ie n \{\
27 | . ds -- \(*W-
28 | . ds PI pi
29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
31 | . ds L" ""
32 | . ds R" ""
33 | . ds C` ""
34 | . ds C' ""
35 | 'br\}
36 | .el\{\
37 | . ds -- \|\(em\|
38 | . ds PI \(*p
39 | . ds L" ``
40 | . ds R" ''
41 | . ds C`
42 | . ds C'
43 | 'br\}
44 | .\"
45 | .\" Escape single quotes in literal strings from groff's Unicode transform.
46 | .ie \n(.g .ds Aq \(aq
47 | .el .ds Aq '
48 | .\"
49 | .\" If the F register is >0, we'll generate index entries on stderr for
50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
51 | .\" entries marked with X<> in POD. Of course, you'll have to process the
52 | .\" output yourself in some meaningful fashion.
53 | .\"
54 | .\" Avoid warning from groff about undefined register 'F'.
55 | .de IX
56 | ..
57 | .nr rF 0
58 | .if \n(.g .if rF .nr rF 1
59 | .if (\n(rF:(\n(.g==0)) \{\
60 | . if \nF \{\
61 | . de IX
62 | . tm Index:\\$1\t\\n%\t"\\$2"
63 | ..
64 | . if !\nF==2 \{\
65 | . nr % 0
66 | . nr F 2
67 | . \}
68 | . \}
69 | .\}
70 | .rr rF
71 | .\" ========================================================================
72 | .\"
73 | .IX Title "STDIN 1"
74 | .TH STDIN 1 "November 2020" "Generated by Swim v0.1.48" "Modern Bash Programming"
75 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes
76 | .\" way too many mistakes in technical documents.
77 | .if n .ad l
78 | .nh
79 | .SH "Name"
80 | .IX Header "Name"
81 | Bash+(1) \- Modern Bash Programming
82 | .SH "Synopsis"
83 | .IX Header "Synopsis"
84 | .Vb 1
85 | \& source bash+ :std :array
86 | \&
87 | \& use Foo::Bar this that
88 | \&
89 | \& Array.new args "$@"
90 | \&
91 | \& if args.empty?; then
92 | \& die "I need args!"
93 | \& fi
94 | \&
95 | \& Foo::Bar.new foo args
96 | \&
97 | \& this is awesome # <= this is a real command! (You just imported it)
98 | .Ve
99 | .SH "Description"
100 | .IX Header "Description"
101 | Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer.
102 | .PP
103 | Get the source code from GitHub:
104 | .PP
105 | .Vb 1
106 | \& git clone git@github.com:ingydotnet/bashplus
107 | .Ve
108 | .PP
109 | Then run:
110 | .PP
111 | .Vb 2
112 | \& make test
113 | \& make install # Possibly with \*(Aqsudo\*(Aq
114 | .Ve
115 | .SH "Usage"
116 | .IX Header "Usage"
117 | For now look at some libraries the use Bash+:
118 | .IP "\(bu" 4
119 |
120 | .IP "\(bu" 4
121 |
122 | .IP "\(bu" 4
123 |
124 | .SH "Status"
125 | .IX Header "Status"
126 | If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net.
127 | .SH "Author"
128 | .IX Header "Author"
129 | Written by Ingy döt Net
130 | .SH "Copyright & License"
131 | .IX Header "Copyright & License"
132 | Copyright 2013\-2020. Ingy döt Net.
133 | .PP
134 | The \s-1MIT\s0 License (\s-1MIT\s0).
135 |
--------------------------------------------------------------------------------
/test/base.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | source bash+ :std
6 |
7 | ok $? "'source bash+' works"
8 |
9 | is "$BASHPLUS_VERSION" '0.1.0' 'BASHPLUS_VERSION is 0.1.0'
10 |
11 | done_testing 2
12 |
--------------------------------------------------------------------------------
/test/die.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | source bash+ :std
6 |
7 | got=$(die "Nope" 2>&1) || true
8 | want="Nope
9 | at line 7 in main of test/die.t"
10 | is "$got" "$want" "die() msg ok"
11 |
12 | got=$(die "Nope\n" 2>&1) || true
13 | want="Nope"
14 | is "$got" "$want" "die() msg ok"
15 |
16 | done_testing 2
17 |
--------------------------------------------------------------------------------
/test/fcopy.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | source bash+
6 |
7 | foo() {
8 | echo O HAI
9 | }
10 |
11 | like "$(type bar 2>&1)" 'bar: not found' \
12 | 'bar is not yet a function'
13 |
14 | bash+:fcopy foo bar
15 |
16 | type -t bar &>/dev/null
17 | ok $? 'bar is now a function'
18 | is "$(type foo | tail -n+3)" "$(type bar | tail -n+3)" \
19 | 'Copy matches original'
20 |
21 | done_testing 3
22 |
--------------------------------------------------------------------------------
/test/lib/foo/bar.bash:
--------------------------------------------------------------------------------
1 | Foo__Bar_VERSION='1.2.3'
2 |
3 | Foo::Bar:baz() { :;}
4 |
--------------------------------------------------------------------------------
/test/lib/foo/foo.bash:
--------------------------------------------------------------------------------
1 | Foo::Foo:import() {
2 | echo $1---$2
3 | }
4 |
--------------------------------------------------------------------------------
/test/setup:
--------------------------------------------------------------------------------
1 | # shellcheck shell=bash
2 |
3 | #------------------------------------------------------------------------------
4 | # This is a tiny version of test-more-bash that I use here. test-more-bash uses
5 | # bash+, so I want to avoid the circular dependency. This little guy does
6 | # 80-90% what test-more-bash does, with minimal code. It's a good example of
7 | # how nice Bash can be.
8 | #------------------------------------------------------------------------------
9 |
10 | set -e -o pipefail
11 |
12 | PATH=$PWD/bin:$PATH
13 |
14 | run=0
15 |
16 | plan() {
17 | echo "1..$1"
18 | }
19 |
20 | pass() {
21 | (( ++run ))
22 | echo "ok $run${1:+ - $1}"
23 | }
24 |
25 | fail() {
26 | (( ++run ))
27 | echo "not ok $run${1:+ - $1}"
28 | }
29 |
30 | is() {
31 | if [[ $1 == "$2" ]]; then
32 | pass "$3"
33 | else
34 | fail "$3"
35 | diag "Got: $1"
36 | diag "Want: $2"
37 | fi
38 | }
39 |
40 | ok() {
41 | if (exit "${1:-$?}"); then
42 | pass "$2"
43 | else
44 | fail "$2"
45 | fi
46 | }
47 |
48 | like() {
49 | if [[ $1 =~ $2 ]]; then
50 | pass "$3"
51 | else
52 | fail "$3"
53 | diag "Got: $1"
54 | diag "Like: $2"
55 | fi
56 | }
57 |
58 | unlike() {
59 | if [[ ! $1 =~ $2 ]]; then
60 | pass "$3"
61 | else
62 | fail "$3"
63 | diag "Got: $1"
64 | diag "Dont: $2"
65 | fi
66 | }
67 |
68 | done_testing() {
69 | echo "1..${1:-$run}"
70 | }
71 |
72 | diag() {
73 | echo "# ${1//$'\n'/$'\n'# }" >&2
74 | }
75 |
76 | note() {
77 | echo "# ${1//$'\n'/$'\n'# }"
78 | }
79 |
80 | #! vim: ft=sh sw=2:
81 |
--------------------------------------------------------------------------------
/test/shellcheck.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | source bash+
6 |
7 | if ! command -v shellcheck >/dev/null; then
8 | plan skip_all "The 'shellcheck' utility is not installed"
9 | fi
10 | if [[ ! $(shellcheck --version) =~ 0\.7\.1 ]]; then
11 | plan skip_all "This test wants shellcheck version 0.7.1"
12 | fi
13 |
14 | IFS=$'\n' read -d '' -r -a shell_files <<< "$(
15 | find bin -type f
16 | find lib -type f
17 | echo test/setup
18 | find test -name '*.t'
19 | )" || true
20 |
21 | skips=(
22 | # We want to keep these 2 here always:
23 | SC1090 # Can't follow non-constant source. Use a directive to specify location.
24 | SC1091 # Not following: bash+ was not specified as input (see shellcheck -x).
25 | )
26 |
27 | skip=$(IFS=,; echo "${skips[*]}")
28 |
29 | for file in "${shell_files[@]}"; do
30 | [[ $file == *swp ]] && continue
31 | is "$(shellcheck -e "$skip" "$file")" "" \
32 | "The shell file '$file' passes shellcheck"
33 | done
34 |
35 | done_testing
36 |
37 | # vim: set ft=sh:
38 |
--------------------------------------------------------------------------------
/test/source-bash+-std.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | source bash+ :std
6 |
7 | ok "$(bash+:can use)" 'use is imported'
8 | ok "$(bash+:can die)" 'die is imported'
9 | ok "$(bash+:can warn)" 'warn is imported'
10 |
11 | ok "$(! bash+:can import)" 'import is not imported'
12 | ok "$(! bash+:can main)" 'main is not imported'
13 | ok "$(! bash+:can fcopy)" 'fcopy is not imported'
14 | ok "$(! bash+:can findlib)" 'findlib is not imported'
15 | ok "$(! bash+:can can)" 'can is not imported'
16 |
17 | done_testing 8
18 |
--------------------------------------------------------------------------------
/test/source-bash+.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | source bash+
6 |
7 | functions=(
8 | use
9 | import
10 | fcopy
11 | findlib
12 | die
13 | warn
14 | can
15 | )
16 |
17 | for f in "${functions[@]}"; do
18 | is "$(type -t "bash+:$f")" function \
19 | "bash+:$f is a function"
20 | done
21 |
22 | done_testing 7
23 |
--------------------------------------------------------------------------------
/test/use.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | source bash+ :std can
6 |
7 | # shellcheck disable=2034
8 | BASHLIB=test/lib
9 |
10 | use Foo::Bar
11 |
12 | ok $? 'use Foo::Bar - works'
13 | ok "$(can Foo::Bar:baz)" 'Function Foo::Bar:baz exists'
14 |
15 | # shellcheck disable=2016,2154
16 | is "$Foo__Bar_VERSION" 1.2.3 '$Foo__Bar_VERSION == 1.2.3'
17 |
18 | output=$(use Foo::Foo Boo Booo)
19 | ok $? 'use Foo::Foo Boo Booo - works'
20 | is "$output" Boo---Booo 'Correct import called'
21 |
22 | done_testing 5
23 |
--------------------------------------------------------------------------------
/test/version-check.t:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source test/setup
4 |
5 | PATH=$PWD/bin:$PATH
6 | source bash+ version-check
7 |
8 | t1() (echo 0.1.2)
9 | t2() (echo 0.1)
10 |
11 | ok "$(version-check t1 0)" "0.1.2 >= 0"
12 | ok "$(version-check t1 0.1)" "0.1.2 >= 0.1"
13 | ok "$(version-check t1 0.1.1)" "0.1.2 >= 0.1.1"
14 | ok "$(version-check t1 0.1.2)" "0.1.2 >= 0.1.2"
15 | ok "$(! version-check t1 0.2)" "0.1.2 >= 0.2 fails"
16 | ok "$(! version-check t1 0.1.3)" "0.1.2 >= 0.1.3 fails"
17 |
18 | ok "$(version-check t2 0)" "0.1 >= 0"
19 | ok "$(version-check t2 0.1)" "0.1 >= 0.1"
20 | ok "$(! version-check t2 0.2)" "0.1 >= 0.2 fails"
21 | ok "$(! version-check t2 0.1.1)" "0.1 >= 0.1.1"
22 |
23 | done_testing 10
24 |
--------------------------------------------------------------------------------