Skip to content

Commit fcf0359

Browse files
authored
Merge pull request #1 from jazzschmidt/function-referencing
Extensive improvements
2 parents 9e65637 + 9adb29a commit fcf0359

File tree

11 files changed

+824
-651
lines changed

11 files changed

+824
-651
lines changed

README.md

Lines changed: 120 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,120 +1,165 @@
1-
<img src="assets/logo.png" />
1+
![Executing the hello command](assets/logo.png)
22

3-
# shkeleton - Bash Script Library
3+
# skeleton:bash - Bash Script Framework
4+
5+
**skeleton:bash** is a small, declarative framework that aims at providing the look-and-feel of
6+
any other *nix tool you and your users are used to.
7+
It offers a convenient way to structure and organize your bash scripts and assists
8+
you in writing versatile and easy-to-use programs.
49

5-
**shkeleton** is a library that provides a convenient way to structure and
6-
organize your bash scripts and assists you in writing versatile and easy-to-use programs.
710
Whats most important: it generates usage information for you and your scripts users.
811

912
## Features
1013

11-
**shkeleton** makes it easy to
12-
- [parse flag and argument options](#parsing-options)
14+
**skeleton:bash** makes it easy to
15+
- [parse and validate flag and parameter options](#parsing-options)
1316
- generate extensive usage information
14-
- write scripts with different commands
15-
- setup and cleanup the environment
17+
- [write scripts with different sub commands](#adding-commands-and-subcommands)
18+
- [setup and cleanup the environment](#helper-functions)
1619
- [debug and trace your script](#debugging-and-tracing)
1720

1821
In addition to that, it offers functions to [colorize output and emit error messages](#helper-functions).
1922

20-
## Minimal Example
21-
Suppose this executable (chmod +x) `minimal-example.sh`:
23+
## Examples
24+
Suppose this executable (chmod +x) `example.sh`:
2225

2326
```bash
24-
#!/bin/bash
27+
#!/usr/bin/env bash
2528

26-
app_version="1.0.0"
27-
app_description="Example shkeleton Script"
29+
source skeleton.sh
2830

29-
source shkeleton.sh
31+
function @greet() {
32+
description "Displays a greeting"
33+
param "name" "n" "name" "greets <name> instead of the current user"; name="$param"
3034

31-
function setup() {
32-
cmd "hello" "hello_world" "Displays 'hello world'"
35+
execute() {
36+
echo "Hello ${name:-$(whoami)}"
37+
}
3338
}
39+
```
3440

35-
function hello_world() {
36-
arg "n" "name" "string" "Greets <name> instead of 'world'"
37-
name=${arg:-world}
41+
**Executing the greet command**
3842

39-
if $flag_help; then
40-
printf "Displays the message 'hello world'.\n\n"
41-
print_usage && exit 0; # Print usage and exit
42-
fi
43-
44-
debug "Debug log message"
45-
46-
echo "hello ${name}" | green
43+
The **greet** command will receive the parameter `name|n` and also shows them when
44+
running `./example.sh greet --help`.
45+
46+
![Executing the greet command](assets/example-1.gif)
47+
48+
Also, the **greet** command is listed in the global help page:
49+
50+
![Showing the script help](assets/example-2.png)
51+
52+
Instead of using named parameters, your commands can also be configured to receive
53+
arguments:
54+
55+
```bash
56+
function @greet() {
57+
description "Displays a greeting"
58+
args "NAME"
59+
60+
execute() {
61+
echo "Hello ${1}"
62+
}
63+
64+
help() {
65+
echo "Greets the given NAME"
66+
}
4767
}
4868
```
4969

50-
**Executing the hello command**
70+
Since we added the `help` function, a help message other than the default description
71+
tells the user that he can provide the **NAME** argument.
72+
73+
![Using options](assets/example-3.png)
74+
75+
## Adding Commands and Subcommands
76+
77+
Every `@function` will be added as a command to your script. The script will fail, if
78+
you don't provide a description. Nesting commands is a matter of renaming the command:
79+
80+
```bash
81+
function @books() {
82+
description "Book Management"
83+
}
5184

52-
![Executing the hello command](assets/example-1.gif)
85+
function @books-list() {
86+
command "books list"
87+
description "Lists books"
5388

54-
**Showing the hello help**
89+
execute() {
90+
:
91+
}
92+
}
5593

56-
![Showing the hello help](assets/example-2.png)
94+
function @books-create() {
95+
command "books create"
96+
description "Creates a book"
5797

58-
**Showing the script help**
98+
execute() {
99+
:
100+
}
101+
}
102+
```
59103

60-
![Showing the script help](assets/example-3.png)
104+
![Adding subcommands](assets/example-4.png)
61105

62106
## Parsing Options
63107

64-
Defining options is pretty easy in **shkeleton**:
108+
Defining options is pretty straightforward in **skeleton:bash**:
65109
```bash
66-
flag "s" "silent" "silences output"
67-
local silent=$flag # true if either -s or --silent is present, false otherwise
110+
function @command {
111+
flag "s" "silent" "silences output"
112+
declare silent=$flag # true if either -s or --silent is present, false otherwise
113+
114+
flag "v" "increases verbosity"
115+
declare verbose=$flag # only true, when -v is set; short and long args are interchangeable
116+
117+
param "n" "name" "string" "sets the name"
118+
declare name=${param:-default} # the value of -n or --name or just 'default'
119+
# multiple values may be read with ${param[*]} or ${param[@]}
120+
121+
param "max-age" "integer" "sets max-age"
122+
local max_age=${arg:-} # the value of --max-age if present
68123

69-
flag "v" "increases verbosity"
70-
local verbose=$flag # only true, when -v is set; short and long args are interchangeable
124+
# ...
125+
}
126+
```
71127

72-
arg "n" "name" "string" "sets the name"
73-
local name=${arg:-default} # the value of -n or --name or just 'default'
128+
Short options can be passed with the shorthand syntax `-abc`. If you provide an
129+
option, that is not declared in your command an error message will be shown:
74130

75-
arg "max-age" "integer" "sets max-age"
76-
local max_age=${arg:-} # the value of --max-age if present
77-
````
131+
![Unknown options error](assets/example-5.png)
78132

79-
The global variables `$flag` and `$arg` will hold the value directly after the
133+
The global variables `$flag` and `$param` will hold the value directly after the
80134
respective registering function has been called.
81135

82-
Options from the `setup` functions are treated as global flags and should therefore
83-
be written to global variables (just like `$flag_help`), whereas options from
84-
command functions should be saved as local variables like shown in the example above.
85-
86-
*Notice*: Options should be stated at the top of the command function,
87-
so that `print_usage` shows them.
136+
Options from command functions should be saved as global variables to remain usable
137+
for the `execute` function like shown in the example above.
88138

89139
## Commands and special Functions
90140

91-
The `cmd` command will add a named command, maps it to the given function and adds it
92-
to the usage page. Commands should be defined in the `setup` function.
93-
The `version` command is added per default.
94-
95-
There are a few special script workflow functions:
141+
There are a few special functions:
96142

97143
|Function|Description|
98144
|---|---|
99-
|`setup`|Bootstraps the script and adds commands and global flags|
100-
|`teardown`|(Optional) Will be called when the script exits|
101-
|`main`|(Optional) Will be called instead of usage page when script is being called without command |
145+
|`setup`|Bootstraps the script, can be used to define global options|
146+
|`teardown`|Will be called when the script exits|
147+
|`main`|Will be called instead of usage page when script is being called without command |
102148

103-
Also, there are internal versions of these functions (`_setup`, `_teardown` and `_main`),
104-
that can be extended when using **shkeleton** as a library and common logic applies to all
105-
your programs.
149+
*Notice*: When using the `teardown` function, it is necessary to call `__main` at the bottom
150+
of your script!
106151

107152
### Helper Functions
108153

109154
|Function|Description|
110155
|---|---|
111-
|`error`|Emits red error messages to stderr along with its root and exits|
156+
|`error`|Emits red error messages to stderr along with its origin function name|
112157
|`debug`|Logs an orange/brown debug message|
113158
|`colorize`|Colorizes output; can also be used as pipe|
114159
|`red`, `green`, `blue`, ...|*see above*|
115-
|`print_usage`|Prints usage information, should be called only after all options are set|
116160
|`has_flag`|Sets `$flag` to `true` when the flag option exists, false otherwise|
117-
|`get_arg`|Sets `$arg` to the argument options value if present|
161+
|`get_param`|Sets `$param` to the param values if present|
162+
|`array_contains`|Returns `true` if the array `$2` contains `$1`|
118163

119164
## Debugging and Tracing
120165

@@ -127,9 +172,15 @@ Since debugging of bash script can be pretty hard, you can also enable tracing b
127172
supplying the `TRACE` parameter just like the debug parameter, which will enable
128173
tracing as soon as the script enters your command.
129174

130-
## TODO
175+
```bash
176+
function @hello() {
177+
description "Hello world"
178+
179+
execute() {
180+
debug "Command @hello started"
181+
echo "Hello world"
182+
}
183+
}
184+
```
131185

132-
- parse concatenated flag arguments (`$ ./script -qts`)
133-
- validate unknown options
134-
- use associative arrays
135-
- parse multiline input from stdin in `colorize`
186+
![Debugging and tracing](assets/example-6.png)

assets/example-1.gif

-83.1 KB
Loading

assets/example-2.png

19.4 KB
Loading

assets/example-3.png

-452 Bytes
Loading

assets/example-4.png

33.9 KB
Loading

assets/example-5.png

15.4 KB
Loading

assets/example-6.png

46.6 KB
Loading

assets/logo.png

-4.35 KB
Loading

example.sh

Lines changed: 7 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,12 @@
11
#!/usr/bin/env bash
22

3-
# ===========================
4-
# == SHKELETON EXAMPLE ==
5-
# ===========================
6-
#
7-
# Running this example script with '$ ./example.sh hello' will greet the current user.
8-
# When invoking '$ ./script greet --name "John Doe"', the greeting is generated as
9-
# "Hello John Doe, nice to meet you!".
10-
#
11-
# Running without a command, will execute the `main` function defined below.
12-
# To see the usage simply invoke it via '$ ./example.sh --help'.
13-
#
14-
# ATTENTION: In order to show usage/help for a command, we need to manually check for
15-
# the `--help` flag. Also, the `print_usage` function must be called after the options
16-
# for that command are defined - have a look the `greet` function.
17-
#
18-
# Running '$ ./example.sh hello --help' will display something like:
19-
#
20-
# Greets the user
21-
#
22-
# Flags:
23-
# --name string who shall be greeted
24-
#
25-
# Global flags:
26-
# -h, --help shows this help message or more information about a command
27-
# -v, --verbose enable verbose output
3+
source skeleton.sh
284

29-
app_version="1.0.0"
30-
app_description="Example shkeleton Script"
5+
function @greet() {
6+
description "Displays a greeting"
7+
param "name" "n" "name" "greets <name> instead of the current user"; name="$param"
318

32-
. ./shkeleton.sh
33-
34-
flag_verbose=false
35-
36-
function setup() {
37-
# Add the verbose flag
38-
flag "v" "verbose" "enable verbose output"; flag_verbose=$flag
39-
# Add `hello` command, that executes `greet`
40-
cmd "hello" "greet" "emits a nice greeting"
41-
}
42-
43-
function greet() {
44-
# Retrieve name via `$arg`
45-
arg "name" "string" "who shall be greeted"; name=${arg:-$(whoami)}
46-
47-
if $flag_help; then
48-
printf "Greets the user\n\n"
49-
print_usage && exit 0; # Print usage and exit
50-
fi
51-
52-
# Log output
53-
if $flag_verbose; then echo "Running hello command"; fi;
54-
# Greet
55-
echo "Hello ${name}, nice to meet you!" | green
56-
}
57-
58-
function teardown() {
59-
: # Clean up temporary files etc.
60-
}
61-
62-
function main() {
63-
# Remove this function if you want the tool to show it's usage instead
64-
echo "info: invoke me with '${app_name} --help'"
9+
execute() {
10+
echo "Hello ${name:-$(whoami)}"
11+
}
6512
}

0 commit comments

Comments
 (0)