Generate BASH from BASH
Download the latest version by clicking one of the download links above or:
curl -o- https://shellpen.sh/installer.sh | bashOnce you have downloaded the latest version of ShellPen, source the library:
source shellpen.shYou're now ready to start generating shell script source code!
ShellPen offers three sources of documentation:
- ShellPen User Guide (this page)
- ShellPen Command Reference (details for individual commands)
- ShellPen Extension Guide (for extending the built-in syntax)
"Pens" are BASH functions used to author snippets of BASH source code.
Every pen is associated with a separate snippet of source code:
- If you want to author 3 source code files, consider creating 3 separate pens
To create a pen:
shellpen pens new [pen name]Or use the preferred shorthand:
shellpen [pen name]A pen can have any name (which is a valid BASH function name):
- No spaces are allowed
- It is recommended to not name your pen
: - None of the following characters:
<>|()!
When writing to only one source, the conventional pen name is: -
shellpen -You will occassionally want to see the code written by your pen!
To do that, run [pen] code
# Create a pen
shellpen -
# Write some source code
- echo "Hello, world!"
# Preview the generated source code
- code
# => 'echo "Hello, world!"'If at any time you wish to clear a pen's source code, run [pen] cleanSlate
# Create a pen
shellpen -
# Write some source code
- echo "Hello, world!"
# Preview the generated source code
- code
# => 'echo "Hello, world!"'
# Clear the pen's source code
- cleanSlate
# Preview the generated source code (now empty)
- code
# => ""Once you are finished with your pen, put it away via [pen] putAway
This will delete the pen and any source code associated with it.
# Create pen
shellpen -
# ...
# Put pen away
- putAway
# Trying to use the - pen again will now result in an error
- code
# => -: command not foundThis page describes the basic features of ShellPen.
To extend the syntax, view the ⚙️ Extending Syntax Reference.
The most basic pen function is writeln which appends one line of source code.
- writeln "Hello this is some code text"
- writeln "More code goes here"
- code
# => "Hello this is some code text\nMore code goes here"Similar commands:
$ [command] [arguments] is an alias for writeln for "syntax sugar"
- $ someCommand "Argument1" "Hello, world!"
- code
# => 'someCommand Argument1 Hello, world!'When you want to generate code that calls a command, use $
- Exceptions are
echoandprintfwhich have useful generation helpers
If you want the provided arguments wrapped in " quotes, use $$ instead:
- $$ someCommand "Argument1" "Hello, world!"
- code
# => 'someCommand "Argument1" "Hello, world!"'If you would like to add a "shebang" or "hashbang" to your source:
- shebang
- code
# => '#! /bin/bash'- You can also provide your own path, e.g.
- shebang /usr/bin/env bash
Use function to start the definition of a function and } to finish:
- function hello
- echo Hello
- }hello() {
echo "Hello"
}ℹ️ Levels of indentation are automatically added in functions et al.
Use main to add conventional code to run a function when the script is run:
- function hello
- :
- }
- main hellohello() {
:
}
[ "${BASH_SOURCE[0]}" = "$0" ] && "hello" "$@"Helper functions are provided for declaring different types of BASH variables:
varDefines a generic BASH variable
- var x = 10x=10localDefines alocalvariable for use in a BASH function
- fn hello
- local x = 10hello() {
local x=10
}intdeclares a BASH integer value variable
- int x = 10declare -i x=10arraydeclares a single-dimensional BASH array
- array xdeclare -a x- array x Hello World '$@'declare -a x=("Hello" "World" "$@")- "Map" is shorthand for defining an associative-array:
- map xdeclare -A x- map x [Hello]=World [Foo]="Foo Bar"declare -A x=([Hello]="World" [Foo]="Foo Bar")ShellPen provides helpers for generating echo and printf commands.
- Any commands provided to
echowill be wrapped in"quotes
- echo "Hello, world" foo barecho "Hello, world" "foo" "bar"printfspecifically supports providing a'single quoted formatter string
- printf "Hello"printf "Hello"- printf '%s' "Hello"printf '%s' "Hello"- printf -v varname '%s' -- "Hello"printf -v "varname" '%s' -- "Hello"- Any command can have output sent to STDERR by prepending the command with
toStderr
- toStderr echo "Hello"echo "Hello" >&2- toStderr $ myCommand arg1 arg2myCommand arg1 arg2 >&2- Any command can have output sent to a file by prepending the command with
toFile [path]
- toFile log.log echo "Hello"echo "Hello" > "log.log"- toFile log.log $ myCommand arg1 arg2myCommand arg1 arg2 > "log.log"ifconditionals are supported (thenis an optional keyword)
- if [ '$#' -eq 0 ]
- comment Hello
- elif [ '$#' -eq 1 ]
- else
- echo "Hello, world"
- fiif [ $# -eq 0 ]
then
# Hello
:
elif [ $# -eq 1 ]
then
:
else
echo "Hello, world"
fiℹ️ Empty blocks (or those with only comments) automatically have a : added
case/esacconditionals are supportedoptionis used for options::is used in place of;;for closing each option
- case '$1' in
- option "foo"
- echo "Hello from foo"
- ::
- option "bar"
- ::
- option '*'
- echo "Other option!"
- ::
- esaccase "$1" in
foo)
echo "Hello from foo"
;;
bar)
:
;;
*)
echo "Other option!"
;;
esac- One-liner conditionals are supported with the use of
ANDand/orOR
- [ '$#' -eq 0 ] AND toStderr echo "At least one argument is required"[ $# -eq 0 ] && echo "At least one argument is required" >&2- One liner statements with
{ ... }are supported using,in place of;
- [ '$#' -eq 0 ] AND { toStderr echo "Argument is required" , return 1 , }[ $# -eq 0 ] && { echo "Argument is required" >&2; return 1; }forloops are supported (dois an optional keyword)
- for arg in '"$@"'
- echo Argument: '$@'
- donefor arg in "$@"
do
echo "Argument:" "$@"
donewhileloops are supported (dois an optional keyword)
- while [ '$#' -gt 0 ]
- echo Argument: '$1'
- shift
- donewhile [ $# -gt 0 ]
do
echo "Argument:" "$1"
shift
done(( ... ))arithmetic is supported using{{ ... }}in place of parenthesis
- int i=42
- {{ i++ }}declare -i i=42
(( i++ ))It's common to need to use | pipes in generated BASH source code.
- Pipes are supported by providing an escaped
\|in place of a|pipe character
- echo '$1' \| $ sed "'s/foo/bar/'" \| $ head -1 \| $ xargs -n1 echoecho "$1" | sed 's/foo/bar/' | head -1 | xargs -n1 echo- Any command can accept input from STDIN by prepending the command with
fromStdin [source of stdin]
- while 'IFS=""' read -r line \|\| [ -n '"$line"' ]
- echo '$line'
- fromStdin some/file.txt donewhile IFS="" read -r line || [ -n "$line" ]
do
echo "$line"
done < some/file.txtℹ️ The previous example uses an escaped OR (\|\|) instead of OR
- Because
ORadds an||after the previous command's output, this would result in the||being added after the while'sdois automatically added.
- Any command can accept STDIN from a file path by prepending the command with
fromFile [file path]- This is the same as
fromStdinbut wraps provided arguments in"
- This is the same as
- while 'IFS=""' read -r line \|\| [ -n '"$line"' ]
- echo '$line'
- fromFile some/file.txt donewhile IFS="" read -r line || [ -n "$line" ]
do
echo "$line"
done < "some/file.txt"- Any command can accept input from a command by prepending the command with
fromCommand "[command with args]"
- var text = '"$(<"$filePath")"'
- while 'IFS=""' read -r line \|\| [ -n '"$line"' ]
- echo '$line'
- fromCommand "printf '%s' \"\$text\"" donetext="$(<"$filePath")"
while IFS="" read -r line || [ -n "$line" ]
do
echo "$line"
done < <(printf '%s' "$text")- Any command can accept input from a string with
fromText "string"
- while 'IFS=""' read -r line \|\| [ -n '"$line"' ]
- echo '$line'
- fromText '$text' donewhile IFS="" read -r line || [ -n "$line" ]
do
echo "$line"
done <<< "$text"