Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Each of these examples demonstrates one aspect or feature of bashly.
- [extensible](extensible#readme) - letting your script's users extend the script
- [extensible-delegate](extensible-delegate#readme) - extending your script by delegating commands to an external executable
- [whitelist](whitelist#readme) - arguments and flags with a predefined allowed list of values
- [repeatable](repeatable#readme) - allowing flags to be provided multiple times
- [repeatable-arg](repeatable-arg#readme) - allowing args to be provided multiple times
- [repeatable-flag](repeatable-flag#readme) - allowing flags to be provided multiple times
- [conflicts](conflicts#readme) - defining mutually exclusive flags
- [command-private](command-private#readme) - hiding commands from the command list
- [stdin](stdin#readme) - reading input from stdin
Expand Down
1 change: 1 addition & 0 deletions examples/repeatable-arg/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
upcase
129 changes: 129 additions & 0 deletions examples/repeatable-arg/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# Repeatable Argument Example

Demonstrates the use of repeatable arguments that allow users to run commands
such `convert *.png` or `convert 1.png 2.png 3.png`.

This example was generated with:

```bash
$ bashly init
# ... now edit src/bashly.yml to match the example ...
# ... now edit src/root_command.sh to match the example ...
$ bashly generate
```

<!-- include: src/root_command.sh -->

-----

## `bashly.yml`

```yaml
name: upcase
help: Sample application to demonstrate the use of repeatable arguments
version: 0.1.0

args:
- name: file
help: One or more files to process
required: true

# Setting repeatable to true means that the user can provide multiple arguments
# for it.
# The argument will be received as a quoted and space-delimited string which
# needs to be converted to an array with `eval "data=(${args[file]})"`
repeatable: true

examples:
- upcase README.md LICENSE
- upcase *.md
```

## `src/root_command.sh`

```bash
# Convert the space delimited string to an array
files=''
eval "files=(${args[file]})"

echo
echo "files:"
for i in "${files[@]}"; do
echo " path: $i:"
content="$(cat "$i")"
echo " content: ${content}"
echo " upcase: ${content^^}"
done

echo
inspect_args

```


## Generated script output

### `$ ./upcase -h`

```shell
upcase - Sample application to demonstrate the use of repeatable arguments

Usage:
upcase FILE...
upcase --help | -h
upcase --version | -v

Options:
--help, -h
Show this help

--version, -v
Show version number

Arguments:
FILE...
One or more files to process

Examples:
upcase README.md LICENSE
upcase *.md



```

### `$ ./upcase file1`

```shell

files:
path: file1:
content: content of file1
upcase: CONTENT OF FILE1

args:
- ${args[file]} = "file1"


```

### `$ ./upcase file*`

```shell

files:
path: file1:
content: content of file1
upcase: CONTENT OF FILE1
path: file2:
content: content of file2
upcase: CONTENT OF FILE2

args:
- ${args[file]} = "file1" "file2"


```



1 change: 1 addition & 0 deletions examples/repeatable-arg/file1
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
content of file1
1 change: 1 addition & 0 deletions examples/repeatable-arg/file2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
content of file2
18 changes: 18 additions & 0 deletions examples/repeatable-arg/src/bashly.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: upcase
help: Sample application to demonstrate the use of repeatable arguments
version: 0.1.0

args:
- name: file
help: One or more files to process
required: true

# Setting repeatable to true means that the user can provide multiple arguments
# for it.
# The argument will be received as a quoted and space-delimited string which
# needs to be converted to an array with `eval "data=(${args[file]})"`
repeatable: true

examples:
- upcase README.md LICENSE
- upcase *.md
15 changes: 15 additions & 0 deletions examples/repeatable-arg/src/root_command.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Convert the space delimited string to an array
files=''
eval "files=(${args[file]})"

echo
echo "files:"
for i in "${files[@]}"; do
echo " path: $i:"
content="$(cat "$i")"
echo " content: ${content}"
echo " upcase: ${content^^}"
done

echo
inspect_args
11 changes: 11 additions & 0 deletions examples/repeatable-arg/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash

set -x

bashly generate

### Try Me ###

./upcase -h
./upcase file1
./upcase file*
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Repeatable Example
# Repeatable Flag Example

Demonstrates the use of repeatable flags that allow users to run commands such
as `download -d one -d "two three" -vvv`.
Expand Down Expand Up @@ -46,7 +46,7 @@ flags:
repeatable: true

examples:
- download -d one -d "two three" -vvv
- download -d one -d "two three" -vvv
```

## `src/root_command.sh`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ flags:
repeatable: true

examples:
- download -d one -d "two three" -vvv
- download -d one -d "two three" -vvv

6 changes: 6 additions & 0 deletions examples/repeatable-flag/src/initialize.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
## Code here runs inside the initialize() function
## Use it for anything that you need to run before any other function, like
## setting environment vairables:
## CONFIG_FILE=settings.ini
##
## Feel free to empty (but not delete) this file.
File renamed without changes.
1 change: 1 addition & 0 deletions lib/bashly/config_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ def assert_flag(key, value)
assert_optional_string "#{key}.default", value['default']
assert_optional_string "#{key}.validate", value['validate']

assert_boolean "#{key}.repeatable", value['repeatable']
assert_boolean "#{key}.required", value['required']
assert_array "#{key}.allowed", value['allowed'], of: :string
assert_array "#{key}.conflicts", value['conflicts'], of: :string
Expand Down
6 changes: 5 additions & 1 deletion lib/bashly/script/argument.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@ module Bashly
module Script
class Argument < Base
def usage_string
required ? name.upcase : "[#{name.upcase}]"
required ? label : "[#{label}]"
end

def label
repeatable ? "#{name.upcase}..." : name.upcase
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/bashly/views/argument/usage.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# :argument.usage
echo " <%= name.upcase %>"
echo " <%= label %>"
printf "<%= help.wrap(76).indent(4).sanitize_for_print %>\n"
% if allowed
printf " <%= strings[:allowed] % { values: allowed.join(', ') } -%>\n"
Expand Down
16 changes: 14 additions & 2 deletions lib/bashly/views/command/parse_requirements_case.erb
Original file line number Diff line number Diff line change
@@ -1,26 +1,38 @@
# :command.parse_requirements_case
% repeatable_arg = false
% if args.any?
% condition = "if"
% args.each do |arg|
<%= condition %> [[ -z ${args[<%= arg.name %>]+x} ]]; then
<%= arg.render(:validations).indent 2 %>
% if arg.repeatable
% repeatable_arg = true
args[<%= arg.name %>]="\"$1\""
shift
else
args[<%= arg.name %>]="${args[<%= arg.name %>]} \"$1\""
shift
% else
args[<%= arg.name %>]=$1
shift
% end
% condition = "elif"
% end
% if !repeatable_arg
else
% end
% if catch_all.enabled?
other_args+=("$1")
shift
% else
% elsif !repeatable_arg
printf "<%= strings[:invalid_argument] %>\n" "$key"
exit 1
% end
fi
% elsif catch_all.enabled?
other_args+=("$1")
shift
% else
% elsif !repeatable_arg
printf "<%= strings[:invalid_argument] %>\n" "$key"
exit 1
% end
10 changes: 10 additions & 0 deletions lib/bashly/views/command/whitelist_filter.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
# :command.whitelist_filter
% whitelisted_args.each do |arg|
% if arg.repeatable
eval "input_array=(${args[<%= arg.name %>]})"
for i in "${input_array[@]}"; do
if [[ ! $i =~ ^(<%= arg.allowed.join '|' %>)$ ]]; then
printf "%s\n" "<%= strings[:disallowed_argument] % { name: arg.name, allowed: arg.allowed.join(', ') } %>"
exit 1
fi
done
% else
if [[ ! ${args[<%= arg.name %>]} =~ ^(<%= arg.allowed.join '|' %>)$ ]]; then
printf "%s\n" "<%= strings[:disallowed_argument] % { name: arg.name, allowed: arg.allowed.join(', ') } %>"
exit 1
fi
% end
% end
% whitelisted_flags.each do |flag|
% if flag.repeatable
eval "input_array=(${args[<%= flag.name %>]})"
Expand Down
50 changes: 50 additions & 0 deletions spec/approvals/examples/repeatable-arg
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
+ bashly generate
creating user files in src
skipped src/initialize.sh (exists)
skipped src/root_command.sh (exists)
created ./upcase
run ./upcase --help to test your bash script
+ ./upcase -h
upcase - Sample application to demonstrate the use of repeatable arguments

Usage:
upcase FILE...
upcase --help | -h
upcase --version | -v

Options:
--help, -h
Show this help

--version, -v
Show version number

Arguments:
FILE...
One or more files to process

Examples:
upcase README.md LICENSE
upcase *.md

+ ./upcase file1

files:
path: file1:
content: content of file1
upcase: CONTENT OF FILE1

args:
- ${args[file]} = "file1"
+ ./upcase file1 file2

files:
path: file1:
content: content of file1
upcase: CONTENT OF FILE1
path: file2:
content: content of file2
upcase: CONTENT OF FILE2

args:
- ${args[file]} = "file1" "file2"