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:
- functionhello
- 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:
- functionhello
- :
- }
- 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:elseecho"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)
- forargin'"$@"'
- echo Argument: '$@'
- doneforargin"$@"doecho"Argument:""$@"donewhileloops are supported (dois an optional keyword)
- while [ '$#'-gt 0 ]
- echo Argument: '$1'
- shift
- donewhile [ $#-gt 0 ]
doecho"Argument:""$1"shiftdone(( ... ))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" ]
doecho"$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" ]
doecho"$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" ]
doecho"$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" ]
doecho"$line"done<<<"$text"