├── README.md ├── bash.yml ├── bash ├── Dockerfile └── test.sh ├── collection.yml └── collection ├── Dockerfile ├── functions ├── cryptocurrency ├── lyrics ├── movies └── weather └── select_function.sh /README.md: -------------------------------------------------------------------------------- 1 | # Bash Functions as a Service 2 | 3 | Goal : Run bash scripts as serverless functions. 4 | 5 | ## Blog post 6 | 7 | There is a more detailed blog post [here](https://medium.com/@thomas.shaw78/bash-functions-as-a-service-b4033bc1ee97). 8 | 9 | OpenFaaS is very straight forward to setup. This setup uses a local Docker Swarm. Instructions can be found [here](https://docs.openfaas.com/deployment/docker-swarm/). 10 | 11 | Follow the instructions [here](https://blog.alexellis.io/cli-functions-with-openfaas). When you reach the "2.1 nmap" section then you can try the following. 12 | 13 | ## Setup 14 | 15 | 16 | ### Create scaffolding 17 | ``` 18 | faas new --lang dockerfile bash --prefix tshaw 19 | ``` 20 | 21 | The prefix will be included in the container image name. For example the image for this function will be called : tshaw/bash:latest. 22 | Without the prefix the image will be named bash:latest and this may cause issues if there is a publicly available image with the same name. 23 | 24 | ### Add in bash script and update Dockerfile 25 | 26 | Copy [test.sh](./bash/test.sh) into the "bash" directory. 27 | 28 | 29 | Update Dockerfile to include : 30 | ``` 31 | RUN apk add --no-cache bash whois iputils 32 | 33 | ADD test.sh /tmp/test.sh 34 | 35 | ENV fprocess="xargs bash /tmp/test.sh" 36 | 37 | ``` 38 | 39 | ## Build and Deploy 40 | 41 | ``` 42 | faas build -f bash.yml && faas deploy -f bash.yml 43 | ``` 44 | 45 | ## Test it works 46 | 47 | echo -n "google.com" | faas invoke bash 48 | 49 | echo -n "bbc.com" | faas invoke bash 50 | 51 | echo -n "amazon.com" | faas invoke bash 52 | 53 | Example Output : 54 | ``` 55 | ========================================= 56 | Running WHOIS for : google.com 57 | ========================================= 58 | Name Server: ns4.google.com 59 | Name Server: ns2.google.com 60 | Name Server: ns3.google.com 61 | Name Server: ns1.google.com 62 | ========================================= 63 | Ping Check 64 | ========================================= 65 | PING google.com (74.125.193.100) 56(84) bytes of data. 66 | 64 bytes from ig-in-f100.1e100.net (74.125.193.100): icmp_seq=1 ttl=46 time=14.0 ms 67 | 68 | --- google.com ping statistics --- 69 | 1 packets transmitted, 1 received, 0% packet loss, time 0ms 70 | rtt min/avg/max/mdev = 14.004/14.004/14.004/0.000 ms 71 | ========================================= 72 | 73 | ``` 74 | 75 | ## Add more replicas to handle incoming requests 76 | 77 | ``` 78 | docker service scale bash=3 79 | 80 | bash scaled to 3 81 | overall progress: 3 out of 3 tasks 82 | 1/3: running [==================================================>] 83 | 2/3: running [==================================================>] 84 | 3/3: running [==================================================>] 85 | verify: Service converged 86 | 87 | Verify the hostname changes when running this a number of times : 88 | 89 | echo -n "amazon.com" | faas invoke bash 90 | 91 | ``` 92 | 93 | # Selecting different scripts from same function container 94 | 95 | In the example above we have put one script into a container image and invoked it as a FaaS. If you have a bunch of similar scripts, they are small and share the same dependencies then you could put them into the same container image. 96 | 97 | There is an example [here](./collection). We use a small piece of bash as the top level function and then select the function we require when invoking the collection function. 98 | 99 | ## Build and deploy the "Collection" function 100 | ``` 101 | faas build collection.yml && faas deploy -f collection.yml 102 | 103 | ``` 104 | 105 | ## Invoke different functions from the collection function 106 | 107 | Invoke Weather function 108 | ``` 109 | echo -n "--function weather --argument Dublin" | faas invoke collection 110 | ``` 111 | 112 | Invoke Movie function 113 | ``` 114 | echo -n "--function movies --argument Seven" | faas invoke collection 115 | ``` 116 | 117 | Invoke cryptocurrency function 118 | ``` 119 | echo -n "--function cryptocurrency" | faas invoke collection 120 | ``` 121 | 122 | Invoke lyrics function 123 | ``` 124 | echo -n "--function lyrics --argument \"-a U2 -s Beautiful Day\"" | faas invoke collection 125 | 126 | ``` 127 | 128 | This method might be frowned upon but it's convenient for evaluation purposes. 129 | -------------------------------------------------------------------------------- /bash.yml: -------------------------------------------------------------------------------- 1 | provider: 2 | name: faas 3 | gateway: http://127.0.0.1:8080 4 | functions: 5 | bash: 6 | lang: dockerfile 7 | handler: ./bash 8 | image: bash:function 9 | -------------------------------------------------------------------------------- /bash/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.8 2 | 3 | RUN mkdir -p /home/app 4 | 5 | RUN apk --no-cache add curl \ 6 | && echo "Pulling watchdog binary from Github." \ 7 | && curl -sSL https://github.com/openfaas/faas/releases/download/0.9.6/fwatchdog > /usr/bin/fwatchdog \ 8 | && chmod +x /usr/bin/fwatchdog \ 9 | && cp /usr/bin/fwatchdog /home/app \ 10 | && apk del curl --no-cache 11 | 12 | RUN apk add --no-cache bash whois iputils 13 | 14 | # Add non root user 15 | RUN addgroup -S app && adduser app -S -G app 16 | 17 | RUN chown app /home/app 18 | 19 | WORKDIR /home/app 20 | 21 | USER app 22 | 23 | ADD test.sh /tmp/test.sh 24 | 25 | # Populate example here - i.e. "cat", "sha512sum" or "node index.js" 26 | ENV fprocess="xargs bash /tmp/test.sh" 27 | # Set to true to see request in function logs 28 | ENV write_debug="true" 29 | 30 | EXPOSE 8080 31 | 32 | ENV PWD /home/app 33 | ENV HOME /home/app 34 | 35 | RUN env 36 | RUN ls /home/app 37 | 38 | HEALTHCHECK --interval=3s CMD [ -e /tmp/.lock ] || exit 1 39 | CMD [ "fwatchdog" ] 40 | -------------------------------------------------------------------------------- /bash/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Hostname : ${HOSTNAME}" 4 | echo "=========================================" 5 | echo "Running WHOIS for : $1" 6 | echo "=========================================" 7 | whois $1 |grep "^Name Server" 8 | echo "=========================================" 9 | 10 | echo "Ping Check" 11 | echo "=========================================" 12 | ping -c 1 $1 13 | echo "=========================================" 14 | -------------------------------------------------------------------------------- /collection.yml: -------------------------------------------------------------------------------- 1 | provider: 2 | name: faas 3 | gateway: http://127.0.0.1:8080 4 | functions: 5 | collection: 6 | lang: dockerfile 7 | handler: ./collection 8 | image: tshaw/collection:latest 9 | -------------------------------------------------------------------------------- /collection/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.8 2 | 3 | RUN apk add --no-cache bash python 4 | 5 | RUN mkdir -p /home/app 6 | 7 | RUN apk --no-cache add curl \ 8 | && echo "Pulling watchdog binary from Github." \ 9 | && curl -sSL https://github.com/openfaas/faas/releases/download/0.9.6/fwatchdog > /usr/bin/fwatchdog \ 10 | && chmod +x /usr/bin/fwatchdog \ 11 | && cp /usr/bin/fwatchdog /home/app \ 12 | && apk del curl --no-cache 13 | 14 | # Add non root user 15 | RUN addgroup -S app && adduser app -S -G app 16 | 17 | RUN chown app /home/app 18 | 19 | WORKDIR /home/app 20 | 21 | USER app 22 | 23 | ADD select_function.sh /tmp/select_function.sh 24 | ADD functions /tmp/functions 25 | 26 | # Populate example here - i.e. "cat", "sha512sum" or "node index.js" 27 | ENV fprocess="xargs bash /tmp/select_function.sh" 28 | 29 | # Set to true to see request in function logs 30 | ENV write_debug="true" 31 | 32 | EXPOSE 8080 33 | 34 | RUN ls /tmp/functions 35 | 36 | HEALTHCHECK --interval=3s CMD [ -e /tmp/.lock ] || exit 1 37 | CMD [ "fwatchdog" ] 38 | -------------------------------------------------------------------------------- /collection/functions/cryptocurrency: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Original Author: Jonas-Taha El Sesiy https://github.com/elsesiy 3 | # Modifications: Alexander Epstein 4 | 5 | unset base 6 | unset exchangeTo 7 | old="false" 8 | currentVersion="1.22.1" 9 | unset configuredClient 10 | 11 | ## This function determines which http get tool the system has installed and returns an error if there isnt one 12 | getConfiguredClient() 13 | { 14 | if command -v curl &>/dev/null; then 15 | configuredClient="curl" 16 | elif command -v wget &>/dev/null; then 17 | configuredClient="wget" 18 | elif command -v http &>/dev/null; then 19 | configuredClient="httpie" 20 | elif command -v fetch &>/dev/null; then 21 | configuredClient="fetch" 22 | else 23 | echo "Error: This tool reqires either curl, wget, httpie or fetch to be installed." >&2 24 | return 1 25 | fi 26 | } 27 | 28 | ## Allows to call the users configured client without if statements everywhere 29 | httpGet() 30 | { 31 | case "$configuredClient" in 32 | curl) curl -A curl -s "$@" ;; 33 | wget) wget -qO- "$@" ;; 34 | httpie) http -b GET "$@" ;; 35 | fetch) fetch -q "$@" ;; 36 | esac 37 | } 38 | 39 | ## Top 10 cryptocurrencies as base 40 | checkValidCurrency() 41 | { 42 | if [[ $1 != "BTC" && $1 != "ETH" \ 43 | && $1 != "XRP" && $1 != "LTC" && $1 != "XEM" \ 44 | && $1 != "ETC" && $1 != "DASH" && $1 != "MIOTA" \ 45 | && $1 != "XMR" && $1 != "STRAT" && $1 != "BCH" ]]; then 46 | echo "1" 47 | else 48 | echo "0" 49 | fi 50 | } 51 | 52 | checkTargetCurrency() 53 | { 54 | if [[ $1 != "AUD" && $1 != "BRL" \ 55 | && $1 != "CAD" && $1 != "CHF" && $1 != "CNY" \ 56 | && $1 != "EUR" && $1 != "GBP" && $1 != "HKD" \ 57 | && $1 != "IDR" && $1 != "INR" && $1 != "JPY" && $1 != "KRW" \ 58 | && $1 != "MXN" && $1 != "RUB" && $1 != "USD" ]]; then 59 | echo "1" 60 | else 61 | echo "0" 62 | fi 63 | } 64 | 65 | ## Grabs the base currency from the user and validates it with all the possible currency 66 | ## types available on the API and guides user through input (doesnt take in arguments) 67 | getBase() 68 | { 69 | echo -n "What is the base currency: " 70 | read -r base 71 | base=$(echo $base | tr /a-z/ /A-Z/) 72 | if [[ $(checkValidCurrency $base) == "1" ]]; then 73 | unset base 74 | echo "Invalid base currency" 75 | getBase 76 | fi 77 | } 78 | 79 | ## Checks base currency from the user and validates it with all the possible currency 80 | ## types available on the API (requires argument) 81 | checkBase() 82 | { 83 | base=$1 84 | base=$(echo $base | tr /a-z/ /A-Z/) 85 | if [[ $(checkValidCurrency $base) == "1" ]]; then 86 | unset base 87 | echo "Invalid base currency" 88 | exit 1 89 | fi 90 | } 91 | 92 | # Matches the symbol to the appropriate ids regarding the API calling. 93 | transformBase() 94 | { 95 | case "$base" in 96 | "ETH") reqId="ethereum" ;; 97 | "BTC") reqId="bitcoin" ;; 98 | "XRP") reqId="ripple" ;; 99 | "LTC") reqId="litecoin" ;; 100 | "XEM") reqId="nem" ;; 101 | "ETC") reqId="ethereum-classic" ;; 102 | "DASH") reqId="dash" ;; 103 | "MIOTA") reqId="iota" ;; 104 | "XMR") reqId="monero" ;; 105 | "STRAT") reqId="stratis" ;; 106 | "BCH") reqId="bitcoin-cash" ;; 107 | esac 108 | } 109 | 110 | ## Grabs the exchange to currency from the user and validates it with all the possible currency 111 | ## types available on the API and guides user through input (doesnt take in arguments) 112 | getExchangeTo() 113 | { 114 | echo -n "What currency to exchange to: " 115 | read -r exchangeTo 116 | exchangeTo=$(echo $exchangeTo | tr /a-z/ /A-Z/) 117 | if [[ $(checkTargetCurrency $exchangeTo) == "1" ]]; then 118 | echo "Invalid exchange currency" 119 | unset exchangeTo 120 | getExchangeTo 121 | fi 122 | } 123 | 124 | ## Grabs the exchange to currency from the user and validates it with all the possible currency 125 | ## types available on the API (requires arguments) 126 | checkExchangeTo() 127 | { 128 | exchangeTo=$1 129 | exchangeTo=$(echo $exchangeTo | tr /a-z/ /A-Z/) 130 | if [[ $(checkTargetCurrency $exchangeTo) == "1" ]]; then 131 | echo "Invalid exchange currency" 132 | unset exchangeTo 133 | exit 1 134 | fi 135 | } 136 | 137 | ## Get the amount that will be exchanged and validate that the user has entered a number (decimals are allowed) 138 | ## doesnt take in argument, it guides user through input 139 | getAmount() 140 | { 141 | echo -n "What is the amount being exchanged: " 142 | read -r amount 143 | if [[ ! "$amount" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then 144 | echo "The amount has to be a number" 145 | unset amount 146 | getAmount 147 | fi 148 | } 149 | 150 | ## Get the amount that will be exchanged 151 | ## validate that the user has entered a number (decimals are allowed and requires argument) 152 | checkAmount() 153 | { 154 | amount=$1 155 | if [[ ! "$amount" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then 156 | echo "The amount has to be a number" 157 | unset amount 158 | exit 1 159 | fi 160 | } 161 | 162 | checkInternet() 163 | { 164 | httpGet github.com > /dev/null 2>&1 || { echo "Error: no active internet connection" >&2; return 1; } # query github with a get request 165 | } 166 | 167 | ## Grabs the exchange rate and does the math for converting the currency 168 | convertCurrency() 169 | { 170 | exchangeTo=$(echo "$exchangeTo" | tr '[:upper:]' '[:lower:]') 171 | exchangeRate=$(httpGet "https://api.coinmarketcap.com/v1/ticker/$reqId/?convert=$exchangeTo" | grep -Eo "\"price_$exchangeTo\": \"[0-9 .]*" | sed s/"\"price_$exchangeTo\": \""//g) > /dev/null 172 | if ! command -v bc &>/dev/null; then 173 | oldRate=$exchangeRate 174 | exchangeRate=$(echo $exchangeRate | grep -Eo "^[0-9]*" ) 175 | amount=$(echo $amount | grep -Eo "^[0-9]*" ) 176 | exchangeAmount=$(( $exchangeRate * $amount )) 177 | exchangeRate=$oldRate 178 | else 179 | exchangeAmount=$( echo "$exchangeRate * $amount" | bc ) 180 | fi 181 | exchangeTo=$(echo "$exchangeTo" | tr '[:lower:]' '[:upper:]') 182 | 183 | cat <&2 205 | exit 1 206 | elif [[ $latestVersion == "" ]]; then 207 | echo "Error: no active internet connection" >&2 208 | exit 1 209 | else 210 | if [[ "$latestVersion" != "$currentVersion" ]]; then 211 | echo "Version $latestVersion available" 212 | echo -n "Do you wish to update $repositoryName [Y/n]: " 213 | read -r answer 214 | if [[ "$answer" == [Yy] ]]; then 215 | cd ~ || { echo 'Update Failed'; exit 1; } 216 | if [[ -d ~/$repositoryName ]]; then rm -r -f $repositoryName || { echo "Permissions Error: try running the update as sudo"; exit 1; } ; fi 217 | echo -n "Downloading latest version of: $repositoryName." 218 | git clone -q "https://github.com/$githubUserName/$repositoryName" && touch .BSnippetsHiddenFile || { echo "Failure!"; exit 1; } & 219 | while [ ! -f .BSnippetsHiddenFile ]; do { echo -n "."; sleep 2; };done 220 | rm -f .BSnippetsHiddenFile 221 | echo "Success!" 222 | cd $repositoryName || { echo 'Update Failed'; exit 1; } 223 | git checkout "v$latestVersion" 2> /dev/null || git checkout "$latestVersion" 2> /dev/null || echo "Couldn't git checkout to stable release, updating to latest commit." 224 | chmod a+x install.sh #this might be necessary in your case but wasnt in mine. 225 | ./$nameOfInstallFile "update" || exit 1 226 | cd .. 227 | rm -r -f $repositoryName || { echo "Permissions Error: update succesfull but cannot delete temp files located at ~/$repositoryName delete this directory with sudo"; exit 1; } 228 | else 229 | exit 1 230 | fi 231 | else 232 | echo "$repositoryName is already the latest version" 233 | fi 234 | fi 235 | } 236 | 237 | usage() 238 | { 239 | cat <&2 260 | exit 1 261 | ;; 262 | c) 263 | currency=$OPTARG 264 | ;; 265 | h) usage 266 | exit 0 267 | ;; 268 | f) 269 | fiat=$OPTARG 270 | ;; 271 | o) 272 | old="true" 273 | ;; 274 | v) echo "Version $currentVersion" 275 | exit 0 276 | ;; 277 | u) checkInternet || exit 1 # check if we have a valid internet connection if this isnt true the rest of the script will not work so stop here 278 | update 279 | exit 0 280 | ;; 281 | :) echo "Option -$OPTARG requires an argument." >&2 282 | exit 1 283 | ;; 284 | esac 285 | done 286 | 287 | if $old;then 288 | getConfiguredClient || exit 1 289 | getBase # get base currency 290 | getExchangeTo # get exchange to currency 291 | getAmount # get the amount to be converted 292 | transformBase 293 | convertCurrency # grab the exhange rate and perform the conversion 294 | exit 0 295 | fi 296 | 297 | 298 | 299 | if [[ $1 == "update" ]]; then 300 | getConfiguredClient || exit 1 301 | checkInternet || exit 1 # check if we have a valid internet connection if this isnt true the rest of the script will not work so stop here 302 | update 303 | exit 0 304 | elif [[ $1 == "help" ]]; then 305 | usage 306 | exit 0 307 | fi 308 | 309 | getConfiguredClient || exit 1 310 | checkInternet || exit 1 311 | link="rate.sx" 312 | if [[ $fiat =~ [a-zA-Z] ]]; then 313 | link="$fiat.$link" 314 | fi 315 | 316 | if [[ $currency =~ [a-zA-Z] ]]; then 317 | link="$link/$currency" 318 | fi 319 | 320 | httpGet $link 321 | -------------------------------------------------------------------------------- /collection/functions/lyrics: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Author: Alexander Epstein https://github.com/alexanderepstein 3 | currentVersion="1.22.1" 4 | configuredClient="" 5 | artist="false" 6 | song="false" 7 | filePath="" 8 | 9 | ## This function determines which http get tool the system has installed and returns an error if there isnt one 10 | getConfiguredClient() 11 | { 12 | if command -v curl &>/dev/null; then 13 | configuredClient="curl" 14 | elif command -v wget &>/dev/null; then 15 | configuredClient="wget" 16 | elif command -v http &>/dev/null; then 17 | configuredClient="httpie" 18 | elif command -v fetch &>/dev/null; then 19 | configuredClient="fetch" 20 | else 21 | echo "Error: This tool reqires either curl, wget, httpie or fetch to be installed." >&2 22 | return 1 23 | fi 24 | } 25 | 26 | getConfiguredPython() 27 | { 28 | if command -v python2 &>/dev/null ; then 29 | configuredPython="python2" 30 | elif command -v python &>/dev/null ; then 31 | configuredPython="python" 32 | else 33 | echo "Error: This tool requires python 2 to be installed." 34 | return 1 35 | fi 36 | } 37 | 38 | if [[ $(uname) != "Darwin" ]]; then 39 | python() 40 | { 41 | case "$configuredPython" in 42 | python2) python2 "$@";; 43 | python) python "$@";; 44 | esac 45 | } 46 | fi 47 | 48 | 49 | ## Allows to call the users configured client without if statements everywhere 50 | httpGet() 51 | { 52 | case "$configuredClient" in 53 | curl) curl -A curl -s "$@" ;; 54 | wget) wget -qO- "$@" ;; 55 | httpie) http -b GET "$@" ;; 56 | fetch) fetch -q "$@" ;; 57 | esac 58 | } 59 | 60 | update() 61 | { 62 | # Author: Alexander Epstein https://github.com/alexanderepstein 63 | # Update utility version 1.2.0 64 | # To test the tool enter in the defualt values that are in the examples for each variable 65 | repositoryName="Bash-Snippets" #Name of repostiory to be updated ex. Sandman-Lite 66 | githubUserName="alexanderepstein" #username that hosts the repostiory ex. alexanderepstein 67 | nameOfInstallFile="install.sh" # change this if the installer file has a different name be sure to include file extension if there is one 68 | latestVersion=$(httpGet https://api.github.com/repos/$githubUserName/$repositoryName/tags | grep -Eo '"name":.*?[^\\]",'| head -1 | grep -Eo "[0-9.]+" ) #always grabs the tag without the v option 69 | 70 | if [[ $currentVersion == "" || $repositoryName == "" || $githubUserName == "" || $nameOfInstallFile == "" ]]; then 71 | echo "Error: update utility has not been configured correctly." >&2 72 | exit 1 73 | elif [[ $latestVersion == "" ]]; then 74 | echo "Error: no active internet connection" >&2 75 | exit 1 76 | else 77 | if [[ "$latestVersion" != "$currentVersion" ]]; then 78 | echo "Version $latestVersion available" 79 | echo -n "Do you wish to update $repositoryName [Y/n]: " 80 | read -r answer 81 | if [[ "$answer" == [Yy] ]]; then 82 | cd ~ || { echo 'Update Failed'; exit 1; } 83 | if [[ -d ~/$repositoryName ]]; then rm -r -f $repositoryName || { echo "Permissions Error: try running the update as sudo"; exit 1; } ; fi 84 | git clone "https://github.com/$githubUserName/$repositoryName" || { echo "Couldn't download latest version"; exit 1; } 85 | cd $repositoryName || { echo 'Update Failed'; exit 1; } 86 | git checkout "v$latestVersion" 2> /dev/null || git checkout "$latestVersion" 2> /dev/null || echo "Couldn't git checkout to stable release, updating to latest commit." 87 | chmod a+x install.sh #this might be necessary in your case but wasnt in mine. 88 | ./$nameOfInstallFile "update" || exit 1 89 | cd .. 90 | rm -r -f $repositoryName || { echo "Permissions Error: update succesfull but cannot delete temp files located at ~/$repositoryName delete this directory with sudo"; exit 1; } 91 | else 92 | exit 1 93 | fi 94 | else 95 | echo "$repositoryName is already the latest version" 96 | fi 97 | fi 98 | } 99 | 100 | checkInternet() 101 | { 102 | httpGet github.com > /dev/null 2>&1 || { echo "Error: no active internet connection" >&2; return 1; } # query github with a get request 103 | } 104 | 105 | getLyrics() 106 | { 107 | encodedArtist=$(echo $1 | sed s/" "/%20/g | sed s/"&"/%26/g | sed s/,/%2C/g | sed s/-/%2D/g) 108 | encodedSong=$(echo $2 | sed s/" "/%20/g | sed s/"&"/%26/g | sed s/,/%2C/g | sed s/-/%2D/g) 109 | response=$(httpGet "https://api.lyrics.ovh/v1/$encodedArtist/$encodedSong") 110 | lyrics=$(echo $response | python -c "import sys, json; print json.load(sys.stdin)['lyrics']" 2> /dev/null) 111 | if [[ $lyrics == "" ]];then { echo "Error: no lyrics found!"; return 1; }; fi 112 | } 113 | 114 | printLyrics() 115 | { 116 | if [[ $filePath == "" ]];then echo -e "$lyrics" 117 | else 118 | if [ -f "$filePath" ];then 119 | echo -n "File already exists, do you want to overwrite it [Y/n]: " 120 | read -r answer 121 | if [[ "$answer" == [Yy] ]]; then 122 | echo -e "$lyrics" > "$filePath"; 123 | fi 124 | else 125 | echo -e "$lyrics" > "$filePath"; 126 | fi 127 | fi 128 | } 129 | 130 | usage() 131 | { 132 | cat <&2 152 | exit 1 153 | ;; 154 | h) usage 155 | exit 0 156 | ;; 157 | v) echo "Version $currentVersion" 158 | exit 0 159 | ;; 160 | u) 161 | getConfiguredClient || exit 1 162 | checkInternet || exit 1 163 | update 164 | exit 0 165 | ;; 166 | f) 167 | filePath="$OPTARG" 168 | ;; 169 | a) 170 | artist="true" 171 | if [[ "$(echo "$@" | grep -Eo "\-s")" == "-s" ]];then song="true";fi # wont go through both options if arg spaced and not quoted this solves that issue (dont need this but once had bug on system where it was necessary) 172 | if [[ "$(echo "$@" | grep -Eo "\-f")" == "-f" ]];then filePath=$(echo "$@" | grep -Eo "\-f [ a-z A-Z / 0-9 . \ ]*[ -]?" | sed s/-f//g | sed s/-//g | sed s/^" "//g);fi 173 | ;; 174 | s) 175 | song="true" 176 | if [[ "$(echo "$@" | grep -Eo "\-a")" == "-a" ]];then artist="true";fi # wont go through both options if arg spaced and not quoted this solves that issue (dont need this but once had bug on system where it was necessary) 177 | if [[ "$(echo "$@" | grep -Eo "\-f")" == "-f" ]];then filePath=$(echo "$@" | grep -Eo "\-f [ a-z A-Z / 0-9 . \ ]*[ -]?" | sed s/-f//g | sed s/-//g | sed s/^" "//g);fi 178 | ;; 179 | :) echo "Option -$OPTARG requires an argument." >&2 180 | exit 1 181 | ;; 182 | esac 183 | done 184 | 185 | # special set of first arguments that have a specific behavior across tools 186 | if [[ $# == "0" ]]; then 187 | usage ## if calling the tool with no flags and args chances are you want to return usage 188 | exit 0 189 | elif [[ $# == "1" ]]; then 190 | if [[ $1 == "update" ]]; then 191 | getConfiguredClient || exit 1 192 | checkInternet || exit 1 193 | update || exit 1 194 | exit 0 195 | elif [[ $1 == "help" ]]; then 196 | usage 197 | exit 0 198 | fi 199 | fi 200 | 201 | if ($artist && ! $song) || ($song && ! $artist);then 202 | echo "Error: the -a and the -s flag must be used to fetch lyrics." 203 | exit 1 204 | elif $artist && $song;then 205 | song=$(echo "$@" | grep -Eo "\-s [ a-z A-Z 0-9 . \ ]*[ -]?" | sed s/-s//g | sed s/-//g | sed s/^" "//g) 206 | if [[ $song == "" ]];then { echo "Error: song could not be parsed from input."; exit 1; };fi 207 | artist=$(echo "$@" | grep -Eo "\-a [ a-z A-Z 0-9 . \ ]*[ -]?" | sed s/-a//g | sed s/-//g | sed s/^" "//g) 208 | if [[ $artist == "" ]];then { echo "Error: artist could not be parsed from input."; exit 1; };fi 209 | getConfiguredClient || exit 1 210 | if [[ $(uname) != "Darwin" ]]; then getConfiguredPython || exit 1;fi 211 | checkInternet || exit 1 212 | getLyrics "$artist" "$song" || exit 1 213 | printLyrics 214 | else 215 | { clear; echo "You shouldnt be here but maaaaaaybeee you slipped passed me, learn to use the tool!"; sleep 5; clear;} 216 | usage 217 | exit 1 218 | fi 219 | -------------------------------------------------------------------------------- /collection/functions/movies: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Author: Alexander Epstein https://github.com/alexanderepstein 3 | 4 | currentVersion="1.22.1" 5 | configuredClient="" 6 | configuredPython="" 7 | detail=false 8 | 9 | ## This function determines which http get tool the system has installed and returns an error if there isnt one 10 | getConfiguredClient() 11 | { 12 | if command -v curl &>/dev/null; then 13 | configuredClient="curl" 14 | elif command -v wget &>/dev/null; then 15 | configuredClient="wget" 16 | elif command -v http &>/dev/null; then 17 | configuredClient="httpie" 18 | elif command -v fetch &>/dev/null; then 19 | configuredClient="fetch" 20 | else 21 | echo "Error: This tool reqires either curl, wget, httpie or fetch to be installed." >&2 22 | return 1 23 | fi 24 | } 25 | 26 | ## Allows to call the users configured client without if statements everywhere 27 | httpGet() 28 | { 29 | case "$configuredClient" in 30 | curl) curl -A curl -s "$@" ;; 31 | wget) wget -qO- "$@" ;; 32 | httpie) http -b GET "$@" ;; 33 | fetch) fetch -q "$@" ;; 34 | esac 35 | } 36 | 37 | getConfiguredPython() 38 | { 39 | if command -v python2 &>/dev/null ; then 40 | configuredPython="python2" 41 | elif command -v python &>/dev/null ; then 42 | configuredPython="python" 43 | else 44 | echo "Error: This tool requires python 2 to be installed." 45 | return 1 46 | fi 47 | } 48 | 49 | if [[ $(uname) != "Darwin" ]]; then 50 | python() 51 | { 52 | case "$configuredPython" in 53 | python2) python2 "$@";; 54 | python) python "$@";; 55 | esac 56 | } 57 | fi 58 | 59 | checkInternet() 60 | { 61 | httpGet github.com > /dev/null 2>&1 || { echo "Error: no active internet connection" >&2; return 1; } # query github with a get request 62 | } 63 | 64 | ## This function grabs information about a movie and using python parses the 65 | ## JSON response to extrapolate the information for storage 66 | getMovieInfo() 67 | { 68 | apiKey=946f500a # try not to abuse this it is a key that came from the ruby-scripts repo I link to. 69 | movie=$( (echo "$@" | tr " " + ) | sed 's/-d+//g' ) ## format the inputs to use for the api. Added sed command to filter -d flag. 70 | export PYTHONIOENCODING=utf8 #necessary for python in some cases 71 | movieInfo=$(httpGet "http://www.omdbapi.com/?t=$movie&apikey=$apiKey") > /dev/null # query the server and get the JSON response 72 | checkResponse=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Response']" 2> /dev/null) 73 | if [[ $checkResponse == "False" ]]; then { echo "No movie found" ; return 1 ;} fi ## check to see if the movie was found 74 | # The rest of the code is just extrapolating the data with python from the JSON response 75 | title=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Title']" 2> /dev/null) 76 | year=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Year']" 2> /dev/null) 77 | runtime=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Runtime']" 2> /dev/null) 78 | imdbScore=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Ratings'][0]['Value']" 2> /dev/null) 79 | tomatoScore=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Ratings'][1]['Value']" 2> /dev/null) 80 | rated=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Rated']" 2> /dev/null) 81 | genre=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Genre']" 2> /dev/null) 82 | director=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Director']" 2> /dev/null) 83 | actors=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Actors']" 2> /dev/null) 84 | plot=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Plot']" 2> /dev/null) 85 | 86 | if $detail; then 87 | awards=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Awards']" 2> /dev/null) 88 | boxOffice=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['BoxOffice']" 2> /dev/null) 89 | metacriticScore=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Ratings'][2]['Value']" 2> /dev/null) 90 | production=$(echo $movieInfo | python -c "import sys, json; print json.load(sys.stdin)['Production']" 2> /dev/null) 91 | fi 92 | } 93 | 94 | # Prints the movie information out in a human readable format 95 | printMovieInfo() 96 | { 97 | echo 98 | echo '==================================================' 99 | echo "| Title: $title" 100 | echo "| Year: $year" 101 | echo "| Runtime: $runtime" 102 | if [[ $imdbScore != "" ]]; then echo "| IMDB: $imdbScore"; fi 103 | if [[ $tomatoScore != "" ]]; then echo "| Tomato: $tomatoScore"; fi 104 | if $detail; then 105 | if [[ $metacriticScore != "" ]]; then echo "| Metascore: $metacriticScore"; fi 106 | fi 107 | if [[ $rated != "N/A" && $rated != "" ]]; then echo "| Rated: $rated"; fi 108 | echo "| Genre: $genre" 109 | echo "| Director: $director" 110 | echo "| Actors: $actors" 111 | if [[ $plot != "N/A" && $plot != "" ]]; then echo "| Plot: $plot"; fi 112 | if $detail; then 113 | if [[ $boxOffice != "" ]]; then echo "| Box Office: $boxOffice"; fi 114 | if [[ $production != "" ]]; then echo "| Production: $production"; fi 115 | if [[ $awards != "" ]]; then echo "| Awards: $awards"; fi 116 | fi 117 | echo '==================================================' 118 | echo 119 | } 120 | 121 | update() 122 | { 123 | # Author: Alexander Epstein https://github.com/alexanderepstein 124 | # Update utility version 2.2.0 125 | # To test the tool enter in the defualt values that are in the examples for each variable 126 | repositoryName="Bash-Snippets" #Name of repostiory to be updated ex. Sandman-Lite 127 | githubUserName="alexanderepstein" #username that hosts the repostiory ex. alexanderepstein 128 | nameOfInstallFile="install.sh" # change this if the installer file has a different name be sure to include file extension if there is one 129 | latestVersion=$(httpGet https://api.github.com/repos/$githubUserName/$repositoryName/tags | grep -Eo '"name":.*?[^\\]",'| head -1 | grep -Eo "[0-9.]+" ) #always grabs the tag without the v option 130 | 131 | if [[ $currentVersion == "" || $repositoryName == "" || $githubUserName == "" || $nameOfInstallFile == "" ]]; then 132 | echo "Error: update utility has not been configured correctly." >&2 133 | exit 1 134 | elif [[ $latestVersion == "" ]]; then 135 | echo "Error: no active internet connection" >&2 136 | exit 1 137 | else 138 | if [[ "$latestVersion" != "$currentVersion" ]]; then 139 | echo "Version $latestVersion available" 140 | echo -n "Do you wish to update $repositoryName [Y/n]: " 141 | read -r answer 142 | if [[ "$answer" == [Yy] ]]; then 143 | cd ~ || { echo 'Update Failed'; exit 1; } 144 | if [[ -d ~/$repositoryName ]]; then rm -r -f $repositoryName || { echo "Permissions Error: try running the update as sudo"; exit 1; } ; fi 145 | echo -n "Downloading latest version of: $repositoryName." 146 | git clone -q "https://github.com/$githubUserName/$repositoryName" && touch .BSnippetsHiddenFile || { echo "Failure!"; exit 1; } & 147 | while [ ! -f .BSnippetsHiddenFile ]; do { echo -n "."; sleep 2; };done 148 | rm -f .BSnippetsHiddenFile 149 | echo "Success!" 150 | cd $repositoryName || { echo 'Update Failed'; exit 1; } 151 | git checkout "v$latestVersion" 2> /dev/null || git checkout "$latestVersion" 2> /dev/null || echo "Couldn't git checkout to stable release, updating to latest commit." 152 | chmod a+x install.sh #this might be necessary in your case but wasnt in mine. 153 | ./$nameOfInstallFile "update" || exit 1 154 | cd .. 155 | rm -r -f $repositoryName || { echo "Permissions Error: update succesfull but cannot delete temp files located at ~/$repositoryName delete this directory with sudo"; exit 1; } 156 | else 157 | exit 1 158 | fi 159 | else 160 | echo "$repositoryName is already the latest version" 161 | fi 162 | fi 163 | } 164 | 165 | usage() 166 | { 167 | cat <&2 196 | exit 1 ;; 197 | *) exit 1 ;; 198 | esac 199 | done 200 | 201 | if [[ $# == 0 ]]; then 202 | usage 203 | elif [[ $1 == "update" ]]; then 204 | checkInternet || exit 1 # check if we have a valid internet connection if this isnt true the rest of the script will not work so stop here 205 | update 206 | elif [[ $1 == "help" ]]; then 207 | usage 208 | else 209 | checkInternet || exit 1 # check if we have a valid internet connection if this isnt true the rest of the script will not work so stop here 210 | getMovieInfo "$@" || exit 1 ## exit if we return 1 (chances are movie was not found) 211 | printMovieInfo ## print out the data 212 | fi 213 | -------------------------------------------------------------------------------- /collection/functions/weather: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Author: Alexander Epstein https://github.com/alexanderepstein 3 | 4 | currentVersion="1.22.1" #This version variable should not have a v but should contain all other characters ex Github release tag is v1.2.4 currentVersion is 1.2.4 5 | LANG="${LANG:-en}" 6 | locale=$(echo $LANG | cut -c1-2) 7 | unset configuredClient 8 | if [[ $(echo $locale | grep -Eo "[a-z A-Z]*" | wc -c) != 3 ]]; then locale="en"; fi 9 | 10 | ## This function determines which http get tool the system has installed and returns an error if there isnt one 11 | getConfiguredClient() 12 | { 13 | if command -v curl &>/dev/null; then 14 | configuredClient="curl" 15 | elif command -v wget &>/dev/null; then 16 | configuredClient="wget" 17 | elif command -v http &>/dev/null; then 18 | configuredClient="httpie" 19 | elif command -v fetch &>/dev/null; then 20 | configuredClient="fetch" 21 | else 22 | echo "Error: This tool reqires either curl, wget, httpie or fetch to be installed\." >&2 23 | return 1 24 | fi 25 | } 26 | 27 | ## Allows to call the users configured client without if statements everywhere 28 | httpGet() 29 | { 30 | case "$configuredClient" in 31 | curl) curl -A curl -s "$@" ;; 32 | wget) wget -qO- "$@" ;; 33 | httpie) http -b GET "$@" ;; 34 | fetch) fetch -q "$@" ;; 35 | esac 36 | } 37 | 38 | getIPWeather() 39 | { 40 | country=$(httpGet ipinfo.io/country) > /dev/null ## grab the country 41 | if [[ $country == "US" ]]; then ## if were in the us id rather not use longitude and latitude so the output is nicer 42 | city=$(httpGet ipinfo.io/city) > /dev/null 43 | region=$(httpGet ipinfo.io/region) > /dev/null 44 | if [[ $(echo $region | wc -w) == 2 ]];then 45 | region=$(echo $region | grep -Eo "[A-Z]*" | tr -d "[:space:]") 46 | fi 47 | httpGet $locale.wttr.in/$city,$region$1 48 | else ## otherwise we are going to use longitude and latitude 49 | location=$(httpGet ipinfo.io/loc) > /dev/null 50 | httpGet $locale.wttr.in/$location$1 51 | fi 52 | } 53 | 54 | getLocationWeather() 55 | { 56 | args=$(echo "$@" | tr " " + ) 57 | httpGet $locale.wttr.in/${args} 58 | } 59 | 60 | checkInternet() 61 | { 62 | httpGet github.com > /dev/null 2>&1 || { echo "Error: no active internet connection" >&2; return 1; } # query github with a get request 63 | } 64 | 65 | update() 66 | { 67 | # Author: Alexander Epstein https://github.com/alexanderepstein 68 | # Update utility version 2.2.0 69 | # To test the tool enter in the defualt values that are in the examples for each variable 70 | repositoryName="Bash-Snippets" #Name of repostiory to be updated ex. Sandman-Lite 71 | githubUserName="alexanderepstein" #username that hosts the repostiory ex. alexanderepstein 72 | nameOfInstallFile="install.sh" # change this if the installer file has a different name be sure to include file extension if there is one 73 | latestVersion=$(httpGet https://api.github.com/repos/$githubUserName/$repositoryName/tags | grep -Eo '"name":.*?[^\\]",'| head -1 | grep -Eo "[0-9.]+" ) #always grabs the tag without the v option 74 | 75 | if [[ $currentVersion == "" || $repositoryName == "" || $githubUserName == "" || $nameOfInstallFile == "" ]]; then 76 | echo "Error: update utility has not been configured correctly." >&2 77 | exit 1 78 | elif [[ $latestVersion == "" ]]; then 79 | echo "Error: no active internet connection" >&2 80 | exit 1 81 | else 82 | if [[ "$latestVersion" != "$currentVersion" ]]; then 83 | echo "Version $latestVersion available" 84 | echo -n "Do you wish to update $repositoryName [Y/n]: " 85 | read -r answer 86 | if [[ "$answer" == [Yy] ]]; then 87 | cd ~ || { echo 'Update Failed'; exit 1; } 88 | if [[ -d ~/$repositoryName ]]; then rm -r -f $repositoryName || { echo "Permissions Error: try running the update as sudo"; exit 1; } ; fi 89 | echo -n "Downloading latest version of: $repositoryName." 90 | git clone -q "https://github.com/$githubUserName/$repositoryName" && touch .BSnippetsHiddenFile || { echo "Failure!"; exit 1; } & 91 | while [ ! -f .BSnippetsHiddenFile ]; do { echo -n "."; sleep 2; };done 92 | rm -f .BSnippetsHiddenFile 93 | echo "Success!" 94 | cd $repositoryName || { echo 'Update Failed'; exit 1; } 95 | git checkout "v$latestVersion" 2> /dev/null || git checkout "$latestVersion" 2> /dev/null || echo "Couldn't git checkout to stable release, updating to latest commit." 96 | chmod a+x install.sh #this might be necessary in your case but wasnt in mine. 97 | ./$nameOfInstallFile "update" || exit 1 98 | cd .. 99 | rm -r -f $repositoryName || { echo "Permissions Error: update succesfull but cannot delete temp files located at ~/$repositoryName delete this directory with sudo"; exit 1; } 100 | else 101 | exit 1 102 | fi 103 | else 104 | echo "$repositoryName is already the latest version" 105 | fi 106 | fi 107 | } 108 | 109 | usage() 110 | { 111 | cat <&2 136 | exit 1 137 | ;; 138 | h) usage 139 | exit 0 140 | ;; 141 | v) echo "Version $currentVersion" 142 | exit 0 143 | ;; 144 | u) checkInternet || exit 1 # check if we have a valid internet connection if this isnt true the rest of the script will not work so stop here 145 | update || exit 1 146 | exit 0 147 | ;; 148 | :) echo "Option -$OPTARG requires an argument." >&2 149 | exit 1 150 | ;; 151 | esac 152 | done 153 | 154 | if [[ $# == "0" ]]; then 155 | checkInternet || exit 1 156 | getIPWeather || exit 1 157 | exit 0 158 | elif [[ $1 == "help" || $1 == ":help" ]]; then 159 | usage 160 | exit 0 161 | elif [[ $1 == "update" ]]; then 162 | checkInternet || exit 1 163 | update || exit 1 164 | exit 0 165 | fi 166 | 167 | checkInternet || exit 1 168 | if [[ $1 == "m" ]]; then 169 | getIPWeather "?m" || exit 1 170 | elif [[ "${@: -1}" == "m" ]];then 171 | args=$( echo "${@:1:(($# - 1))}" ?m | sed s/" "//g) 172 | getLocationWeather $args || exit 1 173 | elif [[ $1 == "M" ]]; then 174 | getIPWeather "?M" || exit 1 175 | elif [[ "${@: -1}" == "M" ]];then 176 | args=$( echo "${@:1:(($# - 1))}" ?M | sed s/" "//g) 177 | getLocationWeather $args || exit 1 178 | elif [[ $1 == "mM" || $1 == "Mm" ]]; then 179 | getIPWeather "?m?M" || exit 1 180 | elif [[ "${@: -1}" == "mM" || "${@:-1}" == "Mm" ]];then 181 | args=$( echo "${@:1:(($# - 1))}" ?m?M | sed s/" "//g) 182 | getLocationWeather $args || exit 1 183 | elif [[ $1 == "iM" || $1 == "Mi" ]]; then 184 | getIPWeather "?u?M" || exit 1 185 | elif [[ "${@: -1}" == "iM" || "${@:-1}" == "Mi" ]];then 186 | args=$( echo "${@:1:(($# - 1))}" ?u?M | sed s/" "//g) 187 | getLocationWeather $args || exit 1 188 | elif [[ $1 == "i" ]]; then 189 | getIPWeather "?u" || exit 1 190 | elif [[ "${@: -1}" == "i" ]];then 191 | args=$( echo "${@:1:(($# - 1))}" ?u | sed s/" "//g) 192 | getLocationWeather $args || exit 1 193 | else 194 | getLocationWeather "$@" || exit 1 195 | fi 196 | -------------------------------------------------------------------------------- /collection/select_function.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Simple script to call a specific function 4 | 5 | # functions stored in /tmp/functions and copied into docker image at build time 6 | 7 | function usage() 8 | { 9 | 10 | echo "Requires function name and args :" 11 | echo "Example : echo -n \"--function weather --argument Dublin\" | faas invoke collection" 12 | 13 | } 14 | 15 | # positional args 16 | args=() 17 | 18 | if [ "$1" = "" ];then 19 | usage 20 | exit 1 21 | fi 22 | 23 | # named args 24 | while [ "$1" != "" ]; do 25 | case "$1" in 26 | -f | --function ) function="$2"; shift;; 27 | -a | --argument ) argument="$2"; shift;; 28 | esac 29 | shift # move to next kv pair 30 | done 31 | 32 | if [ -z ${function} ];then 33 | usage 34 | exit 1 35 | else 36 | if [ ! -f /tmp/functions/${function} ];then 37 | echo "Function does not exist : ${function}. Exiting" 38 | exit 1 39 | else 40 | echo "Running Function : ${function}" 41 | echo "Passing in Argument : ${argument}" 42 | fi 43 | fi 44 | 45 | cd /tmp/functions 46 | 47 | ./${function} "${argument}" 48 | 49 | exit $? 50 | --------------------------------------------------------------------------------