[SCRIPT] RetroPie Shell Script Boilerplate
-
I'm starting to like creating scripts for RetroPie so I created a RetroPie Shell Script Boilerplate for myself and now I'd like to present it to you and get some feedback.
Basically, it's a starting point with:
- Some variables and functions that I found myself using in every script I created.
- A config file, because I use it quite often.
- It also includes a
README.md
and aCHANGELOG.md
to document the script. - Some stuff for GitHub: a
LICENSE
and aCONTRIBUTING.md
.
There a lot of comments and examples on how to use the functions and how to write the documentation.
Everything that's encapsulated in[]
is editable and the comments that should be removed when the script is in 'production' are marked.UPDATE 1:
- Merged #2 from @meleu .
- Added
check_dependencies()
. - Added
reset_config()
to use it in conjunction withset_config()
andget_config()
. - Added
--version
(because I think everyone should use it :P) - Added 2 ways to find
/home
. - Added more useful global variables.
Well, here it is:
#!/usr/bin/env bash # [SCRIPT_NAME] (e.g.: script_template.sh) # # [SCRIPT_TITLE] (e.g.: RetroPie Shell Script Boilerplate) # [SCRIPT_DESCRIPTION] (e.g. A template for building shell scripts for RetroPie.) # # Author: [AUTHOR] (e.g. hiulit) # Repository: [REPO_URL] (e.g. https://github.com/hiulit/RetroPie-Shell-Script-Boilerplate) # License: [LICENSE] [LICENSE_URL] (e.g. MIT https://github.com/hiulit/RetroPie-Shell-Script-Boilerplate/blob/master/LICENSE) # # Requirements: # - RetroPie x.x.x (e.g. RetroPie 4.x.x) # - [PACKAGE_NAME] (e.g. libav-tools) # Globals #################################################################### # If the script is called via sudo, detect the user who called it and the homedir. user="$SUDO_USER" [[ -z "$user" ]] && user="$(id -un)" home="$(eval echo ~$user)" # If you really need that the script is run by root user (e.g. script called # from '/etc/rc.local') the approach below can work better to get the homedir # of the RetroPie user. # Comment the code above and uncomment the code below. #home="$(find /home -type d -name RetroPie -print -quit 2>/dev/null)" #home="${home%/RetroPie}" readonly RP_DIR="$home/RetroPie" readonly RP_CONFIG_DIR="/opt/retropie/configs" readonly SCRIPT_VERSION="0.0.0" # Use Semantinc Versioning https://semver.org/ readonly SCRIPT_DIR="$(cd "$(dirname $0)" && pwd)" readonly SCRIPT_NAME="$(basename "$0")" readonly SCRIPT_FULL="$SCRIPT_DIR/$SCRIPT_NAME" #readonly SCRIPT_CFG="$SCRIPT_DIR/[CONFIG_FILE]" # Uncomment if you want/need to use a config file. readonly SCRIPT_TITLE="[SCRIPT_TITLE]" readonly SCRIPT_DESCRIPTION="[SCRIPT_DESCRIPTION]" #readonly SCRIPTMODULE_DIR="/opt/retropie/supplementary/[SCRIPTMODULE_NAME]" # Uncomment if you want/need to use a scriptmoodule. # Other variables that can be useful. #readonly DEPENDENCIES=("[PACKAGE_1]" "[PACKAGE_2]" "[PACKAGE_N]") #readonly ROMS_DIR="$RP_DIR/roms" #readonly ES_THEMES_DIR="/etc/emulationstation/themes" #readonly RCLOCAL="/etc/rc.local" #readonly GIT_REPO_URL="[REPO_URL]" #readonly GIT_SCRIPT_URL="[REPO_URL]/[path/to/script].sh # Variables ################################################################## # Add your own variables here. # Functions ################################################################## function is_retropie() { [[ -d "$RP_DIR" && -d "$home/.emulationstation" && -d "/opt/retropie" ]] } function is_sudo() { [[ "$(id -u)" -eq 0 ]] } # If your script has dependencies, just use the DEPENDENCIES variable on the definitions above. # Otherwise, leave it as is. function check_dependencies() { local pkg for pkg in "${DEPENDENCIES[@]}";do if ! dpkg-query -W -f='${Status}' "$pkg" | grep -qwo "installed"; then echo "ERROR: The '$pkg' package is not installed!" >&2 echo "Would you like to install it now?" local options=("Yes" "No") local option select option in "${options[@]}"; do case "$option" in Yes) if ! which apt-get > /dev/null; then echo "ERROR: Can't install '$pkg' automatically. Try to install it manually." >&2 exit 1 else sudo apt-get install "$pkg" break fi ;; No) echo "ERROR: Can't launch the script if the '$pkg' package is not installed." >&2 exit 1 ;; *) echo "Invalid option. Choose a number between 1 and ${#options[@]}." ;; esac done fi done } function check_argument() { # This method doesn't accept arguments starting with '-'. if [[ -z "$2" || "$2" =~ ^- ]]; then echo >&2 echo "ERROR: '$1' is missing an argument." >&2 echo >&2 echo "Try '$0 --help' for more info." >&2 echo >&2 return 1 fi } # If you are using the config file, uncomment set_config() and get_config(). # In addition, you can also uncomment reset_config() if you need it. # USAGE: # set_config "[KEY]" "[VALUE]" - Sets the VALUE to the KEY in $SCRIPT_CFG. # get_config "[KEY]" - Returns the KEY's VALUE in $SCRIPT_CFG. # reset_config - Resets all VALUES in $SCRIPT_CFG. # # function set_config() { # sed -i "s|^\($1\s*=\s*\).*|\1\"$2\"|" "$SCRIPT_CFG" # echo "\"$1\" set to \"$2\"." # } # # # function get_config() { # local config # config="$(grep -Po "(?<=^$1 = ).*" "$SCRIPT_CFG")" # config="${config%\"}" # config="${config#\"}" # echo "$config" # } # # # function reset_config() { # while read line; do # set_config "$line" "" # done < <(grep -Po ".*?(?=\ = )" "$SCRIPT_CFG") # } function usage() { echo echo "USAGE: $0 [OPTIONS]" # Add 'sudo' before '$0' if the script needs to be run under sudo (e.g. USAGE: sudo $0 [OPTIONS]). Don't change [OPTIONS]! Remember to remove this comment. echo echo "Use '$0 --help' to see all the options." # Add 'sudo' before '$0' if the script needs to be run under sudo (e.g. Use 'sudo $0 --help' ...). Remember to remove this comment. } # Add your own functions here. # You can add as many options as you want. # To add a new option -> Copy and paste from '#H -[O], --[OPTION] ...' until ';;' and make the desired changes. # If you want to align the descriptions of the options, just play with adding/removing spaces/tabs :P function get_options() { if [[ -z "$1" ]]; then usage exit 0 else case "$1" in #H -h, --help Print the help message and exit. -h|--help) echo echo "$SCRIPT_TITLE" for ((i=1; i<="${#SCRIPT_TITLE}"; i+=1)); do [[ -n "$dashes" ]] && dashes+="-" || dashes="-"; done && echo "$dashes" echo "$SCRIPT_DESCRIPTION" echo echo "USAGE: $0 [OPTIONS]" # Add 'sudo' before '$0' if the script needs to be run under sudo (e.g. USAGE: sudo $0 [OPTIONS]). Don't change [OPTIONS]! Remember to remove this comment. echo echo "OPTIONS:" echo sed '/^#H /!d; s/^#H //' "$0" echo exit 0 ;; #H -v, --version Show script version. -v|--version) echo "$SCRIPT_VERSION" ;; #H -[O], --[OPTION] (e.g '-v, --version') [OPTION_DESCRIPTION] (e.g. Show script version.). -[O]|--[OPTION]) # If the option has arguments, uncomment the code below. # check_argument "$1" "$2" || exit 1 # shift # Add the functions for this options here. ;; *) echo "ERROR: invalid option '$1'" >&2 exit 2 ;; esac fi } function main() { # If you need to check if sudo is used, uncomment the code below. # Remember to add 'sudo' in 'usage' and 'help'. # if ! is_sudo; then # echo "ERROR: Script must be run under sudo." # usage # exit 1 # fi if ! is_retropie; then echo "ERROR: RetroPie is not installed. Aborting ..." >&2 exit 1 fi check_dependencies get_options "$@" } main "$@"
and the config file:
# Settings for [SCRIPT_TITLE] (e.g. RetroPie Shell Script Boilerplate) # Add your own [key = "value"] (e.g. path_to_whatever = "/path/to/whatever") # [KEY] WITHOUT quotes. # [VALUE] WITH quotes. # There MUST be 1 space before and after '='. # To indicate that a [KEY] has NO [VALUE] or is NOT SET, just leave the quotes, like this: "". # Description of the [key = "value"] (e.g. # Set path to whatever). [KEY] = "[VALUE]" # Add your own [key = "value"]
I would like to especially thank @meleu for helping me with other projects I've worked on.
I hope that somebody will find this useful and also I'd like to hear some feedback (criticism, suggestions, etc.) so I can improve it and in addition improve my skills developing scripts for RetroPie :D
-
@hiulit If I ever will use config files than this would be a good starting point ;) Usually I parse arguments via command parameters as all of my scripts are very tight and need no configuration.
-
@hiulit OMFG! I was thinking about making the same thing but was a bit unmotivated to start it from scratch. Now you started it... such a relief! :D
After writing all those scripts here and there for the RetroPie community I think I have something to share. I hope to find some time to contribute on your repo.
Thanks for bringing this up!
-
@cyperghost I'm glad you like! :D
I like to use config files because it makes the script more 'user-friendly' in my opinion. Feel free to contribute to the repo if you find a better way of handling config files (setting and getting values) -
@meleu Awesome! I'm already waiting for your contributions! And any other person who'd like to contribute too.
Together we can come up with a nice and useful boilerplate :) -
@hiulit I think the comments are kinda wordy. Would you mind if I change the style, trying to reduce the text a bit?
-
@meleu Yeah, sure, no problem! Go for it!
-
@hiulit PR submitted.
-
@meleu Thanks! PR commented ;)
-
I've updated the RetroPie Shell Script Boilerplate:
UPDATE 1:
- Merged #2 from @meleu .
- Added
check_dependencies()
. - Added
reset_config()
to use it in conjunction withset_config()
andget_config()
. - Added
--version
(because I think everyone should use it :P) - Added 2 ways to find
/home
. - Added more useful global variables.
These changes are also reflected in the first comment.
Coming soon... dialog functions!! Again, thanks to @meleu ;)
-
@hiulit Some additional functions
# This will determine of savestate directory = config # This is part of hiuilits Boilerplate script, with small modification # if '~' is detected then expand full homepath function get_config() { local config config="$(grep -Po "(?<=^$1 = ).*" "$CONFIG_FILE")" config="${config%\"}" config="${config#\"}" # [[ ${config:0:1} = "~" ]] && config="${config#??}" && config=~/"$config" # Expand homepath # [[ -z ${config##*/} ]] && config="${config%?}" # Sanitize pathes if last character is a / # [[ ${config:0:1} != "/" ]] && config="annother value because it is likely no path" echo "$config" } # This will determine which script is curently running # Is it 'runcommand-onend.sh' or 'runcommand-onstart.sh' # It will extract 'end' or 'start' ... Usefull if you need to know which runcommand called the script function get_runcommand() { local i local file_array=("runcommand-onend.sh" "runcommand-onstart.sh") for i in "${file_array[@]}" do [[ $(pgrep -f "$i") ]] && i="${i#*-on}" && echo "${i%.*}" done } # Determining file ages function file_age() { echo "$(date +%s -r "$1")" }
-
@cyperghost Hey, thanks! I'll take a look at them ;)
Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.
Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.