mirror of
https://github.com/natelandau/shell-scripting-templates.git
synced 2025-11-08 13:13:47 -05:00
Major overhaul
After working for ~6 years in private repositories, bringing my updated BASH scripting templates back into the world.
This commit is contained in:
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Nathaniel Landau
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
235
README.md
235
README.md
@@ -1,70 +1,199 @@
|
|||||||
|
# Shell Scripting Templates and Utilities
|
||||||
|
A collection of shell scripting utilities and templates used to ease the creation of BASH scripts.
|
||||||
|
|
||||||
**As of 2017, this repository is in archival state.** I refactored much of the code and am now using the scripts and utilities available publicly in this repository: [https://github.com/natelandau/dotfiles](https://github.com/natelandau/dotfiles).
|
## Bash Script Template Usage
|
||||||
|
To create a new script, copy `scriptTemplate.sh` to a new file and make it executable `chmod 755 [newscript].sh`. Place your custom script logic within the `_mainScript_` function at the top of the script.
|
||||||
|
|
||||||
|
### Script Template Usage
|
||||||
|
Default flags included in the base template are:
|
||||||
|
|
||||||
|
* `-h`: Prints the contents of the `_usage_` function. Edit the text in that function to provide help
|
||||||
|
* `-l [level]`: Log level of the script. One of: `FATAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `ALL`, `OFF` (Default is '`ERROR`')
|
||||||
|
* `-n`: Dryrun, sets `$DRYRUN` to `true` allowing you to write functions that will work non-destructively using the `_execute_` function
|
||||||
|
* `-v`: Sets `$VERBOSE` to `true` and prints all debug messages to stdout
|
||||||
|
* `-q`: Runs in quiet mode, suppressing all output to stdout. Will still write to log files
|
||||||
|
* `--force`: If using the `_seekConfirmation_` utility function, this skips all user interaction. Implied `Yes` to all confirmations.
|
||||||
|
|
||||||
# My Shell Scripts
|
You can add custom script options and flags to the `_parseOptions_` function.
|
||||||
This is the centralized repository of all the shell scripts which I use for a number of different purposes.
|
|
||||||
|
|
||||||
**Important:** *I am a novice programmer and I bear no responsibility whatsoever if any of these scripts that I have written wipes your computer, destroys your data, crashes your car, or otherwise causes mayhem and destruction. USE AT YOUR OWN RISK.*
|
### Script Template Functions
|
||||||
|
scriptTemplate.sh includes some helper functions to perform common tasks.
|
||||||
|
|
||||||
## What's here
|
* `_alert_` Provides alerting and logging functionality. See notes below.
|
||||||
|
* `_trapCleanup_` Cleans up files on error
|
||||||
|
* `_makeTempDir_` Creates a temp directory to house temporary files
|
||||||
|
* `_acquireScriptLock_` Acquires script lock to avoid race conditions on files
|
||||||
|
* `_functionStack_` Prints the function stack in use to aid debugging
|
||||||
|
* `_parseOptions_` Parse options and take user input (`-a`, `--some-flag`, and `--some-file [filename]` supported)
|
||||||
|
* `_usage_` Prints help text when `-h` passed
|
||||||
|
|
||||||
* **etc/** - Many of my scripts and shared functions call for configuration files. These configs are saved here.
|
### Script Initialization
|
||||||
* **lib/** - Shared functions and libraries that are used throughout the scripts. Note all my scripts require these files and will break if they can not be found.
|
The bottom of the script template file contains a block which initializes the script. Comment, uncomment, or change the settings here for your needs
|
||||||
* **setupScripts/** - Scripts that configure new Mac computers from scratch. These scripts perform such tasks as:
|
|
||||||
* Insalling [Homebrew][1] & associated packages
|
|
||||||
* Installing mac applications using [Homebrew Cask][2]
|
|
||||||
* Configuring OSX to my liking
|
|
||||||
* Syncing user preferences and files using [Mackup][3]
|
|
||||||
* Installing [RVM][4] and associated Gems
|
|
||||||
* Pushing a new SSH key to Github
|
|
||||||
* **syncScripts/** - Scripts which use [RSYNC][5] and [Unison][6] to keep different directories and computers in sync.
|
|
||||||
* **scriptTemplate.sh** - A bash script boilerplate template.
|
|
||||||
|
|
||||||
## Versioning
|
```bash
|
||||||
|
trap '_trapCleanup_ ${LINENO} ${BASH_LINENO} "${BASH_COMMAND}" "${FUNCNAME[*]}" "${0}" "${BASH_SOURCE[0]}"' \
|
||||||
|
EXIT INT TERM SIGINT SIGQUIT
|
||||||
|
set -o errtrace # Trap errors in subshells and functions
|
||||||
|
set -o errexit # Exit on error. Append '||true' if you expect an error
|
||||||
|
set -o pipefail # Use last non-zero exit code in a pipeline
|
||||||
|
# shopt -s nullglob globstar # Make `for f in *.txt` work when `*.txt` matches zero files
|
||||||
|
IFS=$' \n\t' # Set IFS to preferred implementation
|
||||||
|
# set -o xtrace # Run in debug mode
|
||||||
|
set -o nounset # Disallow expansion of unset variables
|
||||||
|
# [[ $# -eq 0 ]] && _parseOptions_ "-h" # Force arguments when invoking the script
|
||||||
|
_parseOptions_ "$@" # Parse arguments passed to script
|
||||||
|
# _makeTempDir_ "$(basename "$0")" # Create a temp directory '$tmpDir'
|
||||||
|
# _acquireScriptLock_ # Acquire script lock
|
||||||
|
_mainScript_ # Run the main logic script
|
||||||
|
_safeExit_ # Exit cleanly
|
||||||
|
```
|
||||||
|
|
||||||
This project implements the [Semantic Versioning][7] guidelines.
|
# Utility Files
|
||||||
|
The files within `utilities/` contain BASH functions which can be used in your scripts. Each included function includes detailed usage information. Read the code for instructions.
|
||||||
|
|
||||||
Releases will be numbered with the following format:
|
## Including Utility Functions
|
||||||
|
Within the `utilities` folder are many BASH functions meant to ease development of more complicated scripts. These can be included in the template in two ways.
|
||||||
|
|
||||||
`<major>.<minor>.<patch>`
|
#### 1. Copy and Paste
|
||||||
|
You can copy any complete function from the Utilities and place it into your script. Copy it beneath the end of `_mainscript_()`
|
||||||
|
|
||||||
And constructed with the following guidelines:
|
#### 2. Source entire utility files
|
||||||
|
You can source entire utility files by pasting the following snippet into your script beneath `_mainScript_()`. Be sure to replace `[PATH_TO]` with the full path to this repository.
|
||||||
|
|
||||||
* Breaking backward compatibility bumps the major (and resets the minor and patch)
|
```bash
|
||||||
* New additions without breaking backward compatibility bumps the minor (and resets the patch)
|
_sourceHelperFiles_() {
|
||||||
* Bug fixes and misc changes bumps the patch
|
# DESC: Sources script helper files.
|
||||||
|
local filesToSource
|
||||||
|
local sourceFile
|
||||||
|
filesToSource=(
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/baseHelpers.bash"
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/arrays.bash"
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/files.bash"
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/macOS.bash"
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/numbers.bash"
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/services.bash"
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/textProcessing.bash"
|
||||||
|
"[PATH_TO]/shell-scripting-templates/utilities/dates.bash"
|
||||||
|
)
|
||||||
|
for sourceFile in "${filesToSource[@]}"; do
|
||||||
|
[ ! -f "${sourceFile}" ] \
|
||||||
|
&& {
|
||||||
|
echo "error: Can not find sourcefile '${sourceFile}'."
|
||||||
|
echo "exiting..."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
source "${sourceFile}"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
_sourceHelperFiles_
|
||||||
|
```
|
||||||
|
|
||||||
[Here's more information on semantic versioning.][7]
|
## alerts.bash
|
||||||
|
Basic alerting and setting colors functions (included in scriptTemplate.sh by default). Print messages to stdout and to a user specified logfile using the following functions.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
debug "some text" # Printed only when in Verbose mode
|
||||||
|
info "some text" # Basic informational messages
|
||||||
|
notice "some text" # Messages which should be read. Brighter than 'info'
|
||||||
|
warning "some text" # Non-critical warnings
|
||||||
|
error "some text" # Error state warnings. (Does not stop the script)
|
||||||
|
fatal "some text" # Fatal errors. Exits the script
|
||||||
|
success "some text" # Prints a success message
|
||||||
|
header "some text" # Prints a header element
|
||||||
|
```
|
||||||
|
|
||||||
|
## arrays.bash
|
||||||
|
Common functions for working with BASH arrays.
|
||||||
|
|
||||||
|
* `_inArray_` Determine if a value is in an array
|
||||||
|
* `_join_` Joins items together with a user specified separator
|
||||||
|
* `_setdiff_` Return items that exist in ARRAY1 that are do not exist in ARRAY2
|
||||||
|
* `_removeDupes_` Removes duplicate array elements
|
||||||
|
* `_randomArrayElement_` Selects a random item from an array
|
||||||
|
|
||||||
|
## baseHelpers.bash
|
||||||
|
Commonly used functions in many scripts
|
||||||
|
|
||||||
|
* `_execute_` Executes commands with safety and logging options. Respects `DRYRUN` and `VERBOSE` flags.
|
||||||
|
* `_findBaseDir_` Locates the real directory of the script being run. Similar to GNU readlink -n
|
||||||
|
* `_checkBinary_` Check if a binary exists in the search PATH
|
||||||
|
* `_haveFunction_` Tests if a function exists
|
||||||
|
* `_pauseScript_` Pause a script at any point and continue after user input
|
||||||
|
* `_progressBar_` Prints a progress bar within a for/while loop
|
||||||
|
* `_seekConfirmation_` Seek user input for yes/no question
|
||||||
|
* `_setPATH_` Add directories to $PATH so script can find executables
|
||||||
|
|
||||||
|
## csv.bash
|
||||||
|
Functions to write to a CSV file.
|
||||||
|
* `_makeCSV_` Creates a new CSV file if one does not already exist
|
||||||
|
* `_writeCSV_` Takes passed arguments and writes them as a comma separated line
|
||||||
|
|
||||||
|
## dates.bash
|
||||||
|
Common utilities for working with dates in BASH scripts.
|
||||||
|
|
||||||
|
* `_monthToNumber_` Convert a month name to a number
|
||||||
|
* `_numberToMonth_` Convert a month number to its name
|
||||||
|
* `_parseDate_` Takes a string as input and attempts to find a date within it to parse into component parts (day, month, year)
|
||||||
|
* `_formatDate_` Reformats dates into user specified formats
|
||||||
|
|
||||||
|
## files.bash
|
||||||
|
Common utilities for working with files.
|
||||||
|
|
||||||
|
* `_listFiles_` Find files in a directory. Use either glob or regex.
|
||||||
|
* `_backupFile_` Creates a backup of a specified file with .bak extension or optionally to a specified directory.
|
||||||
|
* `_cleanFilename_` Cleans a filename of all non-alphanumeric (or user specified) characters and overwrites original
|
||||||
|
* `_parseFilename_` Break a filename into its component parts which and place them into prefixed variables (dir, basename, extension, full path, etc.)
|
||||||
|
* `_decryptFile_` Decrypts a file with `openssl`
|
||||||
|
* `_encryptFile_` Encrypts a file with `openssl`
|
||||||
|
* `_ext_` Extract the extension from a filename
|
||||||
|
* `_extract_` Extract a compressed file
|
||||||
|
* `_json2yaml_` Convert JSON to YAML uses python
|
||||||
|
* `_makeSymlink_` Creates a symlink and backs up a file which may be overwritten by the new symlink. If the exact same symlink already exists, nothing is done.
|
||||||
|
* `_parseYAML_` Convert a YANML file into BASH variables for use in a shell script
|
||||||
|
* `_readFile_` Prints each line of a file
|
||||||
|
* `_sourceFile_` Source a file into a script
|
||||||
|
* `_uniqueFileName_` Ensure a file to be created has a unique filename to avoid overwriting other files
|
||||||
|
* `_yaml2json_` Convert a YAML file to JSON with python
|
||||||
|
|
||||||
|
## macOS.bash
|
||||||
|
Functions useful when writing scripts to be run on macOS
|
||||||
|
|
||||||
|
* `_haveScriptableFinder_` Determine whether we can script the Finder or not
|
||||||
|
* `_guiInput_` Ask for user input using a Mac dialog box
|
||||||
|
|
||||||
|
## numbers.bash
|
||||||
|
Helpers to work with numbers
|
||||||
|
|
||||||
|
* `_fromSeconds_` Convert seconds to HH:MM:SS
|
||||||
|
* `_toSeconds_` Converts HH:MM:SS to seconds
|
||||||
|
* `_countdown_` Sleep for a specified amount of time
|
||||||
|
|
||||||
|
## services.bash
|
||||||
|
Functions to work with external services
|
||||||
|
|
||||||
|
* `_haveInternet_` Tests to see if there is an active Internet connection
|
||||||
|
* `_httpStatus_` Report the HTTP status of a specified URL
|
||||||
|
* `_pushover_` Sends a notification via Pushover (Requires API keys)
|
||||||
|
|
||||||
|
## testProcessing.bash
|
||||||
|
Work with strings in your script
|
||||||
|
|
||||||
|
* `_cleanString_` Cleans a string of text
|
||||||
|
* `_stopWords_` Removes common stopwords from a string. Requires a sed stopwords file. Customize to your needs.
|
||||||
|
* `_escape_` Escapes a string by adding `\` before special chars
|
||||||
|
* `_htmlDecode_` Decode HTML characters with sed. (Requires sed file)
|
||||||
|
* `_htmlEncode_` Encode HTML characters with sed (Requires sed file)
|
||||||
|
* `_lower_` Convert a string to lowercase
|
||||||
|
* `_upper_` Convert a string to uppercase
|
||||||
|
* `_ltrim_` Removes all leading whitespace (from the left)
|
||||||
|
* `_regex_` Use regex to validate and parse strings
|
||||||
|
* `_rtrim_` Removes all leading whitespace (from the right)
|
||||||
|
* `_trim_` Removes all leading/trailing whitespace
|
||||||
|
* `_urlEncode_` URL encode a string
|
||||||
|
* `_urlDecode_` Decode a URL encoded string
|
||||||
|
|
||||||
## A Note on Code Reuse
|
## A Note on Code Reuse
|
||||||
The scripts herein were created by me over many years without ever having the intention to make them public. As a novice programmer, I have Googled, GitHubbed, and StackExchanged a path to solve my own scripting needs. Quite often I would lift a function whole-cloth from a GitHub repo and not keep track of it's original location. I have done my best within the scripts to recreate my footsteps and give credit to the original creators of the code when possible. Unfortunately, I fear that I missed as many as I found. My goal of making these scripts public is not to take credit for the wonderful code written by others. If you recognize or wrote something here that I didn't credit, please let me know.
|
I compiled these scripting utilities over many years without having an intention to make them public. As a novice programmer, I have Googled, GitHubbed, and StackExchanged a path to solve my own scripting needs. I often lift a function whole-cloth from a GitHub repo don't keep track of its original location. I have done my best within these files to recreate my footsteps and give credit to the original creators of the code when possible. Unfortunately, I fear that I missed as many as I found. My goal in making this repository public is not to take credit for the code written by others. If you recognize something that I didn't credit, please let me know.
|
||||||
|
|
||||||
# scriptTemplate.sh
|
## License
|
||||||
This is a bash script boilerplate template that I use to create all my scripts. It takes care of a number of items for you including:
|
MIT
|
||||||
|
|
||||||
* Sourcing associated libraries and functions
|
|
||||||
* Gracefully trapping exits
|
|
||||||
* Creating and cleaning up temporary directories
|
|
||||||
* Writing logs
|
|
||||||
* Checking for dependecies (i.e. - ffmpeg or rsync)
|
|
||||||
* Passing options from the command line
|
|
||||||
* Printing usage information when `-h` is passed
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
To use the bash boilerplate for your own script follow these steps.
|
|
||||||
|
|
||||||
1. Make a copy of `scriptTemplate.sh`.
|
|
||||||
2. Ensure that the new script can find the utilities located in `lib/utils.sh` by updating the path of variable `utilsLocation` near the top.
|
|
||||||
3. Ensure your script is executable (i.e. - run `chmod a+x scriptname`)
|
|
||||||
4. Add your script within the function titled `mainScript`
|
|
||||||
|
|
||||||
[1]: http://brew.sh
|
|
||||||
[2]: http://caskroom.io
|
|
||||||
[3]: https://github.com/lra/mackup
|
|
||||||
[4]: https://rvm.io
|
|
||||||
[5]: http://en.wikipedia.org/wiki/Rsync
|
|
||||||
[6]: http://www.cis.upenn.edu/~bcpierce/unison/
|
|
||||||
[7]: http://semver.org
|
|
||||||
[8]: http://www.controlplaneapp.com/
|
|
||||||
|
|||||||
5
bin/.gitignore
vendored
5
bin/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
!convertMedia
|
|
||||||
!README.md
|
|
||||||
!trash
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
This directory contains executable scripts which are run on a day to day basis. To invoke these scripts from anywhere be sure to [add this directory to your PATH][1]
|
|
||||||
|
|
||||||
# convertMedia Script
|
|
||||||
convertMedia automates a number media commands by invoking the correct settings from [ffmpeg][2] and (optionally) [XLD][3]. This script can save a ton of time searching for the correct formats, encoders, and options withinn ffmpeg.
|
|
||||||
|
|
||||||
For Mac users, if you don't have ffmpeg installed, this script will install it with all necessary options using [Homebrew][4]
|
|
||||||
|
|
||||||
Brief overview of features:
|
|
||||||
|
|
||||||
* Resizing video
|
|
||||||
* Converting formats (for example: FLAC to ALAC or WMV to MP4)
|
|
||||||
* Changing bit rates on audio files
|
|
||||||
* Performing actions on a single file, an entire directory, or only on files which match a certain format.
|
|
||||||
|
|
||||||
### Examples of usage
|
|
||||||
Search for all *.flac files in a directory and convert them to
|
|
||||||
Apple Lossless (alac). Once the conversion is complete, original files
|
|
||||||
will be deleted.
|
|
||||||
|
|
||||||
`$ convertMedia -i flac -o alac --delete`
|
|
||||||
|
|
||||||
Search for all 1080p files in a directory and downsize them to 720p.
|
|
||||||
|
|
||||||
`$ convertMedia --downsize720`
|
|
||||||
|
|
||||||
Convert a Windows Media file (file.wmv) to h264 (mp4).
|
|
||||||
|
|
||||||
`$ convertMedia -o mp4 file.wmv`
|
|
||||||
|
|
||||||
I did my best to write good help documentation so simply run `convertMedia -h` for usage information.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[1]: http://unix.stackexchange.com/questions/26047/how-to-correctly-add-a-path-to-path
|
|
||||||
[2]: https://ffmpeg.org
|
|
||||||
[3]: http://tmkk.undo.jp/xld/index_e.html
|
|
||||||
[4]: http://brew.sh
|
|
||||||
965
bin/convertMedia
965
bin/convertMedia
@@ -1,965 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
# My Generic BASH script template
|
|
||||||
#
|
|
||||||
version="2.2.0" # Sets version variable for this script
|
|
||||||
#
|
|
||||||
scriptTemplateVersion="1.5.0" # Version of scriptTemplate.sh that this script is based on
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# HISTORY:
|
|
||||||
#
|
|
||||||
# * 2015-03-31 - v1.0.0 - First creation
|
|
||||||
# * 2015-04-07 - v1.1.0 - Added support for music files
|
|
||||||
# * 2015-05-26 - v1.1.1 - Fixed log level on downsize720
|
|
||||||
# * 2015-08-30 - v1.2.0 - Support for recursive directory conversions
|
|
||||||
# * 2016-01-03 - v1.2.1 - Fixed XLD audio conversion which had been broken
|
|
||||||
# * 2016-01-05 - v2.0.0 - Major refactoring of code.
|
|
||||||
# - Better JSON parsing via JQ
|
|
||||||
# - MIME type discovery based on file metadata (rather than user input)
|
|
||||||
# - Better error handling
|
|
||||||
# - Better renaming of existing files
|
|
||||||
# * 2016-01-09 - v2.0.1 - Fixed bug on in video preset section where 'videoPreset' was malformed
|
|
||||||
# * 2016-01-09 - v2.1.0 - Support for audiobooks with .m4b
|
|
||||||
# - Support for concatenating multiple audio files
|
|
||||||
# - Support for --probe function to output ffprobe data in JSON
|
|
||||||
# * 2016-01-13 - v2.1.1 - Fixed bug with concatenating files
|
|
||||||
# * 2016-01-14 - v2.2.0 - Added 'spoken word' audio profiles
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
# Provide a variable with the location of this script.
|
|
||||||
scriptPath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
# Source Scripting Utilities
|
|
||||||
# -----------------------------------
|
|
||||||
# These shared utilities provide many functions which are needed to provide
|
|
||||||
# the functionality in this boilerplate. This script will fail if they can
|
|
||||||
# not be found.
|
|
||||||
# -----------------------------------
|
|
||||||
|
|
||||||
utilsLocation="${scriptPath}/../lib/utils.sh" # Update this path to find the utilities.
|
|
||||||
|
|
||||||
if [ -f "${utilsLocation}" ]; then
|
|
||||||
source "${utilsLocation}"
|
|
||||||
else
|
|
||||||
echo "Please find the file util.sh and add a reference to it in this script. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# trapCleanup Function
|
|
||||||
# -----------------------------------
|
|
||||||
# Any actions that should be taken if the script is prematurely
|
|
||||||
# exited. Always call this function at the top of your script.
|
|
||||||
# -----------------------------------
|
|
||||||
function trapCleanup() {
|
|
||||||
echo ""
|
|
||||||
# Delete temp files, if any
|
|
||||||
if is_dir "${tmpDir}"; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
die "Exit trapped." # Edit this if you like.
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set Flags
|
|
||||||
# -----------------------------------
|
|
||||||
# Flags which can be overridden by user input.
|
|
||||||
# Default values are below
|
|
||||||
# -----------------------------------
|
|
||||||
quiet=false
|
|
||||||
printLog=false
|
|
||||||
verbose=false
|
|
||||||
recursive=false
|
|
||||||
force=false
|
|
||||||
strict=false
|
|
||||||
debug=false
|
|
||||||
safeRun=0
|
|
||||||
downsize720=0
|
|
||||||
deleteOriginal=false
|
|
||||||
newFileFlag=false
|
|
||||||
videoFoundFlag=false
|
|
||||||
audioFoundFlag=false
|
|
||||||
spokenWord=false
|
|
||||||
probe=false
|
|
||||||
concat=false
|
|
||||||
XLD=0
|
|
||||||
args=()
|
|
||||||
|
|
||||||
# Set Temp Directory
|
|
||||||
# -----------------------------------
|
|
||||||
# Create temp directory with three random numbers and the process ID
|
|
||||||
# in the name. This directory is removed automatically at exit.
|
|
||||||
# -----------------------------------
|
|
||||||
tmpDir="/tmp/${scriptName}.${RANDOM}.${RANDOM}.${RANDOM}.${$}"
|
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
|
||||||
die "Could not create temporary directory! Exiting."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
# -----------------------------------
|
|
||||||
# Log is only used when the '-l' flag is set.
|
|
||||||
#
|
|
||||||
# To never save a logfile change variable to '/dev/null'
|
|
||||||
# Save to Desktop use: $HOME/Desktop/${scriptBasename}.log
|
|
||||||
# Save to standard user log location use: $HOME/Library/Logs/${scriptBasename}.log
|
|
||||||
# -----------------------------------
|
|
||||||
logFile="${HOME}/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
# Check for Dependencies
|
|
||||||
# -----------------------------------
|
|
||||||
# Arrays containing package dependencies needed to execute this script.
|
|
||||||
# The script will fail if dependencies are not installed. For Mac users,
|
|
||||||
# most dependencies can be installed automatically using the package
|
|
||||||
# manager 'Homebrew'. Mac applications will be installed using
|
|
||||||
# Homebrew Casks. Ruby and gems via RVM.
|
|
||||||
# -----------------------------------
|
|
||||||
homebrewDependencies=(ffmpeg jq rename)
|
|
||||||
caskDependencies=(xld)
|
|
||||||
gemDependencies=()
|
|
||||||
|
|
||||||
function mainScript() {
|
|
||||||
############## Begin Script Here ###################
|
|
||||||
####################################################
|
|
||||||
|
|
||||||
# File extension mappings - ie - are we working with a video or music file?
|
|
||||||
videoTypes=(mp4 mov avi mkv wmv flv ogg m4p m4v 3gp divx h264)
|
|
||||||
audioTypes=(mp3 m4a aiff aac m4p wav wma flac m4b)
|
|
||||||
|
|
||||||
function identifyFiles() {
|
|
||||||
local item
|
|
||||||
local maxSearchDepth
|
|
||||||
local fileType
|
|
||||||
local fileMatch
|
|
||||||
|
|
||||||
# Assign user specified files
|
|
||||||
if [ -n "${userFile}" ]; then
|
|
||||||
filesToConvert+=("${userFile}")
|
|
||||||
fi
|
|
||||||
if [ -n "${args}" ]; then
|
|
||||||
filesToConvert+=("${args[@]}")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Respect recursive file searches when requested
|
|
||||||
if ${recursive}; then
|
|
||||||
maxSearchDepth=4
|
|
||||||
else
|
|
||||||
maxSearchDepth=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Assign files based on MEDIATYPE extension
|
|
||||||
if [ -n "${MEDIATYPE}" ]; then
|
|
||||||
while read item; do
|
|
||||||
filesToConvert+=("${item}")
|
|
||||||
done < <(find . -name "*.${MEDIATYPE}" -type f -maxdepth ${maxSearchDepth})
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Figure out what to do if a user doesn't specify any files or input types
|
|
||||||
if [ ${#filesToConvert[@]} -eq 0 ]; then
|
|
||||||
notice "You didn't specify a specific file."
|
|
||||||
seek_confirmation "Do you want to convert all VIDEO files?"
|
|
||||||
if is_confirmed; then
|
|
||||||
for fileType in "${videoTypes[@]}"; do
|
|
||||||
while read fileMatch; do
|
|
||||||
filesToConvert+=("${fileMatch}")
|
|
||||||
done < <(find . -name "*.${fileType}" -type f -maxdepth ${maxSearchDepth})
|
|
||||||
done
|
|
||||||
else
|
|
||||||
seek_confirmation "Do you want to convert all AUDIO files?"
|
|
||||||
if is_confirmed; then
|
|
||||||
for fileType in "${audioTypes[@]}"; do
|
|
||||||
while read fileMatch; do
|
|
||||||
filesToConvert+=("${fileMatch}")
|
|
||||||
done < <(find . -name "*.${fileType}" -type f -maxdepth ${maxSearchDepth})
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Error handling: Ensure the count of elements in ${filesToConvert[@]} is not zero.
|
|
||||||
if [ ${#filesToConvert[@]} -eq 0 ]; then
|
|
||||||
error "We couldn't find any files to act on. Exiting."
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# debug
|
|
||||||
verbose "filesToConvert=${filesToConvert[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
function testFiles() {
|
|
||||||
# This function ensures that the specified files exist and can be converted
|
|
||||||
local extension
|
|
||||||
|
|
||||||
# Ensure we have a valid file
|
|
||||||
if [ ! -f "${fileToTest}" ]; then die "We can't find '${fileToTest}' or it is not valid."; fi
|
|
||||||
|
|
||||||
# Ensure the extension is known to this script
|
|
||||||
extension="${fileToTest##*.}" # Grab file extension of input file
|
|
||||||
extension="$(echo "${extension}" | tr '[:upper:]' '[:lower:]')" # Lowercase the extension if needed
|
|
||||||
|
|
||||||
# See if the extension is in the specified extension mappings
|
|
||||||
if [[ ! "${videoTypes[*]}" =~ ${extension} ]] && [[ ! "${audioTypes[*]}" =~ ${extension} ]]; then
|
|
||||||
error "The extension of '${extension} ' was not recognized."
|
|
||||||
die "We don't know what to do with ${fileToTest}."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertToFormat() {
|
|
||||||
|
|
||||||
# Do things on video files
|
|
||||||
if [[ "${videoTypes[*]}" =~ ${file##*.} ]]; then
|
|
||||||
# if you wanted a default target format for a specific input format,
|
|
||||||
# you would put it here.
|
|
||||||
# Set the default video conversion format to mp4
|
|
||||||
case "${format}" in
|
|
||||||
'Matroska / WebM') outputFormat='mp4' ;;
|
|
||||||
*) outputFormat='mp4' ;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Do things on audio files
|
|
||||||
if [[ "${audioTypes[*]}" =~ ${file##*.} ]]; then
|
|
||||||
|
|
||||||
# Ensure a user sets an output format since we don't have a default.
|
|
||||||
if [[ -z ${userOutput} ]]; then
|
|
||||||
warning "Please specify an output audio format using '-o, --output'. Exiting"
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Confirm if a user wants to convert audio to it's own format
|
|
||||||
# if [[ "${file##*.}" == "${userOutput}" ]]; then
|
|
||||||
# warning "You are attempting to convert a ${file##*.} file to ${userOutput}."
|
|
||||||
# seek_confirmation "Do you want to proceed?"
|
|
||||||
# if is_not_confirmed; then
|
|
||||||
# continue
|
|
||||||
# fi
|
|
||||||
# fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Reads user input for format (-o, --output)
|
|
||||||
if [ -n "${userOutput}" ]; then
|
|
||||||
outputFormat="${userOutput,,}" #&& verbose "outputFormat=${outputFormat}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function concatFiles() {
|
|
||||||
if ${concat}; then
|
|
||||||
# Create variable for ffmpeg to concacenate the files
|
|
||||||
concatConvert="concat:$(join "|" "${filesToConvert[@]}")"
|
|
||||||
# Create the variable for the deleteOriginalFile function
|
|
||||||
concatDelete="$(join " " ${filesToConvert[@]})"
|
|
||||||
|
|
||||||
# Ask the user for the name of the newly created file
|
|
||||||
input "Please enter the name of the new file to be created. [ENTER]: "
|
|
||||||
read concatOutput
|
|
||||||
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseJSON() {
|
|
||||||
local ii
|
|
||||||
local videoFoundFlag
|
|
||||||
local audioFoundFlag
|
|
||||||
local informationFile
|
|
||||||
|
|
||||||
# Create the temporary JSON file
|
|
||||||
informationFile="${tmpDir}/${file////}.json"
|
|
||||||
|
|
||||||
verbose "Reading audio data and writing JSON to tmp"
|
|
||||||
verbose "-> ${informationFile}"
|
|
||||||
|
|
||||||
# Output a JSON file for each video asset being parsed.
|
|
||||||
ffprobe -v quiet -print_format json -show_format -show_streams "${file}" >> "${informationFile}"
|
|
||||||
|
|
||||||
# Debugging: Uncomment either (or both) of these lines
|
|
||||||
# ffprobe -v quiet -print_format json -show_format -show_streams "${file}" >> "${file}.json"
|
|
||||||
# cat "${informationFile}"
|
|
||||||
|
|
||||||
# If the '--probe' flag is set, we print the media file information onto the screen.
|
|
||||||
if "${probe}"; then
|
|
||||||
cat "${informationFile}"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Read the necessary information from the JSON
|
|
||||||
format="$(jq -r ".format.format_long_name" "${informationFile}")"
|
|
||||||
formatName="$(jq -r ".format.format_name" "${informationFile}")"
|
|
||||||
jsonFilename="$(jq -r ".format.filename" "${informationFile}")"
|
|
||||||
formatBit_Rate="$(jq -r ".format.bit_rate" "${informationFile}")"
|
|
||||||
numStreams="$(jq -r ".format.nb_streams" "${informationFile}")"
|
|
||||||
|
|
||||||
# Iterate over results
|
|
||||||
ii=0
|
|
||||||
while [ ${ii} -lt ${numStreams} ]; do
|
|
||||||
if [[ $(jq -r --arg ii ${ii} ".streams[$ii | tonumber].codec_type" "${informationFile}") == "video" ]]; then
|
|
||||||
videoHeight="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].height" "${informationFile}")"
|
|
||||||
videoWidth="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].width" "${informationFile}")"
|
|
||||||
videoCodec="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].codec_name" "${informationFile}")"
|
|
||||||
videoCodecLong="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].codec_long_name" "${informationFile}")"
|
|
||||||
|
|
||||||
videoFoundFlag=true # Used for error handling to confirm we found a video stream
|
|
||||||
fi
|
|
||||||
if [ $(jq -r --arg ii ${ii} ".streams[$ii | tonumber].codec_type" "${informationFile}") == "audio" ]; then
|
|
||||||
audioCodec="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].codec_name" "${informationFile}")"
|
|
||||||
audioCodecLong="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].codec_long_name" "${informationFile}")"
|
|
||||||
audioSampleRate="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].sample_rate" "${informationFile}")"
|
|
||||||
audioBitRate="$(jq -r --arg ii ${ii} ".streams[$ii | tonumber].bit_rate" "${informationFile}")"
|
|
||||||
|
|
||||||
audioFoundFlag=true # Used for error handling to confirm we found an audio stream
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increase loop count
|
|
||||||
ii=$((ii+1))
|
|
||||||
done
|
|
||||||
|
|
||||||
# Error Handling for video files
|
|
||||||
if [[ "${videoTypes[*]}" =~ ${file##*.} ]]; then
|
|
||||||
if [ "${videoFoundFlag}" = "false" ]; then
|
|
||||||
warning "Missing video stream for '${file}'."
|
|
||||||
seek_confirmation "Inspect the file with ffprobe?"
|
|
||||||
if is_confirmed; then
|
|
||||||
ffprobe -v quiet -print_format json -show_format -show_streams "${file}"
|
|
||||||
safeExit
|
|
||||||
else
|
|
||||||
notice "Exiting"
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Error Handling for audio files
|
|
||||||
if [[ "${audioTypes[*]}" =~ ${file##*.} ]]; then
|
|
||||||
if [ "${audioFoundFlag}" = "false" ]; then
|
|
||||||
warning "Missing audio stream for '${file}'."
|
|
||||||
seek_confirmation "Inspect the file with ffprobe?"
|
|
||||||
if is_confirmed; then
|
|
||||||
ffprobe -v quiet -print_format json -show_format -show_streams "${file}"
|
|
||||||
safeExit
|
|
||||||
else
|
|
||||||
notice "Exiting"
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertVideo() {
|
|
||||||
|
|
||||||
verbose "Running 'convertVideo' function"
|
|
||||||
|
|
||||||
# SET AUDIO INFORMATION
|
|
||||||
# Copy audio in compatible formats. Re-encode audio when needed
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
# Pick the best aac audio encoder
|
|
||||||
if ffmpeg -version | grep enable-libfdk-aac >/dev/null; then
|
|
||||||
aacEncoder="libfdk_aac"
|
|
||||||
else
|
|
||||||
aacEncoder="libfaac"
|
|
||||||
fi
|
|
||||||
|
|
||||||
supportedAudioCodecs=(aac ac3 eac3)
|
|
||||||
if [[ "${supportedAudioCodecs[*]}" =~ ${audioCodec} ]]; then
|
|
||||||
videoAudioCommand="-c:a copy"
|
|
||||||
else
|
|
||||||
videoAudioCommand="-c:a ${aacEncoder} -b:a 160k"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# SET VIDEO INFORMATION
|
|
||||||
# Copy video in compatible formats. Re-encode audio when needed.
|
|
||||||
# Set resizing options
|
|
||||||
################################################################
|
|
||||||
|
|
||||||
# Is input video a known preset size?
|
|
||||||
if [[ "${videoWidth}" == "1920" && "${videoHeight}" == "1080" ]] || [[ "${videoWidth}" == "1920" && "${videoHeight}" == "816" ]]; then
|
|
||||||
videoPreset="1080p" && verbose "Input video has preset: 1080p"
|
|
||||||
fi
|
|
||||||
if [[ "${videoWidth}" == "1280" && "${videoHeight}" == "720" ]] || [[ "${videoWidth}" == "1280" && "$videoHeight" == "544" ]]; then
|
|
||||||
videoPreset="720p" && verbose "Input video has preset: 720p"
|
|
||||||
fi
|
|
||||||
if [[ "${videoWidth}" == "720" && "${videoHeight}" == "576" ]]; then
|
|
||||||
videoPreset="DVPAL" && verbose "Input video has preset: DVPAL"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Enable resizing of videos
|
|
||||||
# #############################
|
|
||||||
|
|
||||||
# Fail if user sets more than one value
|
|
||||||
if [[ -n "${videoSize}" ]] && [[ -n "${height}" || -n "${width}" ]]; then
|
|
||||||
die "We can't set a 'height', 'width', and a 'size' at the same time."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if user sets both a height and width, run that as a videoSize variable
|
|
||||||
if [[ -n "${width}" ]] && [[ -n "${height}" ]]; then
|
|
||||||
videoSize="${width}x${height}" && verbose "videoSize=${width}x${height}"
|
|
||||||
unset width
|
|
||||||
unset height
|
|
||||||
fi
|
|
||||||
|
|
||||||
# downsize720
|
|
||||||
# Commonly used function to downsize 1080p to 720p
|
|
||||||
if [[ "${downsize720}" == "1" ]]; then
|
|
||||||
if [[ "${videoPreset}" == "1080p" ]]; then
|
|
||||||
videoSize="hd720" && verbose "videoSize=hd720"
|
|
||||||
else
|
|
||||||
notice "Skipping ${file}. It's not 1080p"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Do something when a user specifies a size
|
|
||||||
if [[ -n "${videoSize}" ]]; then
|
|
||||||
|
|
||||||
# Don't resize videos to their same size
|
|
||||||
if [[ "${videoSize}" == "hd720" ]] || [[ "${videoSize}" == "720" || "${videoSize}" == "1280x720" ]]; then
|
|
||||||
if [[ "$videoPreset" == "720p" ]]; then
|
|
||||||
notice "${file} is already 720p. Skipping...."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
elif [[ "${videoSize}" == "hd1080" || "${videoSize}" == "1080" || "${videoSize}" == "1920x1080" ]]; then
|
|
||||||
if [[ "$videoPreset" == "1080p" ]]; then
|
|
||||||
notice "${file} is already 1080p. Skipping...."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Confirm if user wants to upscale their video
|
|
||||||
if [[ "${videoSize}" == "hd1080" || "${videoSize}" == "1080" ]]; then
|
|
||||||
userWidth="1920"
|
|
||||||
userHeight="1080"
|
|
||||||
elif [[ "${videoSize}" == "hd720" ]] || [[ "${videoSize}" == "720" ]]; then
|
|
||||||
userWidth="1280"
|
|
||||||
userHeight="720"
|
|
||||||
else
|
|
||||||
# break user's video size into a height and width
|
|
||||||
userWidth=$(echo ${videoSize} | cut -f1 -dx)
|
|
||||||
userHeight=$(echo ${videoSize} | cut -f2 -dx)
|
|
||||||
if [ "${userWidth}" -gt "${videoWidth}" ] || [ "${userHeight}" -gt "${videoHeight}" ]; then
|
|
||||||
seek_confirmation "Upscale ${file} to ${videoSize}? It is already ${videoWidth}x${videoHeight}."
|
|
||||||
if is_not_confirmed; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Finally, set the resize variable
|
|
||||||
videoResize="-vf scale=${videoSize}" && verbose "videoResize='-vf scale=${videoSize}'"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Scaling variables
|
|
||||||
# ####################################
|
|
||||||
if is_not_empty "${height}"; then
|
|
||||||
if [ "${height}" -gt "${videoHeight}" ]; then
|
|
||||||
seek_confirmation "Upscale ${file} to height ${height}? It is already ${videoWidth}x${videoHeight}."
|
|
||||||
if is_not_confirmed; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
videoResize="-vf scale=-1:${height}" && verbose "videoResize='-vf scale=-1:${height}'"
|
|
||||||
fi
|
|
||||||
if is_not_empty "${width}"; then
|
|
||||||
if [ "${width}" -gt "${videoWidth}" ]; then
|
|
||||||
seek_confirmation "Upscale ${file} to width ${width}? It is already ${videoWidth}x${videoHeight}."
|
|
||||||
if is_not_confirmed; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
videoResize="-vf scale=${width}:-1" && verbose "videoResize='-vf scale=${width}:-1'"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Copy h264 when possible
|
|
||||||
# Save precious time by not re-encoding files that are already H264.
|
|
||||||
# ###########################
|
|
||||||
if [[ "${videoCodec}" == "h264" ]] && [[ -z "${videoResize}" ]]; then
|
|
||||||
videoCommand="-c:v copy" && verbose "videoCommand='-c:v copy'"
|
|
||||||
else
|
|
||||||
videoCommand="-c:v libx264 -crf 18 -preset slow" && verbose "videoCommand'-c:v libx264 -crf 18 -preset slow'"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Don't convert to self if no other options set
|
|
||||||
if [[ -z ${height} && -z ${width} && -z ${videoSize} && ${downsize720} == "0" && "${outputFormat}" == "${file##*.}" ]]; then
|
|
||||||
error "Can't convert a '${file##*.}' file to itself. Skipping ${file}"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertAudio() {
|
|
||||||
|
|
||||||
verbose "Running 'convertAudio' function"
|
|
||||||
# note on XLD: If you have XLD installed and configured, lossless audio conversion
|
|
||||||
# will be run using built-in XLD profiles. You can disable this by
|
|
||||||
# changing ensuring that XLD=0 in sections below.
|
|
||||||
#
|
|
||||||
# #############################################################
|
|
||||||
|
|
||||||
# Build the Conversion Command
|
|
||||||
# ########################################
|
|
||||||
|
|
||||||
# Set mono/64k conversion for audiobooks
|
|
||||||
if ${spokenWord}; then
|
|
||||||
monoConversion="-ab 96k -ac 1"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${userOutput,,}" == "alac" ]]; then
|
|
||||||
if type_exists "xld"; then
|
|
||||||
XLD=1
|
|
||||||
#audioConvertCommand="--profile FLACtoALAC"
|
|
||||||
audioConvertCommand="-f alac"
|
|
||||||
outputFormat="m4a"
|
|
||||||
else
|
|
||||||
audioConvertCommand="-acodec alac"
|
|
||||||
fi
|
|
||||||
elif [[ "${userOutput,,}" == "flac" ]]; then
|
|
||||||
if type_exists "xld"; then
|
|
||||||
XLD=1
|
|
||||||
audioConvertCommand="-f flac"
|
|
||||||
else
|
|
||||||
audioConvertCommand="-c:a flac ${monoConversion}"
|
|
||||||
fi
|
|
||||||
elif [[ "${userOutput,,}" == "aac" || "${userOutput,,}" == "m4a" ]]; then
|
|
||||||
outputFormat="m4a"
|
|
||||||
# Pick the best aac audio encoder
|
|
||||||
if ffmpeg -version | grep enable-libfdk-aac >/dev/null; then
|
|
||||||
# set variable bit rate to '5', the highest unless we are doing spoken word
|
|
||||||
if ${spokenWord}; then
|
|
||||||
aacEncoder='libfdk_aac'
|
|
||||||
else
|
|
||||||
aacEncoder='libfdk_aac -vbr 5'
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
aacEncoder='libfaac -q:a 400'
|
|
||||||
fi
|
|
||||||
if type_exists "xlds"; then
|
|
||||||
XLD=1
|
|
||||||
audioConvertCommand="-f aac" && verbose "using xld. audioConvertCommand = -f aac "
|
|
||||||
else
|
|
||||||
audioConvertCommand="-acodec ${aacEncoder} ${monoConversion}"
|
|
||||||
fi
|
|
||||||
elif [[ "${userOutput,,}" == "mp3" ]]; then
|
|
||||||
# Can we convert to mp3? Do we have an ffmpeg encoder?
|
|
||||||
if ffmpeg -version | grep enable-libmp3lame >/dev/null; then
|
|
||||||
mp3Encoder='libmp3lame'
|
|
||||||
# else
|
|
||||||
# warning "No workable ffmpeg mp3 encoder. Skipping ${f}..."
|
|
||||||
# continue
|
|
||||||
fi
|
|
||||||
# Take user specified bitrate
|
|
||||||
if [ -n "$bitrate" ]; then
|
|
||||||
bitrate="${bitrate%k}k" # Ensure 'k' is at the end of any bitrate sent to ffmpeg
|
|
||||||
ffmpegBitrate="-ab ${bitrate}"
|
|
||||||
else
|
|
||||||
ffmpegBitrate="-qscale:a 0"
|
|
||||||
fi
|
|
||||||
# Set mono conversion for audiobooks
|
|
||||||
if ${spokenWord}; then
|
|
||||||
ffmpegBitrate="${monoConversion}"
|
|
||||||
fi
|
|
||||||
audioConvertCommand="-acodec ${mp3Encoder} ${ffmpegBitrate} -map_metadata 0 -id3v2_version 3"
|
|
||||||
elif [[ "${userOutput,,}" == "m4b" ]]; then
|
|
||||||
# m4b is exactly the same as m4a except it tells Apple that the file is an audiobook.
|
|
||||||
# so we use m4a conversion here and then rename the output file to m4b
|
|
||||||
# The main difference here is that we make the assumption that audiobooks don't
|
|
||||||
# need high fidelity or stereo so we make them mono and low bit-rate.
|
|
||||||
|
|
||||||
# Pick the best aac audio encoder
|
|
||||||
if ffmpeg -version | grep enable-libfdk-aac >/dev/null; then
|
|
||||||
# set variable bit rate to '5', the highest
|
|
||||||
aacEncoder="libfdk_aac ${monoConversion} -f m4a"
|
|
||||||
else
|
|
||||||
aacEncoder="libfaac ${monoConversion} -f m4a"
|
|
||||||
fi
|
|
||||||
audioConvertCommand="-acodec ${aacEncoder}"
|
|
||||||
else
|
|
||||||
warning "We don't know what to do with audio format: '${outputFormat}'."
|
|
||||||
warning "Exiting"
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function setOutputDirectory() {
|
|
||||||
if ${verbose}; then v="-v" ; fi
|
|
||||||
|
|
||||||
# Use the user's specified directory to save the new file in if specified.
|
|
||||||
# default to use the location of the original file.
|
|
||||||
if [[ -n "${saveDir}" ]]; then
|
|
||||||
if [[ -e "${saveDir}" && ! -d "${saveDir}" ]]; then
|
|
||||||
die "${saveDir} exists but is not a directory"
|
|
||||||
fi
|
|
||||||
if [[ ! -d "${saveDir}" ]]; then
|
|
||||||
seek_confirmation "${saveDir} does not exist. Create?"
|
|
||||||
if is_confirmed; then
|
|
||||||
mkdir ${v} "${saveDir}"
|
|
||||||
else
|
|
||||||
die "Can't run without a place to save the files."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# remove trailing slash if included. Add back to ensure it's always there.
|
|
||||||
outputDir="${saveDir%/}/"
|
|
||||||
else
|
|
||||||
outputDir=$(dirname "${file}")
|
|
||||||
outputDir="${outputDir#*/}"
|
|
||||||
outputDir="${outputDir%/}/"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function setOutputFile() {
|
|
||||||
|
|
||||||
# This function creates the name of new file to be generated by the conversion
|
|
||||||
# ##################
|
|
||||||
|
|
||||||
# Set output filename
|
|
||||||
output="$(basename "${file%.*}").${outputFormat}"
|
|
||||||
# Add the output directory
|
|
||||||
output="${outputDir}${output}"
|
|
||||||
|
|
||||||
#Override the output file to the user's input if we are concatenating files
|
|
||||||
if [ -n "$concatOutput" ]; then
|
|
||||||
output="${outputDir}${concatOutput%.*}.${outputFormat}"
|
|
||||||
success "output=${output}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Confirm we're not overwriting an existing file
|
|
||||||
# If we are, append '.new' to the name
|
|
||||||
fileFlag=0
|
|
||||||
while [[ fileFlag -eq 0 ]]; do
|
|
||||||
if [ -e "${output}" ]; then
|
|
||||||
# output="$(basename "${file%.*}").new."${outputFormat}""
|
|
||||||
output="$(basename "${output%.*}").new.${outputFormat}"
|
|
||||||
# Add the directory back to the file
|
|
||||||
output="${outputDir}${output}"
|
|
||||||
# Set a flag so that we can rename this file back to the original name if
|
|
||||||
# a user elects to delete the original file
|
|
||||||
newFileFlag=true
|
|
||||||
else
|
|
||||||
fileFlag=1
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
verbose "new file name will be: ${output}"
|
|
||||||
}
|
|
||||||
|
|
||||||
function doConvert() {
|
|
||||||
verbose "running doConvert function"
|
|
||||||
|
|
||||||
if ${verbose}; then v="-v" ; fi
|
|
||||||
|
|
||||||
# Respect the 'Quiet' flag
|
|
||||||
if "${quiet}"; then
|
|
||||||
verbose "running in quiet mode"
|
|
||||||
ffquiet="-loglevel quiet"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# When concatenating files, we need a different $file variable
|
|
||||||
if ${concat}; then
|
|
||||||
file="${concatConvert}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Respect the 'logfile' flag
|
|
||||||
if ${printLog}; then ffmpegLog=">> ${logFile}"; fi
|
|
||||||
|
|
||||||
# Use XLD for audio file conversion if available
|
|
||||||
if [[ "${XLD}" == "1" && "${spokenWord}" == "false" ]]; then
|
|
||||||
verbose "Running XLD commands for audio. No FFMPEG."
|
|
||||||
# Respect --safe flag.
|
|
||||||
if [[ "${safeRun}" == "1" ]]; then
|
|
||||||
notice "xld -o "${output} ${audioConvertCommand} ${file}
|
|
||||||
else
|
|
||||||
verbose "xld -o ${output} ${audioConvertCommand} ${file}"
|
|
||||||
xld -o "${output}" "${audioConvertCommand}" "${file}"
|
|
||||||
fi
|
|
||||||
else # Use ffmpeg when XLD is set to 0
|
|
||||||
# Respect --safe flag.
|
|
||||||
if [[ "${safeRun}" == "1" ]]; then
|
|
||||||
notice "ffmpeg -i ${file} ${videoResize} ${videoCommand} ${videoAudioCommand} ${audioConvertCommand} ${output} ${ffquiet}"
|
|
||||||
else
|
|
||||||
verbose "ffmpeg -i ${file} ${videoResize} ${videoCommand} ${videoAudioCommand} ${audioConvertCommand} ${output} ${ffquiet}"
|
|
||||||
ffmpeg -i "${file}" ${videoResize} ${videoCommand} ${videoAudioCommand} ${audioConvertCommand} "${output}" ${ffquiet}
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Unset variables to get ready for the next file
|
|
||||||
unset jsonFilename
|
|
||||||
unset formatBit_Rate
|
|
||||||
unset numStreams
|
|
||||||
unset videoCodec
|
|
||||||
unset videoCodecLong
|
|
||||||
unset format
|
|
||||||
unset formatName
|
|
||||||
unset videoHeight
|
|
||||||
unset videoWidth
|
|
||||||
unset videoPreset
|
|
||||||
unset audioCodec
|
|
||||||
unset audioCodecLong
|
|
||||||
unset audioSampleRate
|
|
||||||
unset audioBitRate
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteOriginalFile() {
|
|
||||||
if ${verbose}; then v="-v" ; fi
|
|
||||||
|
|
||||||
# first, ensure we don't delete the originals if we're in a safe rune
|
|
||||||
if [[ "${safeRun}" == "0" ]]; then
|
|
||||||
if ${deleteOriginal}; then
|
|
||||||
if ${concat}; then
|
|
||||||
for fileToDelete in "${filesToConvert[@]}"; do
|
|
||||||
rm -f ${v} "${fileToDelete}"
|
|
||||||
done
|
|
||||||
else
|
|
||||||
rm -f ${v} "${file}"
|
|
||||||
if [[ "${file##*.}" == "${outputFormat}" ]]; then
|
|
||||||
mv ${v} "${output}" "${outputDir}${file}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# break the loop if we're concatenating files.
|
|
||||||
if ${concat}; then
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
## RUN THE SCRIPT ##
|
|
||||||
####################################
|
|
||||||
|
|
||||||
# First we need to identify the files
|
|
||||||
identifyFiles
|
|
||||||
|
|
||||||
# Then we test that all files can be operated upon
|
|
||||||
for fileToTest in "${filesToConvert[@]}"; do testFiles; done
|
|
||||||
|
|
||||||
# Then we respect the '--probe' option if requested
|
|
||||||
if "${probe}"; then
|
|
||||||
for file in "${filesToConvert[@]}"; do
|
|
||||||
info "Probing ${file}"
|
|
||||||
verbose "ffprobe -v quiet -print_format json -show_format -show_streams ${file}"
|
|
||||||
parseJSON
|
|
||||||
done
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Then we work on the individual files. This is the fun part.
|
|
||||||
for file in "${filesToConvert[@]}"; do
|
|
||||||
if ! ${concat}; then info "Working on ${file}"; fi
|
|
||||||
# First we grab the metadata of the file and assign it to variables
|
|
||||||
parseJSON
|
|
||||||
# Then we set the expected output format
|
|
||||||
convertToFormat
|
|
||||||
# Then we set the appropriate conversion commands
|
|
||||||
if [[ "${videoTypes[*]}" =~ "$(echo ${file##*.} | tr '[:upper:]' '[:lower:]')" ]]; then convertVideo; fi
|
|
||||||
if [[ "${audioTypes[*]}" =~ "$(echo ${file##*.} | tr '[:upper:]' '[:lower:]')" ]]; then convertAudio; fi
|
|
||||||
# Then we tell the script where to output the file
|
|
||||||
setOutputDirectory
|
|
||||||
# Then we check if we are supposed to concatenate the files
|
|
||||||
concatFiles
|
|
||||||
# Then we generate the name for the new file
|
|
||||||
setOutputFile
|
|
||||||
# Then we actually do the conversion
|
|
||||||
doConvert
|
|
||||||
# Then we delete the original file (if requested)
|
|
||||||
deleteOriginalFile
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
####################################################
|
|
||||||
############### End Script Here ####################
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
usage() {
|
|
||||||
# Print usage
|
|
||||||
echo -n "${scriptName} ${version} [OPTION]... [ARGUMENT]...
|
|
||||||
|
|
||||||
${bold}DESCRIPTION${reset}
|
|
||||||
This is a media conversion script which converts audio and video into many
|
|
||||||
different formats. It was written to eliminate the need to remember specific
|
|
||||||
FFMPEG commands. All conversions can be performed with ffmpeg. XLD on a mac
|
|
||||||
is used for audio conversions when available.
|
|
||||||
|
|
||||||
${BOLD}DEPENDENCIES${reset}
|
|
||||||
This script makes heavy use of shared functions contained in ${bold}lib/utils.sh${reset} which are
|
|
||||||
available as part of the same Github repository. It will fail if these are not found.
|
|
||||||
|
|
||||||
This script relies on ${bold}ffmpeg${reset} for video and audio conversion as well as ${bold}jq${reset}
|
|
||||||
for parsing JSON files. These must be installed prior to usage. If run on a
|
|
||||||
mac, the script will attempt to help you install these packages using ${bold}Homebrew${reset}.
|
|
||||||
|
|
||||||
${bold}General Options:${reset}
|
|
||||||
${bold}-h, --help${reset} Display this help and exit
|
|
||||||
${bold}-d, --debug${reset} Runs script in BASH debug mode ('set -x')
|
|
||||||
${bold}--force${reset} Skip all user interaction. Implied 'Yes' to all actions.
|
|
||||||
${bold}-l, --log${reset} Print log to file
|
|
||||||
${bold}-q, --quiet${reset} Quiet (no output)
|
|
||||||
${bold}-v, --verbose${reset} Output more information. (Items echoed to 'verbose')
|
|
||||||
${bold} --safe${reset} Runs the script without actually invoking FFMPEG. Will simply print
|
|
||||||
the FFMPEG commands to the terminal
|
|
||||||
${bold}--version${reset} Output version information and exit
|
|
||||||
${bold}--recursive${reset} Will search recursively through directories
|
|
||||||
${bold}--probe${reset} Outputs file metadata via ffprobe in JSON format. Does no coversion.
|
|
||||||
${bold}--concat${reset} Will concatenate audio files together into a single output. Good for audiobooks.
|
|
||||||
${bold}--spoken${reset} Will convert audio files to mono, 64k. Good for audiobooks
|
|
||||||
|
|
||||||
|
|
||||||
${bold}File Options:${reset}
|
|
||||||
${bold}-f, --file${reset} Specify a specific file to take actions on.
|
|
||||||
${bold}-i, --input${reset} Specify the specific media type to search for and take action
|
|
||||||
on. (mov', 'mp4', 'mp3')
|
|
||||||
${bold}-o, --output${reset} Specify the output format for the file(s) to be converted to.
|
|
||||||
('mkv', 'mp4', 'm4a')
|
|
||||||
${bold}--delete ${reset} Delete the original file after conversion.
|
|
||||||
${bold}--saveDir${reset} Specify a folder for the converted files to be saved to. Defaults to
|
|
||||||
the directory the script is invoked in.
|
|
||||||
|
|
||||||
${bold}Video Specific Options:${reset}
|
|
||||||
For video files, if no output format is specified, the script will attempt to convert every
|
|
||||||
video file it finds in the directory into h264 .mp4 format.
|
|
||||||
|
|
||||||
${bold}--size${reset} Set the size of the target file. Can be one of 'hd1080', 'hd720',
|
|
||||||
or 'HeightxWidth' (ie. 1920x1080)
|
|
||||||
${bold}--height${reset} Set a height in pixels to scale the target file.
|
|
||||||
${bold}--width${reset} Set a width in pixels to scale the target file.
|
|
||||||
${bold}--downsize720${reset} Searches for 1080p videos and downsizes them to 720p. No need
|
|
||||||
for other scaling options. Used to reduce the size of video collections
|
|
||||||
when quality is not the primary need.
|
|
||||||
|
|
||||||
${bold}Audio Specific Options:${reset}
|
|
||||||
${bold}--bitrate${reset} Set a bit rate for audio conversions. Note, this does not
|
|
||||||
effect video conversions.
|
|
||||||
|
|
||||||
${bold}EXAMPLES:${reset}
|
|
||||||
Search for all *.flac files in a directory and convert them to
|
|
||||||
Apple Lossless (alac). Once the conversion is complete, original files
|
|
||||||
will be deleted.
|
|
||||||
|
|
||||||
$ convertMedia -i flac -o alac --delete
|
|
||||||
|
|
||||||
Search for all 1080p files in a directory and downsize them to 720p.
|
|
||||||
|
|
||||||
$ convertMedia --downsize720
|
|
||||||
|
|
||||||
Convert a Windows Media file (file.wmv) to h264 (mp4).
|
|
||||||
|
|
||||||
$ convertMedia -o mp4 file.wmv
|
|
||||||
|
|
||||||
Do a recursive search for all directories beneath the current one. In each
|
|
||||||
directory, search for .avi files and convert them to .mp4
|
|
||||||
|
|
||||||
$ convertMedia -o mp4 -i avi --recursive
|
|
||||||
|
|
||||||
Create an Apple audiobook (m4b) from all mp3 files in a directory
|
|
||||||
|
|
||||||
$ convertMedia -i mp3 -o m4b --concat
|
|
||||||
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
|
||||||
# --foo bar
|
|
||||||
optstring=h
|
|
||||||
unset options
|
|
||||||
while (($#)); do
|
|
||||||
case $1 in
|
|
||||||
# If option is of type -ab
|
|
||||||
-[!-]?*)
|
|
||||||
# Loop over each character starting with the second
|
|
||||||
for ((i=1; i < ${#1}; i++)); do
|
|
||||||
c=${1:i:1}
|
|
||||||
|
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
|
||||||
# the rest of the string its argument
|
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
|
||||||
options+=("${1:i+1}")
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
|
|
||||||
# If option is of type --foo=bar
|
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
|
||||||
# add --endopts for --
|
|
||||||
--) options+=(--endopts) ;;
|
|
||||||
# Otherwise, nothing special
|
|
||||||
*) options+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
set -- "${options[@]}"
|
|
||||||
unset options
|
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# [[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
|
||||||
while [[ ${1} = -?* ]]; do
|
|
||||||
case ${1} in
|
|
||||||
|
|
||||||
-f|--file) shift; userFile="$1" ;;
|
|
||||||
-i|--input) shift; MEDIATYPE="$1" ;;
|
|
||||||
-o|--output) shift; userOutput="$1" ;;
|
|
||||||
--safe) safeRun=1 ;;
|
|
||||||
--size) shift; videoSize="$1" ;;
|
|
||||||
--height) shift; height="$1" ;;
|
|
||||||
--width) shift; width="$1" ;;
|
|
||||||
--probe) probe=true; safeRun=1 ;;
|
|
||||||
--downsize720) downsize720=1 ;;
|
|
||||||
--recursive) recursive=true ;;
|
|
||||||
--delete) deleteOriginal=true ;;
|
|
||||||
--concat) concat=true; ;;
|
|
||||||
--spoken) spokenWord=true ;;
|
|
||||||
--saveDir) shift; saveDir="$1" ;;
|
|
||||||
--bitrate) shift; bitrate="$1" ;;
|
|
||||||
-h|--help) usage >&2; safeExit ;;
|
|
||||||
--force) force=true ;;
|
|
||||||
--version) echo "$(basename $0) $version"; safeExit ;;
|
|
||||||
-v|--verbose) verbose=true ;;
|
|
||||||
-l|--log) printLog=true ;;
|
|
||||||
-q|--quiet) quiet=true ;;
|
|
||||||
-d|--debug) debug=true;;
|
|
||||||
--endopts) shift; break ;;
|
|
||||||
*) die "invalid option: '$1'." ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Store the remaining part as arguments.
|
|
||||||
args+=("$@")
|
|
||||||
|
|
||||||
# ############# ############# #############
|
|
||||||
# ## TIME TO RUN THE SCRIPT ##
|
|
||||||
# ## ##
|
|
||||||
# ## You shouldn't need to edit anything ##
|
|
||||||
# ## beneath this line ##
|
|
||||||
# ## ##
|
|
||||||
# ############# ############# #############
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
|
||||||
trap trapCleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Set IFS to preferred implementation
|
|
||||||
IFS=$' \n\t'
|
|
||||||
|
|
||||||
# Exit on error. Append '||true' when you run the script if you expect an error.
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
# Run in debug mode, if set
|
|
||||||
if ${debug}; then set -x ; fi
|
|
||||||
|
|
||||||
# Exit on empty variable
|
|
||||||
if ${strict}; then set -o nounset ; fi
|
|
||||||
|
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Invoke the checkDependenices function to test for Bash packages. Uncomment if needed.
|
|
||||||
checkDependencies
|
|
||||||
|
|
||||||
# Run your script
|
|
||||||
mainScript
|
|
||||||
|
|
||||||
# Exit cleanly
|
|
||||||
safeExit
|
|
||||||
547
bin/trash
547
bin/trash
@@ -1,547 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
function mainScript() {
|
|
||||||
local user
|
|
||||||
local uid
|
|
||||||
local finder_pid
|
|
||||||
|
|
||||||
user=$(whoami)
|
|
||||||
uid=$(id -u "$user")
|
|
||||||
finder_pid=$(ps -u "$user" | grep /System/Library/CoreServices/Finder.app | grep -v grep | awk '{print $1}')
|
|
||||||
|
|
||||||
if ${verbose}; then v="-v"; fi
|
|
||||||
|
|
||||||
function have_scriptable_finder() {
|
|
||||||
# Determine whether we can script the Finder or not
|
|
||||||
# We must have a valid PID for Finder, plus we cannot be in
|
|
||||||
# `screen` (another thing that's broken)
|
|
||||||
if [[ (${finder_pid} -gt 1) && ("$STY" == "") ]]; then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
false
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function realpath() {
|
|
||||||
# Convert a relative path to an absolute path.
|
|
||||||
#
|
|
||||||
# From http://github.com/morgant/realpath
|
|
||||||
#
|
|
||||||
# @param string the string to converted from a relative path to an absolute path
|
|
||||||
# @returns Outputs the absolute path to STDOUT, returns 0 if successful or 1 if
|
|
||||||
# an error (esp. path not found).
|
|
||||||
local successPath=true
|
|
||||||
local path="$1"
|
|
||||||
|
|
||||||
# make sure the string isn't empty as that implies something in further logic
|
|
||||||
if [ -z "$path" ]; then
|
|
||||||
successPath=false
|
|
||||||
else
|
|
||||||
# start with the file name (sans the trailing slash)
|
|
||||||
path="${path%/}"
|
|
||||||
|
|
||||||
# if we stripped off the trailing slash and were left with nothing, that means we're in the root directory
|
|
||||||
if [ -z "$path" ]; then
|
|
||||||
path="/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# get the basename of the file (ignoring '.' & '..', because they're really part of the path)
|
|
||||||
local file_basename="${path##*/}"
|
|
||||||
if [[ ( "$file_basename" = "." ) || ( "$file_basename" = ".." ) ]]; then
|
|
||||||
file_basename=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
# extracts the directory component of the full path, if it's empty then assume '.' (the current working directory)
|
|
||||||
local directory="${path%$file_basename}"
|
|
||||||
if [ -z "$directory" ]; then
|
|
||||||
directory='.'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# attempt to change to the directory
|
|
||||||
if ! cd "${directory}" &>/dev/null ; then
|
|
||||||
successPath=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ${success}; then
|
|
||||||
# does the filename exist?
|
|
||||||
if [[ ( -n "${file_basename}" ) && ( ! -e "${file_basename}" ) ]]; then
|
|
||||||
successPath=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
# get the absolute path of the current directory & change back to previous directory
|
|
||||||
local abs_path="$(pwd -P)"
|
|
||||||
cd "-" &>/dev/null
|
|
||||||
|
|
||||||
# Append base filename to absolute path
|
|
||||||
if [ "${abs_path}" = "/" ]; then
|
|
||||||
abs_path="${abs_path}${file_basename}"
|
|
||||||
else
|
|
||||||
abs_path="${abs_path}/${file_basename}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# output the absolute path
|
|
||||||
echo "${abs_path}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
${successPath}
|
|
||||||
}
|
|
||||||
|
|
||||||
function listTrash() {
|
|
||||||
local num_volumes
|
|
||||||
local total_blocks
|
|
||||||
local blocks
|
|
||||||
local size
|
|
||||||
|
|
||||||
num_volumes=0
|
|
||||||
total_blocks=0
|
|
||||||
|
|
||||||
notice "Listing items in Trash"
|
|
||||||
|
|
||||||
# list file contents & calculate size for user's .Trash folder
|
|
||||||
if find "/Users/${user}/.Trash" -depth 1 ! -depth 0; then
|
|
||||||
num_volumes=$(( num_volumes + 1 ))
|
|
||||||
blocks=$(du -cs "/Users/${user}/.Trash" | tail -n 1 | cut -f 1)
|
|
||||||
total_blocks=$(( total_blocks + blocks ))
|
|
||||||
fi
|
|
||||||
# list file contents & calculate size for volume-specific .Trashes folders
|
|
||||||
for file in /Volumes/*; do
|
|
||||||
if [ -d "$file" ]; then
|
|
||||||
folder="${file}/.Trashes/${uid}"
|
|
||||||
if [ -d "${folder}" ]; then
|
|
||||||
if find "${folder}" -depth 1 ! -depth 0; then
|
|
||||||
num_volumes=$(( num_volumes + 1 ))
|
|
||||||
blocks=$(du -cs "${folder}" | tail -n 1 | cut -f 1)
|
|
||||||
total_blocks=$(( total_blocks + blocks ))
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
# convert blocks to human readable size
|
|
||||||
size=0
|
|
||||||
if (( total_blocks >= 2097152 )); then
|
|
||||||
size=$(bc <<< "scale=2; ${total_blocks} / 2097152")
|
|
||||||
size="${size}GB"
|
|
||||||
elif (( total_blocks >= 2048 )); then
|
|
||||||
size=$(bc <<< "scale=2; ${total_blocks} / 2048")
|
|
||||||
size="${size}MB"
|
|
||||||
else
|
|
||||||
size=$(bc <<< "scale=2; ${total_blocks} / 2")
|
|
||||||
size="${size}K"
|
|
||||||
fi
|
|
||||||
info "${size} across ${num_volumes} volume(s)."
|
|
||||||
}
|
|
||||||
|
|
||||||
function emptyTheTrash() {
|
|
||||||
# Determine if we can tell Finder to empty trash via AppleScript
|
|
||||||
if have_scriptable_finder; then
|
|
||||||
notice "Telling Finder to empty trash..."
|
|
||||||
if /usr/bin/osascript -e "tell application \"Finder\" to empty trash" ; then
|
|
||||||
success "Trash has been emptied"
|
|
||||||
safeExit
|
|
||||||
else
|
|
||||||
die "Unable to empty trash"
|
|
||||||
fi
|
|
||||||
# If Finder isn't scriptable, we'll manually empty the trash ourselves
|
|
||||||
else
|
|
||||||
# Confirm that the user wants to empty the trash
|
|
||||||
seek_confirmation "Are you sure you want to empty the trash (this cannot be undone)?"
|
|
||||||
if is_confirmed; then
|
|
||||||
notice "Emptying trash..."
|
|
||||||
# delete the contents of user's .Trash folder
|
|
||||||
find "/Users/${user}/.Trash" -depth 1 ! -depth 0 -print0 | xargs -0 rm $v -r
|
|
||||||
# delete the contents of the volume-specific .Trashes folders
|
|
||||||
for file in /Volumes/*; do
|
|
||||||
if [ -d "${file}" ]; then
|
|
||||||
folder="${file}/.Trashes/${uid}"
|
|
||||||
if [ -d "${folder}" ]; then
|
|
||||||
find "${folder}" -depth 1 ! -depth 0 -print0 | xargs -0 rm $v -r
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
success "Trash has been emptied"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function secureEmptyTheTrash() {
|
|
||||||
# determine if we can tell Finder to securely empty trash via AppleScript
|
|
||||||
if have_scriptable_finder; then
|
|
||||||
notice "Telling Finder to securely empty trash... "
|
|
||||||
if /usr/bin/osascript -e "tell application \"Finder\" to empty trash with security" ; then
|
|
||||||
success "Trash has been securely emptied."
|
|
||||||
safeExit
|
|
||||||
else
|
|
||||||
die "Could not empty trash."
|
|
||||||
fi
|
|
||||||
# if Finder isn't scriptable, we'll manually empty the trash ourselves
|
|
||||||
else
|
|
||||||
# confirm that the user wants to securely empty the trash
|
|
||||||
seek_confirmation "Are you sure you want to securely empty the trash (this REALLY cannot be undone)?"
|
|
||||||
if is_confirmed; then
|
|
||||||
# securely delete the contents of user's .Trash folder
|
|
||||||
find "/Users/${user}/.Trash" -depth 1 ! -depth 0 -print0 | xargs -0 srm $v -r
|
|
||||||
# securely delete the contents of the volume-specific .Trashes folders
|
|
||||||
for file in /Volumes/*; do
|
|
||||||
if [ -d "$file" ]; then
|
|
||||||
folder="${file}/.Trashes/${uid}"
|
|
||||||
if [ -d "${folder}" ]; then
|
|
||||||
find "${folder}" -depth 1 ! -depth 0 -print0 | xargs -0 srm $v -r
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
success "Trash has been securely emptied."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function trashAFile() {
|
|
||||||
# Iterate over all files passed by user
|
|
||||||
for userFile in "${args[@]}"; do
|
|
||||||
if [ ! -e "${userFile}" ]; then
|
|
||||||
warning "${userFile}: No such file or directory"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# determine if we'll tell Finder to trash the file via AppleScript (very easy, plus free undo
|
|
||||||
# support, but Finder must be running for the user and is DOES NOT work from within `screen`)
|
|
||||||
if have_scriptable_finder; then
|
|
||||||
# determine whether we have an absolute path name to the file or not
|
|
||||||
if [ "${userFile:0:1}" = "/" ]; then
|
|
||||||
local file="${userFile}"
|
|
||||||
else
|
|
||||||
# expand relative to absolute path
|
|
||||||
verbose "Determining absolute path for '${userFile}'... "
|
|
||||||
file="$(realpath "${userFile}")"
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
warning "Could not determine absolute path for '${userFile}'!"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
verbose "Telling Finder to trash '${file}'..."
|
|
||||||
if /usr/bin/osascript -e "tell application \"Finder\" to delete POSIX file \"$file\"" &>/dev/null; then
|
|
||||||
success "'${userFile}' moved to trash"
|
|
||||||
else
|
|
||||||
warning "'${userFile}' not moved to trash"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# Finder isn't available for this user, so don't rely on it (we'll do all the dirty work ourselves)
|
|
||||||
else
|
|
||||||
local trash="/Users/${user}/.Trash/"
|
|
||||||
# create the trash folder if necessary
|
|
||||||
if [ ! -d "${trash}" ]; then
|
|
||||||
mkdir ${v} "${trash}"
|
|
||||||
fi
|
|
||||||
# move the file to the trash
|
|
||||||
if [ ! -e "${trash}${userFile}" ]; then
|
|
||||||
mv ${v} "${userFile}" "${trash}"
|
|
||||||
else
|
|
||||||
# determine if the filename has an extension
|
|
||||||
ext=false
|
|
||||||
case "${ }" in
|
|
||||||
*.*) ext=true ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# keep incrementing a number to append to the filename to mimic Finder
|
|
||||||
local i=1
|
|
||||||
if $ext; then
|
|
||||||
new="${trash}${userFile%%.*} ${i}.${userFile##*.}"
|
|
||||||
else
|
|
||||||
new="${trash}${userFile} ${i}"
|
|
||||||
fi
|
|
||||||
while [ -e "${new}" ]; do
|
|
||||||
((i=$i + 1))
|
|
||||||
if ${ext}; then
|
|
||||||
new="${trash}${userFile%%.*} ${i}.${userFile##*.}"
|
|
||||||
else
|
|
||||||
new="${trash}${userFile} ${i}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
#move the file to the trash with the new name
|
|
||||||
mv ${v} "${userFile}" "${new}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
if ${list}; then listTrash; safeExit; fi
|
|
||||||
if ${emptyTrash}; then emptyTheTrash; safeExit; fi
|
|
||||||
if ${secureEmpty}; then secureEmptyTheTrash; safeExit; fi
|
|
||||||
# Default behavior without flags is to delete a file
|
|
||||||
trashAFile
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function trapCleanup() {
|
|
||||||
# trapCleanup Function
|
|
||||||
# -----------------------------------
|
|
||||||
# Any actions that should be taken if the script is prematurely
|
|
||||||
# exited. Always call this function at the top of your script.
|
|
||||||
# -----------------------------------
|
|
||||||
echo ""
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
die "Exit trapped."
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeExit() {
|
|
||||||
# safeExit
|
|
||||||
# -----------------------------------
|
|
||||||
# Non destructive exit for when script exits naturally.
|
|
||||||
# Usage: Add this function at the end of every script.
|
|
||||||
# -----------------------------------
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
trap - INT TERM EXIT
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set Flags
|
|
||||||
# -----------------------------------
|
|
||||||
# Flags which can be overridden by user input.
|
|
||||||
# Default values are below
|
|
||||||
# -----------------------------------
|
|
||||||
list=false
|
|
||||||
emptyTrash=false
|
|
||||||
secureEmpty=false
|
|
||||||
|
|
||||||
quiet=false
|
|
||||||
printLog=false
|
|
||||||
verbose=false
|
|
||||||
force=false
|
|
||||||
strict=false
|
|
||||||
debug=false
|
|
||||||
args=()
|
|
||||||
|
|
||||||
# Set Temp Directory
|
|
||||||
# -----------------------------------
|
|
||||||
# Create temp directory with three random numbers and the process ID
|
|
||||||
# in the name. This directory is removed automatically at exit.
|
|
||||||
# -----------------------------------
|
|
||||||
tmpDir="/tmp/${scriptName}.$RANDOM.$RANDOM.$RANDOM.$$"
|
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
|
||||||
die "Could not create temporary directory! Exiting."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
# -----------------------------------
|
|
||||||
# Log is only used when the '-l' flag is set.
|
|
||||||
#
|
|
||||||
# To never save a logfile change variable to '/dev/null'
|
|
||||||
# Save to Desktop use: $HOME/Desktop/${scriptBasename}.log
|
|
||||||
# Save to standard user log location use: $HOME/Library/Logs/${scriptBasename}.log
|
|
||||||
# -----------------------------------
|
|
||||||
logFile="${HOME}/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
|
|
||||||
# Options and Usage
|
|
||||||
# -----------------------------------
|
|
||||||
# Print usage
|
|
||||||
usage() {
|
|
||||||
echo -n "${scriptName} [OPTION]... [FILE]...
|
|
||||||
|
|
||||||
${bold}Trash${reset} allows MacOS trashing of files instead of tempting fate with ${bold}rm${reset}.
|
|
||||||
Anything deleted with Trash will be moved to the native MacOS trash folder.
|
|
||||||
|
|
||||||
This script:
|
|
||||||
|
|
||||||
- Correctly handles ${bold}trashing files on other volumes${reset}
|
|
||||||
- Uses the ${bold}same filename renaming scheme as Finder${reset} for duplicate file names
|
|
||||||
- Can ${bold}list trash contents${reset} w/disk usage summary
|
|
||||||
- ${bold}Empty trash${reset} (including securely) w/confirmation.
|
|
||||||
- Does not require Finder to be running.
|
|
||||||
|
|
||||||
${bold}Options:${reset}
|
|
||||||
-l , --list List trash contents
|
|
||||||
-e, --empty Empty trash contents
|
|
||||||
-s, --secure Secure empty trash contents
|
|
||||||
|
|
||||||
--force Skip all user interaction. Implied 'Yes' to all actions.
|
|
||||||
--log Print log to file
|
|
||||||
-q, --quiet Quiet (no output)
|
|
||||||
-v, --verbose Output more information. (Items echoed to 'verbose')
|
|
||||||
-d, --debug Runs script in BASH debug mode (set -x)
|
|
||||||
-h, --help Display this help and exit
|
|
||||||
--version Output version information and exit
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
|
||||||
# --foo bar
|
|
||||||
optstring=h
|
|
||||||
unset options
|
|
||||||
while (($#)); do
|
|
||||||
case $1 in
|
|
||||||
# If option is of type -ab
|
|
||||||
-[!-]?*)
|
|
||||||
# Loop over each character starting with the second
|
|
||||||
for ((i=1; i < ${#1}; i++)); do
|
|
||||||
c=${1:i:1}
|
|
||||||
|
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
|
||||||
# the rest of the string its argument
|
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
|
||||||
options+=("${1:i+1}")
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
|
|
||||||
# If option is of type --foo=bar
|
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
|
||||||
# add --endopts for --
|
|
||||||
--) options+=(--endopts) ;;
|
|
||||||
# Otherwise, nothing special
|
|
||||||
*) options+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
set -- "${options[@]}"
|
|
||||||
unset options
|
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# -------------------------------------
|
|
||||||
# [[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
|
||||||
while [[ $1 = -?* ]]; do
|
|
||||||
case $1 in
|
|
||||||
-h|--help) usage >&2; safeExit ;;
|
|
||||||
--version) echo "$(basename $0) ${version}"; safeExit ;;
|
|
||||||
-l|--list) list=true ;;
|
|
||||||
-s|--secure) secureEmpty=true ;;
|
|
||||||
-e|--empty) emptyTrash=true ;;
|
|
||||||
|
|
||||||
-v|--verbose) verbose=true ;;
|
|
||||||
--log) printLog=true ;;
|
|
||||||
-q|--quiet) quiet=true ;;
|
|
||||||
-s|--strict) strict=true;;
|
|
||||||
-d|--debug) debug=true;;
|
|
||||||
--force) force=true ;;
|
|
||||||
--endopts) shift; break ;;
|
|
||||||
*) die "invalid option: '$1'." ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Store the remaining part as arguments.
|
|
||||||
args+=("$@")
|
|
||||||
|
|
||||||
|
|
||||||
# Logging and Colors
|
|
||||||
# -----------------------------------------------------
|
|
||||||
# Here we set the colors for our script feedback.
|
|
||||||
# Example usage: success "sometext"
|
|
||||||
#------------------------------------------------------
|
|
||||||
|
|
||||||
# Set Colors
|
|
||||||
bold=$(tput bold)
|
|
||||||
reset=$(tput sgr0)
|
|
||||||
purple=$(tput setaf 171)
|
|
||||||
red=$(tput setaf 1)
|
|
||||||
green=$(tput setaf 76)
|
|
||||||
tan=$(tput setaf 3)
|
|
||||||
blue=$(tput setaf 38)
|
|
||||||
underline=$(tput sgr 0 1)
|
|
||||||
|
|
||||||
function _alert() {
|
|
||||||
if [ "${1}" = "emergency" ]; then local color="${bold}${red}"; fi
|
|
||||||
if [ "${1}" = "error" ] || [ "${1}" = "warning" ]; then local color="${red}"; fi
|
|
||||||
if [ "${1}" = "success" ]; then local color="${green}"; fi
|
|
||||||
if [ "${1}" = "debug" ]; then local color="${purple}"; fi
|
|
||||||
if [ "${1}" = "header" ]; then local color="${bold}""${tan}"; fi
|
|
||||||
if [ "${1}" = "input" ]; then local color="${bold}"; printLog="0"; fi
|
|
||||||
if [ "${1}" = "info" ] || [ "${1}" = "notice" ]; then local color=""; fi
|
|
||||||
# Don't use colors on pipes or non-recognized terminals
|
|
||||||
if [[ "${TERM}" != "xterm"* ]] || [ -t 1 ]; then color=""; reset=""; fi
|
|
||||||
|
|
||||||
# Print to $logFile
|
|
||||||
if ${printLog}; then
|
|
||||||
echo -e "$(date +"%m-%d-%Y %r") $(printf "[%9s]" "${1}") ${_message}" >> "${logFile}";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print to console when script is not 'quiet'
|
|
||||||
if ${quiet}; then
|
|
||||||
return
|
|
||||||
else
|
|
||||||
echo -e "$(date +"%r") ${color}$(printf "[%9s]" "${1}") ${_message}${reset}";
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function die () { local _message="${*} Exiting."; echo "$(_alert emergency)"; safeExit;}
|
|
||||||
function error () { local _message="${*}"; echo "$(_alert error)"; }
|
|
||||||
function warning () { local _message="${*}"; echo "$(_alert warning)"; }
|
|
||||||
function notice () { local _message="${*}"; echo "$(_alert notice)"; }
|
|
||||||
function info () { local _message="${*}"; echo "$(_alert info)"; }
|
|
||||||
function debug () { local _message="${*}"; echo "$(_alert debug)"; }
|
|
||||||
function success () { local _message="${*}"; echo "$(_alert success)"; }
|
|
||||||
function input() { local _message="${*}"; echo -n "$(_alert input)"; }
|
|
||||||
function header() { local _message="========== ${*} ========== "; echo "$(_alert header)"; }
|
|
||||||
|
|
||||||
# Log messages when verbose is set to "true"
|
|
||||||
verbose() { if ${verbose}; then debug "$@"; fi }
|
|
||||||
|
|
||||||
function seek_confirmation() {
|
|
||||||
# echo ""
|
|
||||||
input "$@"
|
|
||||||
if [[ "${force}" == "1" ]]; then
|
|
||||||
notice "Forcing confirmation with '--force' flag set"
|
|
||||||
else
|
|
||||||
read -p " (y/n) " -n 1
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_confirmed() {
|
|
||||||
if [[ "${force}" == "1" ]]; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_confirmed() {
|
|
||||||
if [[ "${force}" == "1" ]]; then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
if [[ "${REPLY}" =~ ^[Nn]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
|
||||||
trap trapCleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Set IFS to preferred implementation
|
|
||||||
IFS=$' \n\t'
|
|
||||||
|
|
||||||
# Exit on error. Append '||true' when you run the script if you expect an error.
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
# Run in debug mode, if set
|
|
||||||
if ${debug}; then set -x ; fi
|
|
||||||
|
|
||||||
# Exit on empty variable
|
|
||||||
if ${strict}; then set -o nounset ; fi
|
|
||||||
|
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Run your script
|
|
||||||
mainScript
|
|
||||||
|
|
||||||
# Exit cleanly
|
|
||||||
safeExit
|
|
||||||
5
etc/.gitignore
vendored
5
etc/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
*
|
|
||||||
|
|
||||||
!.gitignore
|
|
||||||
!README.md
|
|
||||||
!*.sample
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
This directory contains the configuration files needed for the scripts and functions within this repository.
|
|
||||||
|
|
||||||
# pushover.cfg.sample
|
|
||||||
To use the [Pushover][1] notification function in your scripts take the following steps.
|
|
||||||
|
|
||||||
1. If you haven't done so already, create a [Pushover][1] account and create a Pushover application.
|
|
||||||
2. Rename `pushover.cfg.sample` to `pushover.cfg`
|
|
||||||
3. Add your user API key and your application API key to this file.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[1]: https://pushover.net
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# PUSHOVER CONFIGURATION FILE
|
|
||||||
# -------------------------------------------------------------
|
|
||||||
# To enable the ability to send notifications from scripts
|
|
||||||
# via Pushover enter your Pushover API and USER keys below.
|
|
||||||
# Then rename this file to pushover.cfg
|
|
||||||
#
|
|
||||||
# You may obtain these credentials at https://pushover.net
|
|
||||||
#
|
|
||||||
# -------------------------------------------------------------
|
|
||||||
|
|
||||||
PUSHOVER_API_KEY="YourApplicationAPIKey"
|
|
||||||
PUSHOVER_USER_KEY="YourUserKey"
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
This directory contains the shared libraries and functions that are required by the scripts within this repository.
|
|
||||||
|
|
||||||
# utils.sh
|
|
||||||
This script must be sourced from all my additional scripts. Contained within this are two important functions.
|
|
||||||
|
|
||||||
1. **Logging** - All scripts use the logging functions. There are nine different levels of logs. All log levels are called from within a script in the format `info "some message"`. The levels of logging are:
|
|
||||||
* **die** - Prints an error and exits the script
|
|
||||||
* **error** - prints an error and continues to run the script
|
|
||||||
* **warning** - prints a warning
|
|
||||||
* **notice** - prints a notice to the user
|
|
||||||
* **info** - prints information to the user
|
|
||||||
* **debug** - prints debug information. This output hidden unless scripts are run with the verbose (`-v`) flag
|
|
||||||
* **success** - prints success to a user
|
|
||||||
* **input** - Asks the user for input
|
|
||||||
* **header** - Prints a header to help format logs
|
|
||||||
2. **Sourcing Additional Files** - This script reads a list of additional files and sources them.
|
|
||||||
|
|
||||||
# setupScriptFunctions.sh
|
|
||||||
This script contains different functions used to install software and configure Mac computers from the scripts contained in the `setupScripts` directory.
|
|
||||||
|
|
||||||
# sharedVariables.sh
|
|
||||||
This script contains variables that can be called from any other script.
|
|
||||||
|
|
||||||
# sharedFunctions.sh
|
|
||||||
This script contains many different functions which can be used throughout different scripts. The script is well commented.
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,229 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
# Shared bash functions used by my mac setup scripts.
|
|
||||||
#
|
|
||||||
# VERSION 1.0.0
|
|
||||||
#
|
|
||||||
# HISTORY
|
|
||||||
#
|
|
||||||
# * 2015-01-02 - v1.0.0 - First Creation
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
|
|
||||||
# hasHomebrew
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# This function checks for Homebrew being installed.
|
|
||||||
# If it is not found, we install it and its prerequisites
|
|
||||||
# ------------------------------------------------------
|
|
||||||
hasHomebrew () {
|
|
||||||
# Check for Homebrew
|
|
||||||
#verbose "Checking homebrew install"
|
|
||||||
if type_not_exists 'brew'; then
|
|
||||||
warning "No Homebrew. Gots to install it..."
|
|
||||||
seek_confirmation "Install Homebrew?"
|
|
||||||
if is_confirmed; then
|
|
||||||
# Ensure that we can actually, like, compile anything.
|
|
||||||
if [[ ! "$(type -P gcc)" && "$OSTYPE" =~ ^darwin ]]; then
|
|
||||||
notice "XCode or the Command Line Tools for XCode must be installed first."
|
|
||||||
seek_confirmation "Install Command Line Tools from here?"
|
|
||||||
if is_confirmed; then
|
|
||||||
xcode-select --install
|
|
||||||
else
|
|
||||||
die "Please come back after Command Line Tools are installed."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# Check for Git
|
|
||||||
if type_not_exists 'git'; then
|
|
||||||
die "Git should be installed. It isn't."
|
|
||||||
fi
|
|
||||||
# Install Homebrew
|
|
||||||
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
|
||||||
brew tap homebrew/dupes
|
|
||||||
brew tap homebrew/versions
|
|
||||||
brew tap argon/mas
|
|
||||||
else
|
|
||||||
die "Without Homebrew installed we won't get very far."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# brewMaintenance
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Will run the recommended Homebrew maintenance scripts
|
|
||||||
# ------------------------------------------------------
|
|
||||||
brewMaintenance () {
|
|
||||||
seek_confirmation "Run Homebrew maintenance?"
|
|
||||||
if is_confirmed; then
|
|
||||||
brew doctor
|
|
||||||
brew update
|
|
||||||
brew upgrade --all
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# hasCasks
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# This function checks for Homebrew Casks and Fonts being installed.
|
|
||||||
# If it is not found, we install it and its prerequisites
|
|
||||||
# ------------------------------------------------------
|
|
||||||
hasCasks () {
|
|
||||||
if ! $(brew cask > /dev/null); then
|
|
||||||
brew install caskroom/cask/brew-cask
|
|
||||||
brew tap caskroom/fonts
|
|
||||||
brew tap caskroom/versions
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# My preferred installation of FFMPEG
|
|
||||||
install-ffmpeg () {
|
|
||||||
if [ ! $(type -P "ffmpeg") ]; then
|
|
||||||
brew install ffmpeg --with-faac --with-fdk-aac --with-ffplay --with-fontconfig --with-freetype --with-libcaca --with-libass --with-frei0r --with-libass --with-libbluray --with-libcaca --with-libquvi --with-libvidstab --with-libsoxr --with-libssh --with-libvo-aacenc --with-libvidstab --with-libvorbis --with-libvpx --with-opencore-amr --with-openjpeg --with-openssl --with-opus --with-rtmpdump --with-schroedinger --with-speex --with-theora --with-tools --with-webp --with-x265
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# doInstall
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Reads a list of items, checks if they are installed, installs
|
|
||||||
# those which are needed.
|
|
||||||
#
|
|
||||||
# Variables needed are:
|
|
||||||
# LISTINSTALLED: The command to list all previously installed items
|
|
||||||
# Ex: "brew list" or "gem list | awk '{print $1}'"
|
|
||||||
#
|
|
||||||
# INSTALLCOMMAND: The Install command for the desired items.
|
|
||||||
# Ex: "brew install" or "gem install"
|
|
||||||
#
|
|
||||||
# RECIPES: The list of packages to install.
|
|
||||||
# Ex: RECIPES=(
|
|
||||||
# package1
|
|
||||||
# package2
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Credit: https://github.com/cowboy/dotfiles
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function to_install() {
|
|
||||||
local debugger desired installed i desired_s installed_s remain
|
|
||||||
if [[ "$1" == 1 ]]; then debugger=1; shift; fi
|
|
||||||
# Convert args to arrays, handling both space- and newline-separated lists.
|
|
||||||
read -ra desired < <(echo "$1" | tr '\n' ' ')
|
|
||||||
read -ra installed < <(echo "$2" | tr '\n' ' ')
|
|
||||||
# Sort desired and installed arrays.
|
|
||||||
unset i; while read -r; do desired_s[i++]=$REPLY; done < <(
|
|
||||||
printf "%s\n" "${desired[@]}" | sort
|
|
||||||
)
|
|
||||||
unset i; while read -r; do installed_s[i++]=$REPLY; done < <(
|
|
||||||
printf "%s\n" "${installed[@]}" | sort
|
|
||||||
)
|
|
||||||
# Get the difference. comm is awesome.
|
|
||||||
unset i; while read -r; do remain[i++]=$REPLY; done < <(
|
|
||||||
comm -13 <(printf "%s\n" "${installed_s[@]}") <(printf "%s\n" "${desired_s[@]}")
|
|
||||||
)
|
|
||||||
[[ "$debugger" ]] && for v in desired desired_s installed installed_s remain; do
|
|
||||||
echo "$v ($(eval echo "\${#$v[*]}")) $(eval echo "\${$v[*]}")"
|
|
||||||
done
|
|
||||||
echo "${remain[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Install the desired items that are not already installed.
|
|
||||||
function doInstall () {
|
|
||||||
list=$(to_install "${RECIPES[*]}" "$(${LISTINSTALLED})")
|
|
||||||
if [[ "${list}" ]]; then
|
|
||||||
seek_confirmation "Confirm each package before installing?"
|
|
||||||
if is_confirmed; then
|
|
||||||
for item in "${list[@]}"
|
|
||||||
do
|
|
||||||
seek_confirmation "Install ${item}?"
|
|
||||||
if is_confirmed; then
|
|
||||||
notice "Installing ${item}"
|
|
||||||
# FFMPEG takes additional flags
|
|
||||||
if [[ "${item}" = "ffmpeg" ]]; then
|
|
||||||
install-ffmpeg
|
|
||||||
elif [[ "${item}" = "tldr" ]]; then
|
|
||||||
brew tap tldr-pages/tldr
|
|
||||||
brew install tldr
|
|
||||||
else
|
|
||||||
${INSTALLCOMMAND} "${item}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
for item in "${list[@]}"
|
|
||||||
do
|
|
||||||
notice "Installing ${item}"
|
|
||||||
# FFMPEG takes additional flags
|
|
||||||
if [[ "${item}" = "ffmpeg" ]]; then
|
|
||||||
install-ffmpeg
|
|
||||||
elif [[ "${item}" = "tldr" ]]; then
|
|
||||||
brew tap tldr-pages/tldr
|
|
||||||
brew install tldr
|
|
||||||
else
|
|
||||||
${INSTALLCOMMAND} "${item}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# only print notice when not checking dependencies via another script
|
|
||||||
if [ -z "$homebrewDependencies" ] && [ -z "$caskDependencies" ] && [ -z "$gemDependencies" ]; then
|
|
||||||
notice "Nothing to install. You've already installed all your recipes."
|
|
||||||
fi
|
|
||||||
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# brewCleanup
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# This function cleans up an initial Homebrew installation
|
|
||||||
# ------------------------------------------------------
|
|
||||||
brewCleanup () {
|
|
||||||
# This is where brew stores its binary symlinks
|
|
||||||
binroot="$(brew --config | awk '/HOMEBREW_PREFIX/ {print $2}')"/bin
|
|
||||||
|
|
||||||
if [[ "$(type -P ${binroot}/bash)" && "$(cat /etc/shells | grep -q "$binroot/bash")" ]]; then
|
|
||||||
info "Adding ${binroot}/bash to the list of acceptable shells"
|
|
||||||
echo "$binroot/bash" | sudo tee -a /etc/shells >/dev/null
|
|
||||||
fi
|
|
||||||
if [[ "$SHELL" != "${binroot}/bash" ]]; then
|
|
||||||
info "Making ${binroot}/bash your default shell"
|
|
||||||
sudo chsh -s "${binroot}/bash" "$USER" >/dev/null 2>&1
|
|
||||||
success "Please exit and restart all your shells."
|
|
||||||
fi
|
|
||||||
|
|
||||||
brew cleanup
|
|
||||||
|
|
||||||
if $(brew cask > /dev/null); then
|
|
||||||
brew cask cleanup
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# hasDropbox
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# This function checks for Dropbox being installed.
|
|
||||||
# If it is not found, we install it and its prerequisites
|
|
||||||
# ------------------------------------------------------
|
|
||||||
hasDropbox () {
|
|
||||||
# Confirm we have Dropbox installed
|
|
||||||
notice "Confirming that Dropbox is installed..."
|
|
||||||
if [ ! -e "/Applications/Dropbox.app" ]; then
|
|
||||||
notice "We don't have Dropbox. Let's get it installed."
|
|
||||||
seek_confirmation "Install Dropbox and all necessary prerequisites?"
|
|
||||||
if is_confirmed; then
|
|
||||||
# Run functions
|
|
||||||
hasHomebrew
|
|
||||||
brewMaintenance
|
|
||||||
hasCasks
|
|
||||||
|
|
||||||
# Set Variables
|
|
||||||
local LISTINSTALLED="brew cask list"
|
|
||||||
local INSTALLCOMMAND="brew cask install --appdir=/Applications"
|
|
||||||
|
|
||||||
local RECIPES=(dropbox)
|
|
||||||
Install
|
|
||||||
open -a dropbox
|
|
||||||
else
|
|
||||||
die "Can't run this script. Install Dropbox manually."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
success "Dropbox is installed."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
@@ -1,749 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
# Shared bash functions used by my bash scripts.
|
|
||||||
#
|
|
||||||
# VERSION 1.4.0
|
|
||||||
#
|
|
||||||
# HISTORY
|
|
||||||
#
|
|
||||||
# * 2015-01-02 - v1.0.0 - First Creation
|
|
||||||
# * 2015-04-16 - v1.2.0 - Added 'checkDependencies' and 'pauseScript'
|
|
||||||
# * 2016-01-10 - v1.3.0 - Added 'join' function
|
|
||||||
# * 2016-01-11 - v1.4.9 - Added 'httpStatus' function
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
|
|
||||||
# Traps
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# These functions are for use with different trap scenarios
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
# Non destructive exit for when script exits naturally.
|
|
||||||
# Usage: Add this function at the end of every script
|
|
||||||
function safeExit() {
|
|
||||||
# Delete temp files, if any
|
|
||||||
if is_dir "${tmpDir}"; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
trap - INT TERM EXIT
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
# readFile
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Function to read a line from a file.
|
|
||||||
#
|
|
||||||
# Most often used to read the config files saved in my etc directory.
|
|
||||||
# Outputs each line in a variable named $result
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function readFile() {
|
|
||||||
unset "${result}"
|
|
||||||
while read result
|
|
||||||
do
|
|
||||||
echo "${result}"
|
|
||||||
done < "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Escape a string
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# usage: var=$(escape "String")
|
|
||||||
# ------------------------------------------------------
|
|
||||||
escape() { echo "${@}" | sed 's/[]\.|$(){}?+*^]/\\&/g'; }
|
|
||||||
|
|
||||||
# needSudo
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# If a script needs sudo access, call this function which
|
|
||||||
# requests sudo access and then keeps it alive.
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function needSudo() {
|
|
||||||
# Update existing sudo time stamp if set, otherwise do nothing.
|
|
||||||
sudo -v
|
|
||||||
while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null &
|
|
||||||
}
|
|
||||||
|
|
||||||
# convertsecs
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Convert Seconds to human readable time
|
|
||||||
#
|
|
||||||
# To use this, pass a number (seconds) into the function as this:
|
|
||||||
# print "$(convertsecs $TOTALTIME)"
|
|
||||||
#
|
|
||||||
# To compute the time it takes a script to run use tag the start and end times with
|
|
||||||
# STARTTIME=$(date +"%s")
|
|
||||||
# ENDTIME=$(date +"%s")
|
|
||||||
# TOTALTIME=$(($ENDTIME-$STARTTIME))
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function convertsecs() {
|
|
||||||
((h=${1}/3600))
|
|
||||||
((m=(${1}%3600)/60))
|
|
||||||
((s=${1}%60))
|
|
||||||
printf "%02d:%02d:%02d\n" $h $m $s
|
|
||||||
}
|
|
||||||
|
|
||||||
function pushover() {
|
|
||||||
# pushover
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Sends notifications view Pushover
|
|
||||||
# Requires a file named 'pushover.cfg' be placed in '../etc/'
|
|
||||||
#
|
|
||||||
# Usage: pushover "Title Goes Here" "Message Goes Here"
|
|
||||||
#
|
|
||||||
# Credit: http://ryonsherman.blogspot.com/2012/10/shell-script-to-send-pushover.html
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
# Check for config file containing API Keys
|
|
||||||
if [ ! -f "${SOURCEPATH}/../etc/pushover.cfg" ]; then
|
|
||||||
error "Please locate the pushover.cfg to send notifications to Pushover."
|
|
||||||
else
|
|
||||||
# Grab variables from the config file
|
|
||||||
source "${SOURCEPATH}/../etc/pushover.cfg"
|
|
||||||
|
|
||||||
# Send to Pushover
|
|
||||||
PUSHOVERURL="https://api.pushover.net/1/messages.json"
|
|
||||||
API_KEY="${PUSHOVER_API_KEY}"
|
|
||||||
USER_KEY="${PUSHOVER_USER_KEY}"
|
|
||||||
DEVICE=""
|
|
||||||
TITLE="${1}"
|
|
||||||
MESSAGE="${2}"
|
|
||||||
curl \
|
|
||||||
-F "token=${API_KEY}" \
|
|
||||||
-F "user=${USER_KEY}" \
|
|
||||||
-F "device=${DEVICE}" \
|
|
||||||
-F "title=${TITLE}" \
|
|
||||||
-F "message=${MESSAGE}" \
|
|
||||||
"${PUSHOVERURL}" > /dev/null 2>&1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Join
|
|
||||||
# ----------------------------------------------
|
|
||||||
# This function joins items together with a user specified separator
|
|
||||||
# Taken whole cloth from: http://stackoverflow.com/questions/1527049/bash-join-elements-of-an-array
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# join , a "b c" d #a,b c,d
|
|
||||||
# join / var local tmp #var/local/tmp
|
|
||||||
# join , "${FOO[@]}" #a,b,c
|
|
||||||
# ----------------------------------------------
|
|
||||||
function join() { local IFS="${1}"; shift; echo "${*}"; }
|
|
||||||
|
|
||||||
# File Checks
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# A series of functions which make checks against the filesystem. For
|
|
||||||
# use in if/then statements.
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# if is_file "file"; then
|
|
||||||
# ...
|
|
||||||
# fi
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
function is_exists() {
|
|
||||||
if [[ -e "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_exists() {
|
|
||||||
if [[ ! -e "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_file() {
|
|
||||||
if [[ -f "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_file() {
|
|
||||||
if [[ ! -f "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_dir() {
|
|
||||||
if [[ -d "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_dir() {
|
|
||||||
if [[ ! -d "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_symlink() {
|
|
||||||
if [[ -L "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_symlink() {
|
|
||||||
if [[ ! -L "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_empty() {
|
|
||||||
if [[ -z "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_empty() {
|
|
||||||
if [[ -n "$1" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test whether a command exists
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Usage:
|
|
||||||
# if type_exists 'git'; then
|
|
||||||
# some action
|
|
||||||
# else
|
|
||||||
# some other action
|
|
||||||
# fi
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
function type_exists() {
|
|
||||||
if [ "$(type -P "$1")" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function type_not_exists() {
|
|
||||||
if [ ! "$(type -P "$1")" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test which OS the user runs
|
|
||||||
# $1 = OS to test
|
|
||||||
# Usage: if is_os 'darwin'; then
|
|
||||||
|
|
||||||
function is_os() {
|
|
||||||
if [[ "${OSTYPE}" == $1* ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# SEEKING CONFIRMATION
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Asks questions of a user and then does something with the answer.
|
|
||||||
# y/n are the only possible answers.
|
|
||||||
#
|
|
||||||
# USAGE:
|
|
||||||
# seek_confirmation "Ask a question"
|
|
||||||
# if is_confirmed; then
|
|
||||||
# some action
|
|
||||||
# else
|
|
||||||
# some other action
|
|
||||||
# fi
|
|
||||||
#
|
|
||||||
# Credt: https://github.com/kevva/dotfiles
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
# Ask the question
|
|
||||||
function seek_confirmation() {
|
|
||||||
# echo ""
|
|
||||||
input "$@"
|
|
||||||
if "${force}"; then
|
|
||||||
notice "Forcing confirmation with '--force' flag set"
|
|
||||||
else
|
|
||||||
read -p " (y/n) " -n 1
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Test whether the result of an 'ask' is a confirmation
|
|
||||||
function is_confirmed() {
|
|
||||||
if "${force}"; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_confirmed() {
|
|
||||||
if "${force}"; then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
if [[ "${REPLY}" =~ ^[Nn]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Skip something
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Offer the user a chance to skip something.
|
|
||||||
# Credit: https://github.com/cowboy/dotfiles
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function skip() {
|
|
||||||
REPLY=noskip
|
|
||||||
read -t 5 -n 1 -s -p "${bold}To skip, press ${underline}X${reset}${bold} within 5 seconds.${reset}"
|
|
||||||
if [[ "$REPLY" =~ ^[Xx]$ ]]; then
|
|
||||||
notice " Skipping!"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
notice " Continuing..."
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# unmountDrive
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# If an AFP drive is mounted as part of a script, this
|
|
||||||
# will unmount the volume. This will only work on Macs.
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function unmountDrive() {
|
|
||||||
if [ -d "$1" ]; then
|
|
||||||
diskutil unmount "$1"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# help
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Prints help for a script when invoked from the command
|
|
||||||
# line. Typically via '-h'. If additional flags or help
|
|
||||||
# text is available in the script they will be printed
|
|
||||||
# in the '$usage' variable.
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function help () {
|
|
||||||
echo "" 1>&2
|
|
||||||
input " $@" 1>&2
|
|
||||||
if [ -n "${usage}" ]; then # print usage information if available
|
|
||||||
echo " ${usage}" 1>&2
|
|
||||||
fi
|
|
||||||
echo "" 1>&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Dependencies
|
|
||||||
# -----------------------------------
|
|
||||||
# Arrays containing package dependencies needed to execute this script.
|
|
||||||
# The script will fail if dependencies are not installed. For Mac users,
|
|
||||||
# most dependencies can be installed automatically using the package
|
|
||||||
# manager 'Homebrew'.
|
|
||||||
# Usage in script: $ homebrewDependencies=(package1 package2)
|
|
||||||
# -----------------------------------
|
|
||||||
function checkDependencies() {
|
|
||||||
saveIFS=$IFS
|
|
||||||
IFS=$' \n\t'
|
|
||||||
if [ -n "${homebrewDependencies}" ]; then
|
|
||||||
LISTINSTALLED="brew list"
|
|
||||||
INSTALLCOMMAND="brew install"
|
|
||||||
RECIPES=("${homebrewDependencies[@]}")
|
|
||||||
# Invoke functions from setupScriptFunctions.sh
|
|
||||||
hasHomebrew
|
|
||||||
doInstall
|
|
||||||
fi
|
|
||||||
if [ -n "$caskDependencies" ]; then
|
|
||||||
LISTINSTALLED="brew cask list"
|
|
||||||
INSTALLCOMMAND="brew cask install --appdir=/Applications"
|
|
||||||
RECIPES=("${caskDependencies[@]}")
|
|
||||||
|
|
||||||
# Invoke functions from setupScriptFunctions.sh
|
|
||||||
hasHomebrew
|
|
||||||
hasCasks
|
|
||||||
doInstall
|
|
||||||
fi
|
|
||||||
if [ -n "$gemDependencies" ]; then
|
|
||||||
LISTINSTALLED="gem list | awk '{print $1}'"
|
|
||||||
INSTALLCOMMAND="gem install"
|
|
||||||
RECIPES=("${gemDependencies[@]}")
|
|
||||||
# Invoke functions from setupScriptFunctions.sh
|
|
||||||
doInstall
|
|
||||||
fi
|
|
||||||
IFS=$saveIFS
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function pauseScript() {
|
|
||||||
# A simple function used to pause a script at any point and
|
|
||||||
# only continue on user input
|
|
||||||
seek_confirmation "Ready to continue?"
|
|
||||||
if is_confirmed; then
|
|
||||||
info "Continuing"
|
|
||||||
else
|
|
||||||
warning "Exiting Script."
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function in_array() {
|
|
||||||
# Determine if a value is in an array.
|
|
||||||
# Usage: if in_array "VALUE" "${ARRAY[@]}"; then ...
|
|
||||||
local value="$1"; shift
|
|
||||||
for arrayItem in "$@"; do
|
|
||||||
[[ "${arrayItem}" == "${value}" ]] && return 0
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Text Transformations
|
|
||||||
# -----------------------------------
|
|
||||||
# Transform text using these functions.
|
|
||||||
# Adapted from https://github.com/jmcantrell/bashful
|
|
||||||
# -----------------------------------
|
|
||||||
|
|
||||||
lower() {
|
|
||||||
# Convert stdin to lowercase.
|
|
||||||
# usage: text=$(lower <<<"$1")
|
|
||||||
# echo "MAKETHISLOWERCASE" | lower
|
|
||||||
tr '[:upper:]' '[:lower:]'
|
|
||||||
}
|
|
||||||
|
|
||||||
upper() {
|
|
||||||
# Convert stdin to uppercase.
|
|
||||||
# usage: text=$(upper <<<"$1")
|
|
||||||
# echo "MAKETHISUPPERCASE" | upper
|
|
||||||
tr '[:lower:]' '[:upper:]'
|
|
||||||
}
|
|
||||||
|
|
||||||
ltrim() {
|
|
||||||
# Removes all leading whitespace (from the left).
|
|
||||||
local char=${1:-[:space:]}
|
|
||||||
sed "s%^[${char//%/\\%}]*%%"
|
|
||||||
}
|
|
||||||
|
|
||||||
rtrim() {
|
|
||||||
# Removes all trailing whitespace (from the right).
|
|
||||||
local char=${1:-[:space:]}
|
|
||||||
sed "s%[${char//%/\\%}]*$%%"
|
|
||||||
}
|
|
||||||
|
|
||||||
trim() {
|
|
||||||
# Removes all leading/trailing whitespace
|
|
||||||
# Usage examples:
|
|
||||||
# echo " foo bar baz " | trim #==> "foo bar baz"
|
|
||||||
ltrim "$1" | rtrim "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
squeeze() {
|
|
||||||
# Removes leading/trailing whitespace and condenses all other consecutive
|
|
||||||
# whitespace into a single space.
|
|
||||||
#
|
|
||||||
# Usage examples:
|
|
||||||
# echo " foo bar baz " | squeeze #==> "foo bar baz"
|
|
||||||
|
|
||||||
local char=${1:-[[:space:]]}
|
|
||||||
sed "s%\(${char//%/\\%}\)\+%\1%g" | trim "$char"
|
|
||||||
}
|
|
||||||
|
|
||||||
squeeze_lines() {
|
|
||||||
# <doc:squeeze_lines> {{{
|
|
||||||
#
|
|
||||||
# Removes all leading/trailing blank lines and condenses all other
|
|
||||||
# consecutive blank lines into a single blank line.
|
|
||||||
#
|
|
||||||
# </doc:squeeze_lines> }}}
|
|
||||||
|
|
||||||
sed '/^[[:space:]]\+$/s/.*//g' | cat -s | trim_lines
|
|
||||||
}
|
|
||||||
|
|
||||||
progressBar() {
|
|
||||||
# progressBar
|
|
||||||
# -----------------------------------
|
|
||||||
# Prints a progress bar within a for/while loop.
|
|
||||||
# To use this function you must pass the total number of
|
|
||||||
# times the loop will run to the function.
|
|
||||||
#
|
|
||||||
# usage:
|
|
||||||
# for number in $(seq 0 100); do
|
|
||||||
# sleep 1
|
|
||||||
# progressBar 100
|
|
||||||
# done
|
|
||||||
# -----------------------------------
|
|
||||||
if [[ "${quiet}" = "true" ]] || [ "${quiet}" == "1" ]; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
local width
|
|
||||||
width=30
|
|
||||||
bar_char="#"
|
|
||||||
|
|
||||||
# Don't run this function when scripts are running in verbose mode
|
|
||||||
if ${verbose}; then return; fi
|
|
||||||
|
|
||||||
# Reset the count
|
|
||||||
if [ -z "${progressBarProgress}" ]; then
|
|
||||||
progressBarProgress=0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Do nothing if the output is not a terminal
|
|
||||||
if [ ! -t 1 ]; then
|
|
||||||
echo "Output is not a terminal" 1>&2
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
# Hide the cursor
|
|
||||||
tput civis
|
|
||||||
trap 'tput cnorm; exit 1' SIGINT
|
|
||||||
|
|
||||||
if [ ! "${progressBarProgress}" -eq $(( $1 - 1 )) ]; then
|
|
||||||
# Compute the percentage.
|
|
||||||
perc=$(( progressBarProgress * 100 / $1 ))
|
|
||||||
# Compute the number of blocks to represent the percentage.
|
|
||||||
num=$(( progressBarProgress * width / $1 ))
|
|
||||||
# Create the progress bar string.
|
|
||||||
bar=
|
|
||||||
if [ ${num} -gt 0 ]; then
|
|
||||||
bar=$(printf "%0.s${bar_char}" $(seq 1 ${num}))
|
|
||||||
fi
|
|
||||||
# Print the progress bar.
|
|
||||||
progressBarLine=$(printf "%s [%-${width}s] (%d%%)" "Running Process" "${bar}" "${perc}")
|
|
||||||
echo -en "${progressBarLine}\r"
|
|
||||||
progressBarProgress=$(( progressBarProgress + 1 ))
|
|
||||||
else
|
|
||||||
# Clear the progress bar when complete
|
|
||||||
echo -ne "${width}%\033[0K\r"
|
|
||||||
unset progressBarProgress
|
|
||||||
fi
|
|
||||||
|
|
||||||
tput cnorm
|
|
||||||
}
|
|
||||||
|
|
||||||
htmlDecode() {
|
|
||||||
# Decode HTML characters with sed
|
|
||||||
# Usage: htmlDecode <string>
|
|
||||||
echo "${1}" | sed -f "${SOURCEPATH}/htmlDecode.sed"
|
|
||||||
}
|
|
||||||
|
|
||||||
htmlEncode() {
|
|
||||||
# Encode HTML characters with sed
|
|
||||||
# Usage: htmlEncode <string>
|
|
||||||
echo "${1}" | sed -f "${SOURCEPATH}/htmlEncode.sed"
|
|
||||||
}
|
|
||||||
|
|
||||||
urlencode() {
|
|
||||||
# URL encoding/decoding from: https://gist.github.com/cdown/1163649
|
|
||||||
# Usage: urlencode <string>
|
|
||||||
|
|
||||||
local length="${#1}"
|
|
||||||
for (( i = 0; i < length; i++ )); do
|
|
||||||
local c="${1:i:1}"
|
|
||||||
case $c in
|
|
||||||
[a-zA-Z0-9.~_-]) printf "%s" "$c" ;;
|
|
||||||
*) printf '%%%02X' "'$c"
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
urldecode() {
|
|
||||||
# Usage: urldecode <string>
|
|
||||||
|
|
||||||
local url_encoded="${1//+/ }"
|
|
||||||
printf '%b' "${url_encoded//%/\x}"
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_yaml() {
|
|
||||||
# Function to parse YAML files and add values to variables. Send it to a temp file and source it
|
|
||||||
# https://gist.github.com/DinoChiesa/3e3c3866b51290f31243 which is derived from
|
|
||||||
# https://gist.github.com/epiloque/8cf512c6d64641bde388
|
|
||||||
#
|
|
||||||
# Usage:
|
|
||||||
# $ parse_yaml sample.yml > /some/tempfile
|
|
||||||
#
|
|
||||||
# parse_yaml accepts a prefix argument so that imported settings all have a common prefix
|
|
||||||
# (which will reduce the risk of name-space collisions).
|
|
||||||
#
|
|
||||||
# $ parse_yaml sample.yml "CONF_"
|
|
||||||
|
|
||||||
local prefix=$2
|
|
||||||
local s
|
|
||||||
local w
|
|
||||||
local fs
|
|
||||||
s='[[:space:]]*'
|
|
||||||
w='[a-zA-Z0-9_]*'
|
|
||||||
fs="$(echo @|tr @ '\034')"
|
|
||||||
sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \
|
|
||||||
-e "s|^\($s\)\($w\)$s[:-]$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$1" |
|
|
||||||
awk -F"$fs" '{
|
|
||||||
indent = length($1)/2;
|
|
||||||
if (length($2) == 0) { conj[indent]="+";} else {conj[indent]="";}
|
|
||||||
vname[indent] = $2;
|
|
||||||
for (i in vname) {if (i > indent) {delete vname[i]}}
|
|
||||||
if (length($3) > 0) {
|
|
||||||
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
|
|
||||||
printf("%s%s%s%s=(\"%s\")\n", "'"$prefix"'",vn, $2, conj[indent-1],$3);
|
|
||||||
}
|
|
||||||
}' | sed 's/_=/+=/g'
|
|
||||||
}
|
|
||||||
|
|
||||||
httpStatus() {
|
|
||||||
# -----------------------------------
|
|
||||||
# Shamelessly taken from: https://gist.github.com/rsvp/1171304
|
|
||||||
#
|
|
||||||
# Usage: httpStatus URL [timeout] [--code or --status] [see 4.]
|
|
||||||
# ^message with code (default)
|
|
||||||
# ^code (numeric only)
|
|
||||||
# ^in secs (default: 3)
|
|
||||||
# ^URL without "http://" prefix works fine.
|
|
||||||
#
|
|
||||||
# 4. curl options: e.g. use -L to follow redirects.
|
|
||||||
#
|
|
||||||
# Dependencies: curl
|
|
||||||
#
|
|
||||||
# Example: $ httpStatus bit.ly
|
|
||||||
# 301 Redirection: Moved Permanently
|
|
||||||
#
|
|
||||||
# Example: $ httpStatus www.google.com 100 -c
|
|
||||||
# 200
|
|
||||||
#
|
|
||||||
# -----------------------------------
|
|
||||||
local curlops
|
|
||||||
local arg4
|
|
||||||
local arg5
|
|
||||||
local arg6
|
|
||||||
local arg7
|
|
||||||
local flag
|
|
||||||
local timeout
|
|
||||||
local url
|
|
||||||
|
|
||||||
saveIFS=${IFS}
|
|
||||||
IFS=$' \n\t'
|
|
||||||
|
|
||||||
url=${1}
|
|
||||||
timeout=${2:-'3'}
|
|
||||||
# ^in seconds
|
|
||||||
flag=${3:-'--status'}
|
|
||||||
# curl options, e.g. -L to follow redirects
|
|
||||||
arg4=${4:-''}
|
|
||||||
arg5=${5:-''}
|
|
||||||
arg6=${6:-''}
|
|
||||||
arg7=${7:-''}
|
|
||||||
curlops="${arg4} ${arg5} ${arg6} ${arg7}"
|
|
||||||
|
|
||||||
# __________ get the CODE which is numeric:
|
|
||||||
code=`echo $(curl --write-out %{http_code} --silent --connect-timeout ${timeout} \
|
|
||||||
--no-keepalive ${curlops} --output /dev/null ${url})`
|
|
||||||
|
|
||||||
# __________ get the STATUS (from code) which is human interpretable:
|
|
||||||
case $code in
|
|
||||||
000) status="Not responding within ${timeout} seconds" ;;
|
|
||||||
100) status="Informational: Continue" ;;
|
|
||||||
101) status="Informational: Switching Protocols" ;;
|
|
||||||
200) status="Successful: OK within ${timeout} seconds" ;;
|
|
||||||
201) status="Successful: Created" ;;
|
|
||||||
202) status="Successful: Accepted" ;;
|
|
||||||
203) status="Successful: Non-Authoritative Information" ;;
|
|
||||||
204) status="Successful: No Content" ;;
|
|
||||||
205) status="Successful: Reset Content" ;;
|
|
||||||
206) status="Successful: Partial Content" ;;
|
|
||||||
300) status="Redirection: Multiple Choices" ;;
|
|
||||||
301) status="Redirection: Moved Permanently" ;;
|
|
||||||
302) status="Redirection: Found residing temporarily under different URI" ;;
|
|
||||||
303) status="Redirection: See Other" ;;
|
|
||||||
304) status="Redirection: Not Modified" ;;
|
|
||||||
305) status="Redirection: Use Proxy" ;;
|
|
||||||
306) status="Redirection: status not defined" ;;
|
|
||||||
307) status="Redirection: Temporary Redirect" ;;
|
|
||||||
400) status="Client Error: Bad Request" ;;
|
|
||||||
401) status="Client Error: Unauthorized" ;;
|
|
||||||
402) status="Client Error: Payment Required" ;;
|
|
||||||
403) status="Client Error: Forbidden" ;;
|
|
||||||
404) status="Client Error: Not Found" ;;
|
|
||||||
405) status="Client Error: Method Not Allowed" ;;
|
|
||||||
406) status="Client Error: Not Acceptable" ;;
|
|
||||||
407) status="Client Error: Proxy Authentication Required" ;;
|
|
||||||
408) status="Client Error: Request Timeout within ${timeout} seconds" ;;
|
|
||||||
409) status="Client Error: Conflict" ;;
|
|
||||||
410) status="Client Error: Gone" ;;
|
|
||||||
411) status="Client Error: Length Required" ;;
|
|
||||||
412) status="Client Error: Precondition Failed" ;;
|
|
||||||
413) status="Client Error: Request Entity Too Large" ;;
|
|
||||||
414) status="Client Error: Request-URI Too Long" ;;
|
|
||||||
415) status="Client Error: Unsupported Media Type" ;;
|
|
||||||
416) status="Client Error: Requested Range Not Satisfiable" ;;
|
|
||||||
417) status="Client Error: Expectation Failed" ;;
|
|
||||||
500) status="Server Error: Internal Server Error" ;;
|
|
||||||
501) status="Server Error: Not Implemented" ;;
|
|
||||||
502) status="Server Error: Bad Gateway" ;;
|
|
||||||
503) status="Server Error: Service Unavailable" ;;
|
|
||||||
504) status="Server Error: Gateway Timeout within ${timeout} seconds" ;;
|
|
||||||
505) status="Server Error: HTTP Version Not Supported" ;;
|
|
||||||
*) echo " !! httpstatus: status not defined." && safeExit ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
|
|
||||||
# _______________ MAIN
|
|
||||||
case ${flag} in
|
|
||||||
--status) echo "${code} ${status}" ;;
|
|
||||||
-s) echo "${code} ${status}" ;;
|
|
||||||
--code) echo "${code}" ;;
|
|
||||||
-c) echo "${code}" ;;
|
|
||||||
*) echo " !! httpstatus: bad flag" && safeExit;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
IFS="${saveIFS}"
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeCSV() {
|
|
||||||
# Creates a new CSV file if one does not already exist.
|
|
||||||
# Takes passed arguments and writes them as a header line to the CSV
|
|
||||||
# Usage 'makeCSV column1 column2 column3'
|
|
||||||
|
|
||||||
# Set the location and name of the CSV File
|
|
||||||
if [ -z "${csvLocation}" ]; then
|
|
||||||
csvLocation="${HOME}/Desktop"
|
|
||||||
fi
|
|
||||||
if [ -z "${csvName}" ]; then
|
|
||||||
csvName="$(LC_ALL=C date +%Y-%m-%d)-${FUNCNAME[1]}.csv"
|
|
||||||
fi
|
|
||||||
csvFile="${csvLocation}/${csvName}"
|
|
||||||
|
|
||||||
# Overwrite existing file? If not overwritten, new content is added
|
|
||||||
# to the bottom of the existing file
|
|
||||||
if [ -f "${csvFile}" ]; then
|
|
||||||
seek_confirmation "${csvFile} already exists. Overwrite?"
|
|
||||||
if is_confirmed; then
|
|
||||||
rm "${csvFile}"
|
|
||||||
writeCSV "$@"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function writeCSV() {
|
|
||||||
# Takes passed arguments and writes them as a comma separated line
|
|
||||||
# Usage 'writeCSV column1 column2 column3'
|
|
||||||
|
|
||||||
csvInput=($@)
|
|
||||||
saveIFS=$IFS
|
|
||||||
IFS=','
|
|
||||||
echo "${csvInput[*]}" >> "${csvFile}"
|
|
||||||
IFS=$saveIFS
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function json2yaml() {
|
|
||||||
# convert json files to yaml using python and PyYAML
|
|
||||||
python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' < "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
function yaml2json() {
|
|
||||||
# convert yaml files to json using python and PyYAML
|
|
||||||
python -c 'import sys, yaml, json; json.dump(yaml.load(sys.stdin), sys.stdout, indent=4)' < "$1"
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
# Shared bash functions used by my bash scripts.
|
|
||||||
#
|
|
||||||
# VERSION 1.0.2
|
|
||||||
#
|
|
||||||
# HISTORY
|
|
||||||
#
|
|
||||||
# * 2015-01-02 - v1.0.0 - First Creation
|
|
||||||
# * 2015-08-05 - v1.0.1 - Now has $hourstamp (10:34:40 PM)
|
|
||||||
# * 2016-01-10 - v1.0.2 - Now has $longdate and set LC_ALL=C to all dates
|
|
||||||
# * 2016-01-13 - v1.0.3 - now has $gmtdate
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
# SCRIPTNAME
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Will return the name of the script being run
|
|
||||||
# ------------------------------------------------------
|
|
||||||
scriptName=`basename $0` #Set Script Name variable
|
|
||||||
scriptBasename="$(basename ${scriptName} .sh)" # Strips '.sh' from scriptName
|
|
||||||
|
|
||||||
# TIMESTAMPS
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Prints the current date and time in a variety of formats:
|
|
||||||
#
|
|
||||||
# ------------------------------------------------------
|
|
||||||
now=$(LC_ALL=C date +"%m-%d-%Y %r") # Returns: 06-14-2015 10:34:40 PM
|
|
||||||
datestamp=$(LC_ALL=C date +%Y-%m-%d) # Returns: 2015-06-14
|
|
||||||
hourstamp=$(LC_ALL=C date +%r) # Returns: 10:34:40 PM
|
|
||||||
timestamp=$(LC_ALL=C date +%Y%m%d_%H%M%S) # Returns: 20150614_223440
|
|
||||||
today=$(LC_ALL=C date +"%m-%d-%Y") # Returns: 06-14-2015
|
|
||||||
longdate=$(LC_ALL=C date +"%a, %d %b %Y %H:%M:%S %z") # Returns: Sun, 10 Jan 2016 20:47:53 -0500
|
|
||||||
gmtdate=$(LC_ALL=C date -u -R | sed 's/\+0000/GMT/') # Returns: Wed, 13 Jan 2016 15:55:29 GMT
|
|
||||||
|
|
||||||
# THISHOST
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Will print the current hostname of the computer the script
|
|
||||||
# is being run on.
|
|
||||||
# ------------------------------------------------------
|
|
||||||
thisHost=$(hostname)
|
|
||||||
107
lib/utils.sh
107
lib/utils.sh
@@ -1,107 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
# Bash scripting Utilities.
|
|
||||||
#
|
|
||||||
# VERSION 1.0.0
|
|
||||||
#
|
|
||||||
# This script sources my collection of scripting utilities making
|
|
||||||
# it possible to source this one script and gain access to a
|
|
||||||
# complete collection of functions, variables, and other options.
|
|
||||||
#
|
|
||||||
# HISTORY
|
|
||||||
#
|
|
||||||
# * 2015-01-02 - v1.0.0 - First Creation
|
|
||||||
# * 2016-02-10 - v1.1.1 - Minor changes to satisfy Shellcheck
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
# Logging and Colors
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Here we set the colors for our script feedback.
|
|
||||||
# Example usage: success "sometext"
|
|
||||||
#------------------------------------------------------
|
|
||||||
|
|
||||||
# Set Colors
|
|
||||||
bold=$(tput bold)
|
|
||||||
underline=$(tput sgr 0 1)
|
|
||||||
reset=$(tput sgr0)
|
|
||||||
purple=$(tput setaf 171)
|
|
||||||
red=$(tput setaf 1)
|
|
||||||
green=$(tput setaf 76)
|
|
||||||
tan=$(tput setaf 3)
|
|
||||||
blue=$(tput setaf 38)
|
|
||||||
|
|
||||||
function _alert() {
|
|
||||||
if [ "${1}" = "emergency" ]; then
|
|
||||||
local color="${bold}${red}"
|
|
||||||
fi
|
|
||||||
if [ "${1}" = "error" ]; then local color="${bold}${red}"; fi
|
|
||||||
if [ "${1}" = "warning" ]; then local color="${red}"; fi
|
|
||||||
if [ "${1}" = "success" ]; then local color="${green}"; fi
|
|
||||||
if [ "${1}" = "debug" ]; then local color="${purple}"; fi
|
|
||||||
if [ "${1}" = "header" ]; then local color="${bold}""${tan}"; fi
|
|
||||||
if [ "${1}" = "input" ]; then local color="${bold}"; printLog="false"; fi
|
|
||||||
if [ "${1}" = "info" ] || [ "${1}" = "notice" ]; then local color=""; fi
|
|
||||||
# Don't use colors on pipes or non-recognized terminals
|
|
||||||
if [[ "${TERM}" != "xterm"* ]] || [ -t 1 ]; then color=""; reset=""; fi
|
|
||||||
|
|
||||||
# Print to $logFile
|
|
||||||
if [[ ${printLog} = "true" ]] || [ "${printLog}" == "1" ]; then
|
|
||||||
echo -e "$(date +"%m-%d-%Y %r") $(printf "[%9s]" "${1}") ${_message}" >> "${logFile}";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print to console when script is not 'quiet'
|
|
||||||
if [[ "${quiet}" = "true" ]] || [ "${quiet}" == "1" ]; then
|
|
||||||
return
|
|
||||||
else
|
|
||||||
echo -e "$(date +"%r") ${color}$(printf "[%9s]" "${1}") ${_message}${reset}";
|
|
||||||
fi
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function die () { local _message="${*} Exiting."; echo "$(_alert emergency)"; safeExit;}
|
|
||||||
function error () { local _message="${*}"; echo "$(_alert error)"; }
|
|
||||||
function warning () { local _message="${*}"; echo "$(_alert warning)"; }
|
|
||||||
function notice () { local _message="${*}"; echo "$(_alert notice)"; }
|
|
||||||
function info () { local _message="${*}"; echo "$(_alert info)"; }
|
|
||||||
function debug () { local _message="${*}"; echo "$(_alert debug)"; }
|
|
||||||
function success () { local _message="${*}"; echo "$(_alert success)"; }
|
|
||||||
function input() { local _message="${*}"; echo -n "$(_alert input)"; }
|
|
||||||
function header() { local _message="========== ${*} ========== "; echo "$(_alert header)"; }
|
|
||||||
|
|
||||||
# Log messages when verbose is set to "true"
|
|
||||||
verbose() {
|
|
||||||
if [[ "${verbose}" = "true" ]] || [ "${verbose}" == "1" ]; then
|
|
||||||
debug "$@"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Source additional /lib files
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
# First we locate this script and populate the $SCRIPTPATH variable
|
|
||||||
# Doing so allows us to source additional files from this utils file.
|
|
||||||
SOURCE="${BASH_SOURCE[0]}"
|
|
||||||
while [ -h "${SOURCE}" ]; do # resolve ${SOURCE} until the file is no longer a symlink
|
|
||||||
DIR="$( cd -P "$( dirname "${SOURCE}" )" && pwd )"
|
|
||||||
SOURCE="$(readlink "${SOURCE}")"
|
|
||||||
[[ ${SOURCE} != /* ]] && SOURCE="${DIR}/${SOURCE}" # if ${SOURCE} was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
|
||||||
done
|
|
||||||
SOURCEPATH="$( cd -P "$( dirname "${SOURCE}" )" && pwd )"
|
|
||||||
|
|
||||||
if [ ! -d "${SOURCEPATH}" ]
|
|
||||||
then
|
|
||||||
die "Failed to find library files expected in: ${SOURCEPATH}"
|
|
||||||
fi
|
|
||||||
for utility_file in "${SOURCEPATH}"/*.sh
|
|
||||||
do
|
|
||||||
if [ -e "${utility_file}" ]; then
|
|
||||||
# Don't source self
|
|
||||||
if [[ "${utility_file}" == *"utils.sh"* ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
source "$utility_file"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
516
scriptTemplate.sh
Normal file → Executable file
516
scriptTemplate.sh
Normal file → Executable file
@@ -1,157 +1,304 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# ##################################################
|
_mainScript_() {
|
||||||
# My Generic BASH script template
|
|
||||||
#
|
|
||||||
version="1.0.0" # Sets version variable
|
|
||||||
#
|
|
||||||
scriptTemplateVersion="1.5.0" # Version of scriptTemplate.sh that this script is based on
|
|
||||||
# v1.1.0 - Added 'debug' option
|
|
||||||
# v1.1.1 - Moved all shared variables to Utils
|
|
||||||
# - Added $PASS variable when -p is passed
|
|
||||||
# v1.2.0 - Added 'checkDependencies' function to ensure needed
|
|
||||||
# Bash packages are installed prior to execution
|
|
||||||
# v1.3.0 - Can now pass CLI without an option to $args
|
|
||||||
# v1.4.0 - checkDependencies now checks gems and mac apps via
|
|
||||||
# Homebrew cask
|
|
||||||
# v1.5.0 - Now has preferred IFS setting
|
|
||||||
# - Preset flags now respect true/false
|
|
||||||
# - Moved 'safeExit' function into template where it should
|
|
||||||
# have been all along.
|
|
||||||
#
|
|
||||||
# HISTORY:
|
|
||||||
#
|
|
||||||
# * DATE - v1.0.0 - First Creation
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
# Provide a variable with the location of this script.
|
info "Hello world"
|
||||||
scriptPath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
# Source Scripting Utilities
|
} # end _mainScript_
|
||||||
# -----------------------------------
|
|
||||||
# These shared utilities provide many functions which are needed to provide
|
|
||||||
# the functionality in this boilerplate. This script will fail if they can
|
|
||||||
# not be found.
|
|
||||||
# -----------------------------------
|
|
||||||
|
|
||||||
utilsLocation="${scriptPath}/lib/utils.sh" # Update this path to find the utilities.
|
# Set flags and default variables
|
||||||
|
# Script specific
|
||||||
|
|
||||||
if [ -f "${utilsLocation}" ]; then
|
# Common
|
||||||
source "${utilsLocation}"
|
LOGFILE="${HOME}/logs/$(basename "$0")"
|
||||||
|
QUIET=false
|
||||||
|
LOGLEVEL=ERROR
|
||||||
|
VERBOSE=false
|
||||||
|
FORCE=false
|
||||||
|
DRYRUN=false
|
||||||
|
declare -a args=()
|
||||||
|
now=$(LC_ALL=C date +"%m-%d-%Y %r") # Returns: 06-14-2015 10:34:40 PM
|
||||||
|
datestamp=$(LC_ALL=C date +%Y-%m-%d) # Returns: 2015-06-14
|
||||||
|
hourstamp=$(LC_ALL=C date +%r) # Returns: 10:34:40 PM
|
||||||
|
timestamp=$(LC_ALL=C date +%Y%m%d_%H%M%S) # Returns: 20150614_223440
|
||||||
|
longdate=$(LC_ALL=C date +"%a, %d %b %Y %H:%M:%S %z") # Returns: Sun, 10 Jan 2016 20:47:53 -0500
|
||||||
|
gmtdate=$(LC_ALL=C date -u -R | sed 's/\+0000/GMT/') # Returns: Wed, 13 Jan 2016 15:55:29 GMT
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
if tput setaf 1 &>/dev/null; then
|
||||||
|
bold=$(tput bold)
|
||||||
|
white=$(tput setaf 7)
|
||||||
|
reset=$(tput sgr0)
|
||||||
|
purple=$(tput setaf 171)
|
||||||
|
red=$(tput setaf 1)
|
||||||
|
green=$(tput setaf 76)
|
||||||
|
tan=$(tput setaf 3)
|
||||||
|
yellow=$(tput setaf 3)
|
||||||
|
blue=$(tput setaf 38)
|
||||||
|
underline=$(tput sgr 0 1)
|
||||||
else
|
else
|
||||||
echo "Please find the file util.sh and add a reference to it in this script. Exiting."
|
bold="\033[4;37m"
|
||||||
exit 1
|
white="\033[0;37m"
|
||||||
|
reset="\033[0m"
|
||||||
|
purple="\033[0;35m"
|
||||||
|
red="\033[0;31m"
|
||||||
|
green="\033[1;32m"
|
||||||
|
tan="\033[0;33m"
|
||||||
|
yellow="\033[0;33m"
|
||||||
|
blue="\033[0;34m"
|
||||||
|
underline="\033[4;37m"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# trapCleanup Function
|
_alert_() {
|
||||||
# -----------------------------------
|
# DESC: Controls all printing of messages to log files and stdout.
|
||||||
# Any actions that should be taken if the script is prematurely
|
# ARGS: $1 (required) - The type of alert to print
|
||||||
# exited. Always call this function at the top of your script.
|
# (success, header, notice, dryrun, debug, warning, error,
|
||||||
# -----------------------------------
|
# fatal, info, input)
|
||||||
function trapCleanup() {
|
# $2 (required) - The message to be printed to stdout and/or a log file
|
||||||
echo ""
|
# $3 (optional) - Pass '$LINENO' to print the line number where the _alert_ was triggered
|
||||||
# Delete temp files, if any
|
# OUTS: None
|
||||||
if is_dir "${tmpDir}"; then
|
# USAGE: [ALERTTYPE] "[MESSAGE]" "$LINENO"
|
||||||
rm -r "${tmpDir}"
|
# NOTES: The colors of each alert type are set in this function
|
||||||
|
# For specified alert types, the funcstac will be printed
|
||||||
|
|
||||||
|
local function_name color
|
||||||
|
local alertType="${1}"
|
||||||
|
local message="${2}"
|
||||||
|
local line="${3-}"
|
||||||
|
|
||||||
|
[ -z "${LOGFILE-}" ] && fatal "\$LOGFILE must be set"
|
||||||
|
[ ! -d "$(dirname "${LOGFILE}")" ] && mkdir -p "$(dirname "${LOGFILE}")"
|
||||||
|
|
||||||
|
if [ -z "${line}" ]; then
|
||||||
|
[[ "$1" =~ ^(fatal|error|debug|warning) && "${FUNCNAME[2]}" != "_trapCleanup_" ]] \
|
||||||
|
&& message="${message} $(_functionStack_)"
|
||||||
|
else
|
||||||
|
[[ "$1" =~ ^(fatal|error|debug) && "${FUNCNAME[2]}" != "_trapCleanup_" ]] \
|
||||||
|
&& message="${message} (line: $line) $(_functionStack_)"
|
||||||
fi
|
fi
|
||||||
die "Exit trapped. In function: '${FUNCNAME[*]}'"
|
|
||||||
|
if [ -n "${line}" ]; then
|
||||||
|
[[ "$1" =~ ^(warning|info|notice|dryrun) && "${FUNCNAME[2]}" != "_trapCleanup_" ]] \
|
||||||
|
&& message="${message} (line: $line)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${alertType}" =~ ^(error|fatal) ]]; then
|
||||||
|
color="${bold}${red}"
|
||||||
|
elif [ "${alertType}" = "warning" ]; then
|
||||||
|
color="${red}"
|
||||||
|
elif [ "${alertType}" = "success" ]; then
|
||||||
|
color="${green}"
|
||||||
|
elif [ "${alertType}" = "debug" ]; then
|
||||||
|
color="${purple}"
|
||||||
|
elif [ "${alertType}" = "header" ]; then
|
||||||
|
color="${bold}${tan}"
|
||||||
|
elif [[ "${alertType}" =~ ^(input|notice) ]]; then
|
||||||
|
color="${bold}"
|
||||||
|
elif [ "${alertType}" = "dryrun" ]; then
|
||||||
|
color="${blue}"
|
||||||
|
else
|
||||||
|
color=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
_writeToScreen_() {
|
||||||
|
|
||||||
|
("${QUIET}") && return 0 # Print to console when script is not 'quiet'
|
||||||
|
[[ ${VERBOSE} == false && "${alertType}" =~ ^(debug|verbose) ]] && return 0
|
||||||
|
|
||||||
|
if ! [[ -t 1 ]]; then # Don't use colors on non-recognized terminals
|
||||||
|
color=""
|
||||||
|
reset=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "$(date +"%r") ${color}$(printf "[%7s]" "${alertType}") ${message}${reset}"
|
||||||
|
}
|
||||||
|
_writeToScreen_
|
||||||
|
|
||||||
|
_writeToLog_() {
|
||||||
|
[[ "${alertType}" == "input" ]] && return 0
|
||||||
|
[[ "${LOGLEVEL}" =~ (off|OFF|Off) ]] && return 0
|
||||||
|
[[ ! -f "${LOGFILE}" ]] && touch "${LOGFILE}"
|
||||||
|
|
||||||
|
# Don't use colors in logs
|
||||||
|
if command -v gsed &>/dev/null; then
|
||||||
|
local cleanmessage="$(echo "${message}" | gsed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')"
|
||||||
|
else
|
||||||
|
local cleanmessage="$(echo "${message}" | sed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')"
|
||||||
|
fi
|
||||||
|
echo -e "$(date +"%b %d %R:%S") $(printf "[%7s]" "${alertType}") [$(/bin/hostname)] ${cleanmessage}" >>"${LOGFILE}"
|
||||||
}
|
}
|
||||||
|
|
||||||
# safeExit
|
# Write specified log level data to logfile
|
||||||
# -----------------------------------
|
case "${LOGLEVEL:-ERROR}" in
|
||||||
# Non destructive exit for when script exits naturally.
|
ALL|all|All)
|
||||||
# Usage: Add this function at the end of every script.
|
_writeToLog_
|
||||||
# -----------------------------------
|
;;
|
||||||
function safeExit() {
|
DEBUG|debug|Debug)
|
||||||
# Delete temp files, if any
|
_writeToLog_
|
||||||
if is_dir "${tmpDir}"; then
|
;;
|
||||||
rm -r "${tmpDir}"
|
INFO|info|Info)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal|warning|info|notice|success) ]]; then
|
||||||
|
_writeToLog_
|
||||||
fi
|
fi
|
||||||
|
;;
|
||||||
|
WARN|warn|Warn)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal|warning) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
ERROR|error|Error)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
FATAL|fatal|Fatal)
|
||||||
|
if [[ "${alertType}" =~ ^(die|fatal) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
OFF|off)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
} # /_alert_
|
||||||
|
|
||||||
|
error() { _alert_ error "${1}" "${2-}"; }
|
||||||
|
warning() { _alert_ warning "${1}" "${2-}"; }
|
||||||
|
notice() { _alert_ notice "${1}" "${2-}"; }
|
||||||
|
info() { _alert_ info "${1}" "${2-}"; }
|
||||||
|
success() { _alert_ success "${1}" "${2-}"; }
|
||||||
|
dryrun() { _alert_ dryrun "${1}" "${2-}"; }
|
||||||
|
input() { _alert_ input "${1}" "${2-}"; }
|
||||||
|
header() { _alert_ header "== ${1} ==" "${2-}"; }
|
||||||
|
die() { _alert_ fatal "${1}" "${2-}"; _safeExit_ "1" ; }
|
||||||
|
fatal() { _alert_ fatal "${1}" "${2-}"; _safeExit_ "1" ; }
|
||||||
|
debug() { _alert_ debug "${1}" "${2-}"; }
|
||||||
|
verbose() { _alert_ debug "${1}" "${2-}"; }
|
||||||
|
|
||||||
|
_safeExit_() {
|
||||||
|
# DESC: Cleanup and exit from a script
|
||||||
|
# ARGS: $1 (optional) - Exit code (defaults to 0)
|
||||||
|
# OUTS: None
|
||||||
|
|
||||||
|
if [[ -d "${script_lock-}" ]]; then
|
||||||
|
if command rm -rf "${script_lock}"; then
|
||||||
|
debug "Removing script lock"
|
||||||
|
else
|
||||||
|
warning "Script lock could not be removed. Try manually deleting ${tan}'${lock_dir}'${red}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${tmpDir-}" && -d "${tmpDir-}" ]]; then
|
||||||
|
if [[ ${1-} == 1 && -n "$(ls "${tmpDir}")" ]]; then
|
||||||
|
command rm -r "${tmpDir}"
|
||||||
|
else
|
||||||
|
command rm -r "${tmpDir}"
|
||||||
|
debug "Removing temp directory"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
trap - INT TERM EXIT
|
trap - INT TERM EXIT
|
||||||
exit
|
exit ${1:-0}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Set Flags
|
_trapCleanup_() {
|
||||||
# -----------------------------------
|
# DESC: Log errors and cleanup from script when an error is trapped
|
||||||
# Flags which can be overridden by user input.
|
# ARGS: $1 - Line number where error was trapped
|
||||||
# Default values are below
|
# $2 - Line number in function
|
||||||
# -----------------------------------
|
# $3 - Command executing at the time of the trap
|
||||||
quiet=false
|
# $4 - Names of all shell functions currently in the execution call stack
|
||||||
printLog=false
|
# $5 - Scriptname
|
||||||
verbose=false
|
# $6 - $BASH_SOURCE
|
||||||
force=false
|
# OUTS: None
|
||||||
strict=false
|
|
||||||
debug=false
|
|
||||||
args=()
|
|
||||||
|
|
||||||
# Set Temp Directory
|
local line=${1-} # LINENO
|
||||||
# -----------------------------------
|
local linecallfunc=${2-}
|
||||||
# Create temp directory with three random numbers and the process ID
|
local command="${3-}"
|
||||||
# in the name. This directory is removed automatically at exit.
|
local funcstack="${4-}"
|
||||||
# -----------------------------------
|
local script="${5-}"
|
||||||
tmpDir="/tmp/${scriptName}.$RANDOM.$RANDOM.$RANDOM.$$"
|
local sourced="${6-}"
|
||||||
|
|
||||||
|
funcstack="'$(echo "$funcstack" | sed -E 's/ / < /g')'"
|
||||||
|
|
||||||
|
if [[ "${script##*/}" == "${sourced##*/}" ]]; then
|
||||||
|
fatal "${7-} command: '$command' (line: $line) [func: $(_functionStack_)]"
|
||||||
|
else
|
||||||
|
fatal "${7-} command: '$command' (func: ${funcstack} called at line $linecallfunc of '${script##*/}') (line: $line of '${sourced##*/}') "
|
||||||
|
fi
|
||||||
|
|
||||||
|
_safeExit_ "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
_makeTempDir_() {
|
||||||
|
# DESC: Creates a temp directory to house temporary files
|
||||||
|
# ARGS: $1 (Optional) - First characters/word of directory name
|
||||||
|
# OUTS: $tmpDir - Temporary directory
|
||||||
|
# USAGE: _makeTempDir_ "$(basename "$0")"
|
||||||
|
|
||||||
|
[ -d "${tmpDir:-}" ] && return 0
|
||||||
|
|
||||||
|
if [ -n "${1-}" ]; then
|
||||||
|
tmpDir="${TMPDIR:-/tmp/}${1}.$RANDOM.$RANDOM.$$"
|
||||||
|
else
|
||||||
|
tmpDir="${TMPDIR:-/tmp/}$(basename "$0").$RANDOM.$RANDOM.$RANDOM.$$"
|
||||||
|
fi
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
(umask 077 && mkdir "${tmpDir}") || {
|
||||||
die "Could not create temporary directory! Exiting."
|
fatal "Could not create temporary directory! Exiting."
|
||||||
|
}
|
||||||
|
debug "\$tmpDir=$tmpDir"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Logging
|
_acquireScriptLock_() {
|
||||||
# -----------------------------------
|
# DESC: Acquire script lock
|
||||||
# Log is only used when the '-l' flag is set.
|
# ARGS: $1 (optional) - Scope of script execution lock (system or user)
|
||||||
#
|
# OUTS: $script_lock - Path to the directory indicating we have the script lock
|
||||||
# To never save a logfile change variable to '/dev/null'
|
# NOTE: This lock implementation is extremely simple but should be reliable
|
||||||
# Save to Desktop use: $HOME/Desktop/${scriptBasename}.log
|
# across all platforms. It does *not* support locking a script with
|
||||||
# Save to standard user log location use: $HOME/Library/Logs/${scriptBasename}.log
|
# symlinks or multiple hardlinks as there's no portable way of doing so.
|
||||||
# -----------------------------------
|
# If the lock was acquired it's automatically released in _safeExit_()
|
||||||
logFile="$HOME/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
# Check for Dependencies
|
local lock_dir
|
||||||
# -----------------------------------
|
if [[ ${1-} == 'system' ]]; then
|
||||||
# Arrays containing package dependencies needed to execute this script.
|
lock_dir="${TMPDIR:-/tmp/}$(basename "$0").lock"
|
||||||
# The script will fail if dependencies are not installed. For Mac users,
|
else
|
||||||
# most dependencies can be installed automatically using the package
|
lock_dir="${TMPDIR:-/tmp/}$(basename "$0").$UID.lock"
|
||||||
# manager 'Homebrew'. Mac applications will be installed using
|
fi
|
||||||
# Homebrew Casks. Ruby and gems via RVM.
|
|
||||||
# -----------------------------------
|
|
||||||
homebrewDependencies=()
|
|
||||||
caskDependencies=()
|
|
||||||
gemDependencies=()
|
|
||||||
|
|
||||||
function mainScript() {
|
if command mkdir "${lock_dir}" 2>/dev/null; then
|
||||||
############## Begin Script Here ###################
|
readonly script_lock="${lock_dir}"
|
||||||
####################################################
|
debug "Acquired script lock: ${tan}${script_lock}${purple}"
|
||||||
|
else
|
||||||
echo -n
|
error "Unable to acquire script lock: ${tan}${lock_dir}${red}"
|
||||||
|
fatal "If you trust the script isn't running, delete the lock dir"
|
||||||
####################################################
|
fi
|
||||||
############### End Script Here ####################
|
|
||||||
}
|
}
|
||||||
|
|
||||||
############## Begin Options and Usage ###################
|
_functionStack_() {
|
||||||
|
# DESC: Prints the function stack in use
|
||||||
|
# ARGS: None
|
||||||
# Print usage
|
# OUTS: Prints [function]:[file]:[line]
|
||||||
usage() {
|
# NOTE: Does not print functions from the alert class
|
||||||
echo -n "${scriptName} [OPTION]... [FILE]...
|
local _i
|
||||||
|
funcStackResponse=()
|
||||||
This is a script template. Edit this description to print help to users.
|
for ((_i = 1; _i < ${#BASH_SOURCE[@]}; _i++)); do
|
||||||
|
case "${FUNCNAME[$_i]}" in "_alert_" | "_trapCleanup_" | fatal | error | warning | verbose | debug | die) continue ;; esac
|
||||||
${bold}Options:${reset}
|
funcStackResponse+=("${FUNCNAME[$_i]}:$(basename ${BASH_SOURCE[$_i]}):${BASH_LINENO[$_i - 1]}")
|
||||||
-u, --username Username for script
|
done
|
||||||
-p, --password User password
|
printf "( "
|
||||||
--force Skip all user interaction. Implied 'Yes' to all actions.
|
printf %s "${funcStackResponse[0]}"
|
||||||
-q, --quiet Quiet (no output)
|
printf ' < %s' "${funcStackResponse[@]:1}"
|
||||||
-l, --log Print log to file
|
printf ' )\n'
|
||||||
-s, --strict Exit script with null variables. i.e 'set -o nounset'
|
|
||||||
-v, --verbose Output more information. (Items echoed to 'verbose')
|
|
||||||
-d, --debug Runs script in BASH debug mode (set -x)
|
|
||||||
-h, --help Display this help and exit
|
|
||||||
--version Output version information and exit
|
|
||||||
"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
_parseOptions_() {
|
||||||
# --foo bar
|
# Iterate over options
|
||||||
|
# breaking -ab into -a -b when needed and --foo=bar into --foo bar
|
||||||
optstring=h
|
optstring=h
|
||||||
unset options
|
unset options
|
||||||
while (($#)); do
|
while (($#)); do
|
||||||
@@ -161,19 +308,15 @@ while (($#)); do
|
|||||||
# Loop over each character starting with the second
|
# Loop over each character starting with the second
|
||||||
for ((i = 1; i < ${#1}; i++)); do
|
for ((i = 1; i < ${#1}; i++)); do
|
||||||
c=${1:i:1}
|
c=${1:i:1}
|
||||||
|
options+=("-$c") # Add current char to options
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
# If option takes a required argument, and it's not the last char make
|
||||||
# the rest of the string its argument
|
# the rest of the string its argument
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
if [[ $optstring == *"$c:"* && ${1:i+1} ]]; then
|
||||||
options+=("${1:i+1}")
|
options+=("${1:i+1}")
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
;;
|
;;
|
||||||
|
|
||||||
# If option is of type --foo=bar
|
# If option is of type --foo=bar
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
||||||
# add --endopts for --
|
# add --endopts for --
|
||||||
@@ -186,70 +329,65 @@ done
|
|||||||
set -- "${options[@]}"
|
set -- "${options[@]}"
|
||||||
unset options
|
unset options
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# [[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
# Read the options and set stuff
|
||||||
while [[ $1 = -?* ]]; do
|
while [[ ${1-} == -?* ]]; do
|
||||||
case $1 in
|
case $1 in
|
||||||
-h|--help) usage >&2; safeExit ;;
|
-h | --help)
|
||||||
--version) echo "$(basename $0) ${version}"; safeExit ;;
|
_usage_ >&2
|
||||||
-u|--username) shift; username=${1} ;;
|
_safeExit_
|
||||||
-p|--password) shift; echo "Enter Pass: "; stty -echo; read PASS; stty echo;
|
;;
|
||||||
echo ;;
|
-l | --loglevel)
|
||||||
-v|--verbose) verbose=true ;;
|
shift
|
||||||
-l|--log) printLog=true ;;
|
LOGLEVEL=${1}
|
||||||
-q|--quiet) quiet=true ;;
|
;;
|
||||||
-s|--strict) strict=true;;
|
-n | --dryrun) DRYRUN=true ;;
|
||||||
-d|--debug) debug=true;;
|
-v | --verbose) VERBOSE=true ;;
|
||||||
--force) force=true ;;
|
-q | --quiet) QUIET=true ;;
|
||||||
--endopts) shift; break ;;
|
--force) FORCE=true ;;
|
||||||
*) die "invalid option: '$1'." ;;
|
--endopts)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*) fatal "invalid option: '$1'." ;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
args+=("$@") # Store the remaining user input as arguments.
|
||||||
|
}
|
||||||
|
|
||||||
# Store the remaining part as arguments.
|
_usage_() {
|
||||||
args+=("$@")
|
cat <<EOF
|
||||||
|
|
||||||
############## End Options and Usage ###################
|
${bold}$(basename "$0") [OPTION]... [FILE]...${reset}
|
||||||
|
|
||||||
|
This is a script template. Edit this description to print help to users.
|
||||||
|
|
||||||
|
${bold}Options:${reset}
|
||||||
|
-h, --help Display this help and exit
|
||||||
|
-l, --loglevel One of: FATAL, ERROR, WARN, INFO, DEBUG, ALL, OFF (Default is 'ERROR')
|
||||||
|
|
||||||
|
$ $(basename "$0") --loglevel 'WARN'
|
||||||
|
|
||||||
# ############# ############# #############
|
-n, --dryrun Non-destructive. Makes no permanent changes.
|
||||||
# ## TIME TO RUN THE SCRIPT ##
|
-q, --quiet Quiet (no output)
|
||||||
# ## ##
|
-v, --verbose Output more information. (Items echoed to 'verbose')
|
||||||
# ## You shouldn't need to edit anything ##
|
--force Skip all user interaction. Implied 'Yes' to all actions.
|
||||||
# ## beneath this line ##
|
EOF
|
||||||
# ## ##
|
}
|
||||||
# ############# ############# #############
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
# Initialize and run the script
|
||||||
trap trapCleanup EXIT INT TERM
|
trap '_trapCleanup_ ${LINENO} ${BASH_LINENO} "${BASH_COMMAND}" "${FUNCNAME[*]}" "${0}" "${BASH_SOURCE[0]}"' \
|
||||||
|
EXIT INT TERM SIGINT SIGQUIT
|
||||||
# Set IFS to preferred implementation
|
set -o errtrace # Trap errors in subshells and functions
|
||||||
IFS=$'\n\t'
|
set -o errexit # Exit on error. Append '||true' if you expect an error
|
||||||
|
set -o pipefail # Use last non-zero exit code in a pipeline
|
||||||
# Exit on error. Append '||true' when you run the script if you expect an error.
|
# shopt -s nullglob globstar # Make `for f in *.txt` work when `*.txt` matches zero files
|
||||||
set -o errexit
|
IFS=$' \n\t' # Set IFS to preferred implementation
|
||||||
|
# set -o xtrace # Run in debug mode
|
||||||
# Run in debug mode, if set
|
set -o nounset # Disallow expansion of unset variables
|
||||||
if ${debug}; then set -x ; fi
|
# [[ $# -eq 0 ]] && _parseOptions_ "-h" # Force arguments when invoking the script
|
||||||
|
_parseOptions_ "$@" # Parse arguments passed to script
|
||||||
# Exit on empty variable
|
# _makeTempDir_ "$(basename "$0")" # Create a temp directory '$tmpDir'
|
||||||
if ${strict}; then set -o nounset ; fi
|
# _acquireScriptLock_ # Acquire script lock
|
||||||
|
_mainScript_ # Run the main logic script
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
_safeExit_ # Exit cleanly
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Invoke the checkDependenices function to test for Bash packages. Uncomment if needed.
|
|
||||||
# checkDependencies
|
|
||||||
|
|
||||||
# Run your script
|
|
||||||
mainScript
|
|
||||||
|
|
||||||
# Exit cleanlyd
|
|
||||||
safeExit
|
|
||||||
|
|||||||
2
sedfiles/README.md
Normal file
2
sedfiles/README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Sed Files
|
||||||
|
These files are used by utility functions to perform complex operations with sed. If you plan on using those functions, ensure they point to these files.
|
||||||
@@ -480,8 +480,9 @@ s/</</g
|
|||||||
s/</</g
|
s/</</g
|
||||||
s/>/>/g
|
s/>/>/g
|
||||||
s/>/>/g
|
s/>/>/g
|
||||||
s/&/&/g
|
s/&/\&/g
|
||||||
s/&/&/g
|
s/&/\&/g
|
||||||
|
|
||||||
# http://www.w3schools.com/tags/ref_entities.asp
|
# http://www.w3schools.com/tags/ref_entities.asp
|
||||||
# ^([^ \t]+)[ \t]+(&[^;]*;)[ \t]+(&[^;]*;).*$
|
# ^([^ \t]+)[ \t]+(&[^;]*;)[ \t]+(&[^;]*;).*$
|
||||||
# s/\2/\1/g\ns/\3/\1/g
|
# s/\2/\1/g\ns/\3/\1/g
|
||||||
1293
sedfiles/stopwords.sed
Normal file
1293
sedfiles/stopwords.sed
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,23 +0,0 @@
|
|||||||
# Setup Scripts Readme File
|
|
||||||
These are the files I have created to mange the configuration of new computers.
|
|
||||||
|
|
||||||
## File Manifest
|
|
||||||
* **casks.sh** - Installs native Mac applications via [Homebrew Cask][1] casks and runs Brew maintenance scripts.
|
|
||||||
* **dropbox.sh** - Installs [Dropbox][2] if not already installed.
|
|
||||||
* **fileTest.sh** - Maintenance script to run occasionally. This ensures that the files called from the `mackup.sh` actually exist. If any of these files fail `mackup.sh` will fail as well.
|
|
||||||
* **homebrew.sh** - Installs [Homebrew][3] and associated packages and runs Brew maintenance scripts
|
|
||||||
* **mackup.sh** - This script configures and installs [mackup][4], and then restores my files from Dropbox to their correct locations. **Note:** *This script relies on the existence of certain files in Dropbox to ensure that synching has completed. This list of files needs maintenance from time to time. You can test these files using `fileeTest.sh`*
|
|
||||||
* **newMackSetup.sh** - This is script calls all of the other scripts in the correct order to configure a new computer from scratch. **Start here**.
|
|
||||||
* **osx.sh** - This script contains the Mac OSX specific settings.
|
|
||||||
* **ruby.sh** - This script installs [RVM (Ruby Version Manager)][5] and certain Gems including Jekyll.
|
|
||||||
* **ssh.sh** - Script to configure SSH and link it to Github.
|
|
||||||
|
|
||||||
|
|
||||||
## USAGE
|
|
||||||
To configure a new computer simply run the script `newMacSetup.sh` in Terminal.app and follow the onscreen instructions.
|
|
||||||
|
|
||||||
[1]: https://github.com/caskroom/homebrew-cask
|
|
||||||
[2]: http://dropbox.com
|
|
||||||
[3]: http://brew.sh/
|
|
||||||
[4]: https://github.com/lra/mackup
|
|
||||||
[5]: http://rvm.io/
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
#
|
|
||||||
# This script was taken in its entirety from:
|
|
||||||
# https://github.com/rtrouton/rtrouton_scripts/
|
|
||||||
#
|
|
||||||
# This script will download a disk image containing the latest Adobe Flash
|
|
||||||
# Player and install Flash Player using the installer package stored inside
|
|
||||||
# the downloaded disk image.
|
|
||||||
#
|
|
||||||
# How the script works:
|
|
||||||
#
|
|
||||||
# 1. Uses curl to download a disk image containing the latest Flash Player
|
|
||||||
# installer from Adobe's web site
|
|
||||||
# 2. Renames the downloaded disk image to flash.dmg and stores it in /tmp
|
|
||||||
# 2. Mounts the disk image silently in /tmp. Disk image will not be visible
|
|
||||||
# to any logged-in user.
|
|
||||||
# 3. Installs the latest Flash Player using the installer package stored on
|
|
||||||
# the disk image
|
|
||||||
# 4. After installation, unmounts the disk image and removes it from the Mac
|
|
||||||
# in question.
|
|
||||||
#
|
|
||||||
version="1.0.0" # Sets version variable
|
|
||||||
#
|
|
||||||
scriptTemplateVersion="1.4.1" # Version of scriptTemplate.sh that this script is based on
|
|
||||||
#
|
|
||||||
# HISTORY:
|
|
||||||
#
|
|
||||||
# * 2015-06-21 - v1.0.0 - First Creation
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
# Provide a variable with the location of this script.
|
|
||||||
scriptPath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
|
|
||||||
# Source Scripting Utilities
|
|
||||||
# -----------------------------------
|
|
||||||
# These shared utilities provide many functions which are needed to provide
|
|
||||||
# the functionality in this boilerplate. This script will fail if they can
|
|
||||||
# not be found.
|
|
||||||
# -----------------------------------
|
|
||||||
|
|
||||||
utilsLocation="${scriptPath}/../lib/utils.sh" # Update this path to find the utilities.
|
|
||||||
|
|
||||||
if [ -f "${utilsLocation}" ]; then
|
|
||||||
source "${utilsLocation}"
|
|
||||||
else
|
|
||||||
echo "Please find the file util.sh and add a reference to it in this script. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# trapCleanup Function
|
|
||||||
# -----------------------------------
|
|
||||||
# Any actions that should be taken if the script is prematurely
|
|
||||||
# exited. Always call this function at the top of your script.
|
|
||||||
# -----------------------------------
|
|
||||||
function trapCleanup() {
|
|
||||||
echo ""
|
|
||||||
if is_dir "${tmpDir}"; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
die "Exit trapped." # Edit this if you like.
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set Flags
|
|
||||||
# -----------------------------------
|
|
||||||
# Flags which can be overridden by user input.
|
|
||||||
# Default values are below
|
|
||||||
# -----------------------------------
|
|
||||||
quiet=0
|
|
||||||
printLog=0
|
|
||||||
verbose=0
|
|
||||||
force=0
|
|
||||||
strict=0
|
|
||||||
debug=0
|
|
||||||
args=()
|
|
||||||
|
|
||||||
# Set Temp Directory
|
|
||||||
# -----------------------------------
|
|
||||||
# Create temp directory with three random numbers and the process ID
|
|
||||||
# in the name. This directory is removed automatically at exit.
|
|
||||||
# -----------------------------------
|
|
||||||
tmpDir="/tmp/${scriptName}.$RANDOM.$RANDOM.$RANDOM.$$"
|
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
|
||||||
die "Could not create temporary directory! Exiting."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
# -----------------------------------
|
|
||||||
# Log is only used when the '-l' flag is set.
|
|
||||||
#
|
|
||||||
# To never save a logfile change variable to '/dev/null'
|
|
||||||
# Save to Desktop use: $HOME/Desktop/${scriptBasename}.log
|
|
||||||
# Save to standard user log location use: $HOME/Library/Logs/${scriptBasename}.log
|
|
||||||
# -----------------------------------
|
|
||||||
logFile="$HOME/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
# Check for Dependencies
|
|
||||||
# -----------------------------------
|
|
||||||
# Arrays containing package dependencies needed to execute this script.
|
|
||||||
# The script will fail if dependencies are not installed. For Mac users,
|
|
||||||
# most dependencies can be installed automatically using the package
|
|
||||||
# manager 'Homebrew'. Mac applications will be installed using
|
|
||||||
# Homebrew Casks. Ruby and gems via RVM.
|
|
||||||
# -----------------------------------
|
|
||||||
homebrewDependencies=()
|
|
||||||
caskDependencies=()
|
|
||||||
gemDependencies=()
|
|
||||||
|
|
||||||
function mainScript() {
|
|
||||||
############## Begin Script Here ###################
|
|
||||||
####################################################
|
|
||||||
|
|
||||||
# invoke verbose usage of commands when set
|
|
||||||
if $verbose; then v="-v" ; fi
|
|
||||||
|
|
||||||
# Determine OS version
|
|
||||||
osvers=$(sw_vers -productVersion | awk -F. '{print $2}')
|
|
||||||
|
|
||||||
# Determine current major version of Adobe Flash for use
|
|
||||||
# with the fileURL variable
|
|
||||||
flash_major_version=`/usr/bin/curl --silent http://fpdownload2.macromedia.com/get/flashplayer/update/current/xml/version_en_mac_pl.xml | cut -d , -f 1 | awk -F\" '/update version/{print $NF}'`
|
|
||||||
|
|
||||||
# Specify the complete address of the Adobe Flash Player
|
|
||||||
# disk image
|
|
||||||
fileURL="http://fpdownload.macromedia.com/get/flashplayer/current/licensing/mac/install_flash_player_"${flash_major_version}"_osx_pkg.dmg"
|
|
||||||
|
|
||||||
flash_dmg="${tmpDir}/flash.dmg"
|
|
||||||
|
|
||||||
if [[ ${osvers} -lt 6 ]]; then
|
|
||||||
echo "Adobe Flash Player is not available for Mac OS X 10.5.8 or below."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ${osvers} -ge 6 ]]; then
|
|
||||||
|
|
||||||
# Download the latest Adobe Flash Player software disk image
|
|
||||||
|
|
||||||
/usr/bin/curl --output "${flash_dmg}" "${fileURL}"
|
|
||||||
|
|
||||||
# Specify a /tmp/flashplayer.XXXX mountpoint for the disk image
|
|
||||||
|
|
||||||
TMPMOUNT=`/usr/bin/mktemp -d ${tmpDir}/flashplayer.XXXX`
|
|
||||||
|
|
||||||
# Mount the latest Flash Player disk image to /tmp/flashplayer.XXXX mountpoint
|
|
||||||
|
|
||||||
hdiutil attach "$flash_dmg" -mountpoint "$TMPMOUNT" -nobrowse -noverify -noautoopen
|
|
||||||
|
|
||||||
pkg_path="$(/usr/bin/find $TMPMOUNT -maxdepth 1 \( -iname \*Flash*\.pkg -o -iname \*Flash*\.mpkg \))"
|
|
||||||
|
|
||||||
# Before installation on Mac OS X 10.7.x and later, the installer's
|
|
||||||
# developer certificate is checked to see if it has been signed by
|
|
||||||
# Adobe's developer certificate. Once the certificate check has been
|
|
||||||
# passed, the package is then installed.
|
|
||||||
|
|
||||||
if [[ ${pkg_path} != "" ]]; then
|
|
||||||
if [[ ${osvers} -ge 7 ]]; then
|
|
||||||
signature_check=`/usr/sbin/pkgutil --check-signature "$pkg_path" | awk /'Developer ID Installer/{ print $5 }'`
|
|
||||||
if [[ ${signature_check} = "Adobe" ]]; then
|
|
||||||
# Install Adobe Flash Player from the installer package stored inside the disk image
|
|
||||||
/usr/sbin/installer -dumplog -verbose -pkg "${pkg_path}" -target "/"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# On Mac OS X 10.6.x, the developer certificate check is not an
|
|
||||||
# available option, so the package is just installed.
|
|
||||||
|
|
||||||
if [[ ${osvers} -eq 6 ]]; then
|
|
||||||
# Install Adobe Flash Player from the installer package stored inside the disk image
|
|
||||||
/usr/sbin/installer -dumplog -verbose -pkg "${pkg_path}" -target "/"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Clean-up
|
|
||||||
|
|
||||||
# Unmount the Flash Player disk image from /tmp/flashplayer.XXXX
|
|
||||||
|
|
||||||
/usr/bin/hdiutil detach "$TMPMOUNT"
|
|
||||||
|
|
||||||
# Remove the /tmp/flashplayer.XXXX mountpoint
|
|
||||||
|
|
||||||
rm -rf $v "$TMPMOUNT"
|
|
||||||
|
|
||||||
# Remove the downloaded disk image
|
|
||||||
|
|
||||||
rm -rf $v "$flash_dmg"
|
|
||||||
fi
|
|
||||||
|
|
||||||
####################################################
|
|
||||||
############### End Script Here ####################
|
|
||||||
}
|
|
||||||
|
|
||||||
############## Begin Options and Usage ###################
|
|
||||||
|
|
||||||
|
|
||||||
# Print usage
|
|
||||||
usage() {
|
|
||||||
echo -n "${scriptName} [OPTION]... [FILE]...
|
|
||||||
|
|
||||||
This script was taken in its entirety from:
|
|
||||||
https://github.com/rtrouton/rtrouton_scripts/
|
|
||||||
|
|
||||||
This script will download a disk image containing the latest Adobe Flash
|
|
||||||
Player and install Flash Player using the installer package stored inside
|
|
||||||
the downloaded disk image.
|
|
||||||
|
|
||||||
How the script works:
|
|
||||||
|
|
||||||
1. Uses curl to download a disk image containing the latest Flash Player
|
|
||||||
installer from Adobe's web site
|
|
||||||
2. Renames the downloaded disk image to flash.dmg and stores it in /tmp
|
|
||||||
2. Mounts the disk image silently in /tmp. Disk image will not be visible
|
|
||||||
to any logged-in user.
|
|
||||||
3. Installs the latest Flash Player using the installer package stored on
|
|
||||||
the disk image
|
|
||||||
4. After installation, unmounts the disk image and removes it from the Mac
|
|
||||||
in question.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
|
|
||||||
-q, --quiet Quiet (no output)
|
|
||||||
-l, --log Print log to file
|
|
||||||
-s, --strict Exit script with null variables. i.e 'set -o nounset'
|
|
||||||
-v, --verbose Output more information. (Items echoed to 'verbose')
|
|
||||||
-d, --debug Runs script in BASH debug mode (set -x)
|
|
||||||
-h, --help Display this help and exit
|
|
||||||
--version Output version information and exit
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
|
||||||
# --foo bar
|
|
||||||
optstring=h
|
|
||||||
unset options
|
|
||||||
while (($#)); do
|
|
||||||
case $1 in
|
|
||||||
# If option is of type -ab
|
|
||||||
-[!-]?*)
|
|
||||||
# Loop over each character starting with the second
|
|
||||||
for ((i=1; i < ${#1}; i++)); do
|
|
||||||
c=${1:i:1}
|
|
||||||
|
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
|
||||||
# the rest of the string its argument
|
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
|
||||||
options+=("${1:i+1}")
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
|
|
||||||
# If option is of type --foo=bar
|
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
|
||||||
# add --endopts for --
|
|
||||||
--) options+=(--endopts) ;;
|
|
||||||
# Otherwise, nothing special
|
|
||||||
*) options+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
set -- "${options[@]}"
|
|
||||||
unset options
|
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# [[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
|
||||||
while [[ $1 = -?* ]]; do
|
|
||||||
case $1 in
|
|
||||||
-h|--help) usage >&2; safeExit ;;
|
|
||||||
--version) echo "$(basename $0) ${version}"; safeExit ;;
|
|
||||||
-u|--username) shift; username=${1} ;;
|
|
||||||
-p|--password) shift; echo "Enter Pass: "; stty -echo; read PASS; stty echo;
|
|
||||||
echo ;;
|
|
||||||
-v|--verbose) verbose=true ;;
|
|
||||||
-l|--log) printLog=1 ;;
|
|
||||||
-q|--quiet) quiet=1 ;;
|
|
||||||
-s|--strict) strict=1;;
|
|
||||||
-d|--debug) debug=1;;
|
|
||||||
--force) force=1 ;;
|
|
||||||
--endopts) shift; break ;;
|
|
||||||
*) die "invalid option: '$1'." ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Store the remaining part as arguments.
|
|
||||||
args+=("$@")
|
|
||||||
|
|
||||||
############## End Options and Usage ###################
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ############# ############# #############
|
|
||||||
# ## TIME TO RUN THE SCRIPT ##
|
|
||||||
# ## ##
|
|
||||||
# ## You shouldn't need to edit anything ##
|
|
||||||
# ## beneath this line ##
|
|
||||||
# ## ##
|
|
||||||
# ############# ############# #############
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
|
||||||
trap trapCleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Set IFS to preferred implementation
|
|
||||||
IFS=$'\n\t'
|
|
||||||
|
|
||||||
# Exit on error. Append '||true' when you run the script if you expect an error.
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
# Run in debug mode, if set
|
|
||||||
if [ "${debug}" == "1" ]; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Exit on empty variable
|
|
||||||
if [ "${strict}" == "1" ]; then
|
|
||||||
set -o nounset
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Invoke the checkDependenices function to test for Bash packages
|
|
||||||
# checkDependencies
|
|
||||||
|
|
||||||
# Run your script
|
|
||||||
mainScript
|
|
||||||
|
|
||||||
safeExit # Exit cleanly
|
|
||||||
@@ -1,929 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
#
|
|
||||||
version="1.0.0" # Sets version variable
|
|
||||||
#
|
|
||||||
# HISTORY:
|
|
||||||
#
|
|
||||||
# * 2016-04-19 - v1.0.0 - First Creation
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
|
|
||||||
function mainScript() {
|
|
||||||
# invoke verbose usage when set
|
|
||||||
if ${verbose}; then v="-v" ; fi
|
|
||||||
|
|
||||||
# Helper Functions
|
|
||||||
# ###################
|
|
||||||
function isAppInstalled() {
|
|
||||||
# Feed this function either the bundleID (com.apple.finder) or a name (finder) for a native
|
|
||||||
# mac app and it will determine whether it is installed or not
|
|
||||||
#
|
|
||||||
# usage: if isAppInstalled 'finder' &>/dev/null; then ...
|
|
||||||
#
|
|
||||||
# http://stackoverflow.com/questions/6682335/how-can-check-if-particular-application-software-is-installed-in-mac-os
|
|
||||||
|
|
||||||
local appNameOrBundleId="$1" isAppName=0 bundleId
|
|
||||||
# Determine whether an app *name* or *bundle ID* was specified.
|
|
||||||
[[ $appNameOrBundleId =~ \.[aA][pP][pP]$ || $appNameOrBundleId =~ ^[^.]+$ ]] && isAppName=1
|
|
||||||
if (( isAppName )); then # an application NAME was specified
|
|
||||||
# Translate to a bundle ID first.
|
|
||||||
bundleId=$(osascript -e "id of application \"$appNameOrBundleId\"" 2>/dev/null) ||
|
|
||||||
{ echo "$FUNCNAME: ERROR: Application with specified name not found: $appNameOrBundleId" 1>&2; return 1; }
|
|
||||||
else # a BUNDLE ID was specified
|
|
||||||
bundleId=$appNameOrBundleId
|
|
||||||
fi
|
|
||||||
# Let AppleScript determine the full bundle path.
|
|
||||||
osascript -e "tell application \"Finder\" to POSIX path of (get application file id \"$bundleId\" as alias)" 2>/dev/null ||
|
|
||||||
{ echo "$FUNCNAME: ERROR: Application with specified bundle ID not found: $bundleId" 1>&2; return 1; }
|
|
||||||
}
|
|
||||||
|
|
||||||
function brewMaintenance () {
|
|
||||||
# brewMaintenance
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# Will run the recommended Homebrew maintenance scripts
|
|
||||||
# ------------------------------------------------------
|
|
||||||
seek_confirmation "Run Homebrew maintenance?"
|
|
||||||
if is_confirmed; then
|
|
||||||
brew doctor
|
|
||||||
brew update
|
|
||||||
brew upgrade --all
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function brewCleanup () {
|
|
||||||
# This function cleans up an initial Homebrew installation
|
|
||||||
|
|
||||||
notice "Running Homebrew maintenance..."
|
|
||||||
|
|
||||||
# This is where brew stores its binary symlinks
|
|
||||||
binroot="$(brew --config | awk '/HOMEBREW_PREFIX/ {print $2}')"/bin
|
|
||||||
|
|
||||||
if [[ "$(type -P ${binroot}/bash)" && "$(cat /etc/shells | grep -q "$binroot/bash")" ]]; then
|
|
||||||
info "Adding ${binroot}/bash to the list of acceptable shells"
|
|
||||||
echo "$binroot/bash" | sudo tee -a /etc/shells >/dev/null
|
|
||||||
fi
|
|
||||||
if [[ "$SHELL" != "${binroot}/bash" ]]; then
|
|
||||||
info "Making ${binroot}/bash your default shell"
|
|
||||||
sudo chsh -s "${binroot}/bash" "$USER" >/dev/null 2>&1
|
|
||||||
success "Please exit and restart all your shells."
|
|
||||||
fi
|
|
||||||
|
|
||||||
brew cleanup
|
|
||||||
|
|
||||||
if brew cask > /dev/null; then
|
|
||||||
brew cask cleanup
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function doInstall () {
|
|
||||||
# Reads a list of items, checks if they are installed, installs
|
|
||||||
# those which are needed.
|
|
||||||
#
|
|
||||||
# Variables needed are:
|
|
||||||
# LISTINSTALLED: The command to list all previously installed items
|
|
||||||
# Ex: "brew list" or "gem list | awk '{print $1}'"
|
|
||||||
#
|
|
||||||
# INSTALLCOMMAND: The Install command for the desired items.
|
|
||||||
# Ex: "brew install" or "gem install"
|
|
||||||
#
|
|
||||||
# RECIPES: The list of packages to install.
|
|
||||||
# Ex: RECIPES=(
|
|
||||||
# package1
|
|
||||||
# package2
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# Credit: https://github.com/cowboy/dotfiles
|
|
||||||
|
|
||||||
function to_install() {
|
|
||||||
local desired installed i desired_s installed_s remain
|
|
||||||
# Convert args to arrays, handling both space- and newline-separated lists.
|
|
||||||
read -ra desired < <(echo "$1" | tr '\n' ' ')
|
|
||||||
read -ra installed < <(echo "$2" | tr '\n' ' ')
|
|
||||||
# Sort desired and installed arrays.
|
|
||||||
unset i; while read -r; do desired_s[i++]=$REPLY; done < <(
|
|
||||||
printf "%s\n" "${desired[@]}" | sort
|
|
||||||
)
|
|
||||||
unset i; while read -r; do installed_s[i++]=$REPLY; done < <(
|
|
||||||
printf "%s\n" "${installed[@]}" | sort
|
|
||||||
)
|
|
||||||
# Get the difference. comm is awesome.
|
|
||||||
unset i; while read -r; do remain[i++]=$REPLY; done < <(
|
|
||||||
comm -13 <(printf "%s\n" "${installed_s[@]}") <(printf "%s\n" "${desired_s[@]}")
|
|
||||||
)
|
|
||||||
echo "${remain[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkInstallItems() {
|
|
||||||
# If we are working with 'cask' we need to dedupe lists
|
|
||||||
# since apps might be installed by hand
|
|
||||||
if [[ $INSTALLCOMMAND =~ cask ]]; then
|
|
||||||
if isAppInstalled "${item}" &>/dev/null; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
# If we installing from mas (mac app store), we need to dedupe the list AND
|
|
||||||
# sign in to the app store
|
|
||||||
if [[ $INSTALLCOMMAND =~ mas ]]; then
|
|
||||||
# Lookup the name of the application being installed
|
|
||||||
appName="$(curl -s https://itunes.apple.com/lookup?id=$item | jq .results[].trackName)"
|
|
||||||
if isAppInstalled "${appName}" &> /dev/null; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
# Tell the user the name of the app
|
|
||||||
notice "$item --> $appName"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Log in to the Mac App Store if using mas
|
|
||||||
if [[ $INSTALLCOMMAND =~ mas ]]; then
|
|
||||||
mas signout
|
|
||||||
input "Please enter your Mac app store username: "
|
|
||||||
read macStoreUsername
|
|
||||||
input "Please enter your Mac app store password: "
|
|
||||||
read -s macStorePass
|
|
||||||
echo ""
|
|
||||||
mas signin $macStoreUsername "$macStorePass"
|
|
||||||
fi
|
|
||||||
|
|
||||||
list=($(to_install "${RECIPES[*]}" "$(${LISTINSTALLED})"))
|
|
||||||
|
|
||||||
if [ ${#list[@]} -gt 0 ]; then
|
|
||||||
seek_confirmation "Confirm each package before installing?"
|
|
||||||
if is_confirmed; then
|
|
||||||
for item in "${list[@]}"; do
|
|
||||||
checkInstallItems
|
|
||||||
seek_confirmation "Install ${item}?"
|
|
||||||
if is_confirmed; then
|
|
||||||
notice "Installing ${item}"
|
|
||||||
# FFMPEG takes additional flags
|
|
||||||
if [[ "${item}" = "ffmpeg" ]]; then
|
|
||||||
installffmpeg
|
|
||||||
elif [[ "${item}" = "tldr" ]]; then
|
|
||||||
brew tap tldr-pages/tldr
|
|
||||||
brew install tldr
|
|
||||||
else
|
|
||||||
${INSTALLCOMMAND} "${item}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
for item in "${list[@]}"; do
|
|
||||||
checkInstallItems
|
|
||||||
notice "Installing ${item}"
|
|
||||||
# FFMPEG takes additional flags
|
|
||||||
if [[ "${item}" = "ffmpeg" ]]; then
|
|
||||||
installffmpeg
|
|
||||||
elif [[ "${item}" = "tldr" ]]; then
|
|
||||||
brew tap tldr-pages/tldr
|
|
||||||
brew install tldr
|
|
||||||
else
|
|
||||||
${INSTALLCOMMAND} "${item}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Installation Commands
|
|
||||||
# ###################
|
|
||||||
function installCommandLineTools() {
|
|
||||||
notice "Checking for Command Line Tools..."
|
|
||||||
|
|
||||||
if [[ ! "$(type -P gcc)" || ! "$(type -P make)" ]]; then
|
|
||||||
local osx_vers=$(sw_vers -productVersion | awk -F "." '{print $2}')
|
|
||||||
local cmdLineToolsTmp="${tmpDir}/.com.apple.dt.CommandLineTools.installondemand.in-progress"
|
|
||||||
|
|
||||||
# Create the placeholder file which is checked by the software update tool
|
|
||||||
# before allowing the installation of the Xcode command line tools.
|
|
||||||
touch "${cmdLineToolsTmp}"
|
|
||||||
|
|
||||||
# Find the last listed update in the Software Update feed with "Command Line Tools" in the name
|
|
||||||
cmd_line_tools=$(softwareupdate -l | awk '/\*\ Command Line Tools/ { $1=$1;print }' | tail -1 | sed 's/^[[ \t]]*//;s/[[ \t]]*$//;s/*//' | cut -c 2-)
|
|
||||||
|
|
||||||
softwareupdate -i "${cmd_line_tools}" -v
|
|
||||||
|
|
||||||
# Remove the temp file
|
|
||||||
if [ -f "${cmdLineToolsTmp}" ]; then
|
|
||||||
rm ${v} "${cmdLineToolsTmp}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "Command Line Tools installed"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installHomebrew () {
|
|
||||||
# Check for Homebrew
|
|
||||||
notice "Checking for Homebrew..."
|
|
||||||
if [ ! "$(type -P brew)" ]; then
|
|
||||||
notice "No Homebrew. Gots to install it..."
|
|
||||||
# Ensure that we can actually, like, compile anything.
|
|
||||||
if [[ ! $(type -P gcc) && "$OSTYPE" =~ ^darwin ]]; then
|
|
||||||
notice "XCode or the Command Line Tools for XCode must be installed first."
|
|
||||||
installCommandLineTools
|
|
||||||
fi
|
|
||||||
# Check for Git
|
|
||||||
if [ ! "$(type -P git)" ]; then
|
|
||||||
notice "XCode or the Command Line Tools for XCode must be installed first."
|
|
||||||
installCommandLineTools
|
|
||||||
fi
|
|
||||||
# Install Homebrew
|
|
||||||
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
|
|
||||||
|
|
||||||
installHomebrewTaps
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "Homebrew installed"
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkTaps() {
|
|
||||||
|
|
||||||
verbose "Confirming we have required Homebrew taps"
|
|
||||||
if ! brew cask help &>/dev/null; then
|
|
||||||
installHomebrewTaps
|
|
||||||
fi
|
|
||||||
if [ ! "$(type -P mas)" ]; then
|
|
||||||
installHomebrewTaps
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function installHomebrewTaps() {
|
|
||||||
brew tap homebrew/dupes
|
|
||||||
brew tap homebrew/versions
|
|
||||||
brew install argon/mas/mas
|
|
||||||
brew tap argon/mas
|
|
||||||
brew tap caskroom/cask
|
|
||||||
# brew tap caskroom/fonts
|
|
||||||
brew tap caskroom/versions
|
|
||||||
}
|
|
||||||
|
|
||||||
function installXcode() {
|
|
||||||
notice "Checking for XCode..."
|
|
||||||
if ! isAppInstalled 'xcode' &>/dev/null; then
|
|
||||||
unset LISTINSTALLED INSTALLCOMMAND RECIPES
|
|
||||||
|
|
||||||
checkTaps
|
|
||||||
|
|
||||||
LISTINSTALLED="mas list"
|
|
||||||
INSTALLCOMMAND="mas install"
|
|
||||||
RECIPES=(
|
|
||||||
497799835 #xCode
|
|
||||||
)
|
|
||||||
doInstall
|
|
||||||
|
|
||||||
# we also accept the license
|
|
||||||
sudo xcodebuild -license accept
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "XCode installed"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installDropbox () {
|
|
||||||
# This function checks for Dropbox being installed.
|
|
||||||
# If it is not found, we install it and its prerequisites
|
|
||||||
notice "Checking for Dropbox..."
|
|
||||||
|
|
||||||
checkTaps
|
|
||||||
|
|
||||||
if ! isAppInstalled 'Dropbox' &>/dev/null; then
|
|
||||||
unset LISTINSTALLED INSTALLCOMMAND RECIPES
|
|
||||||
LISTINSTALLED="brew cask list"
|
|
||||||
INSTALLCOMMAND="brew cask install --appdir=/Applications"
|
|
||||||
RECIPES=(
|
|
||||||
dropbox
|
|
||||||
)
|
|
||||||
doInstall
|
|
||||||
open -a dropbox
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "Dropbox installed"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installffmpeg () {
|
|
||||||
|
|
||||||
notice "Checking for ffmpeg...."
|
|
||||||
# My preferred install of ffmpeg
|
|
||||||
if [ ! $(type -P "ffmpeg") ]; then
|
|
||||||
brew install ffmpeg --with-faac --with-fdk-aac --with-ffplay --with-fontconfig --with-freetype --with-libcaca --with-libass --with-frei0r --with-libass --with-libbluray --with-libcaca --with-libquvi --with-libvidstab --with-libsoxr --with-libssh --with-libvo-aacenc --with-libvidstab --with-libvorbis --with-libvpx --with-opencore-amr --with-openjpeg --with-openssl --with-opus --with-rtmpdump --with-schroedinger --with-speex --with-theora --with-tools --with-webp --with-x265
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "Done ffmpeg installed"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installCaskApps() {
|
|
||||||
unset LISTINSTALLED INSTALLCOMMAND RECIPES
|
|
||||||
|
|
||||||
notice "Checking for casks to install..."
|
|
||||||
|
|
||||||
checkTaps
|
|
||||||
|
|
||||||
LISTINSTALLED="brew cask list"
|
|
||||||
INSTALLCOMMAND="brew cask install --appdir=/Applications"
|
|
||||||
RECIPES=(
|
|
||||||
alfred
|
|
||||||
arq
|
|
||||||
bartender
|
|
||||||
betterzipql
|
|
||||||
carbon-copy-cloner
|
|
||||||
controlplane
|
|
||||||
dash
|
|
||||||
default-folder-x
|
|
||||||
fantastical
|
|
||||||
firefox
|
|
||||||
flux
|
|
||||||
fluid
|
|
||||||
google-chrome
|
|
||||||
hazel
|
|
||||||
houdahgeo
|
|
||||||
iterm2
|
|
||||||
istat-menus
|
|
||||||
java
|
|
||||||
marked
|
|
||||||
mailplane
|
|
||||||
moom
|
|
||||||
ngrok
|
|
||||||
nvalt
|
|
||||||
omnifocus
|
|
||||||
omnifocus-clip-o-tron
|
|
||||||
1password
|
|
||||||
plex-home-theater
|
|
||||||
qlcolorcode
|
|
||||||
qlmarkdown
|
|
||||||
qlprettypatch
|
|
||||||
qlstephen
|
|
||||||
quicklook-csv
|
|
||||||
quicklook-json
|
|
||||||
skitch
|
|
||||||
spillo
|
|
||||||
sublime-text3
|
|
||||||
textexpander
|
|
||||||
trickster
|
|
||||||
vlc
|
|
||||||
vyprvpn
|
|
||||||
webpquicklook
|
|
||||||
xld
|
|
||||||
)
|
|
||||||
|
|
||||||
# for item in "${RECIPES[@]}"; do
|
|
||||||
# info "$item"
|
|
||||||
# done
|
|
||||||
doInstall
|
|
||||||
|
|
||||||
success "Done installing cask apps"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installAppStoreApps() {
|
|
||||||
unset LISTINSTALLED INSTALLCOMMAND RECIPES
|
|
||||||
|
|
||||||
notice "Checking for App Store apps to install..."
|
|
||||||
|
|
||||||
checkTaps
|
|
||||||
|
|
||||||
LISTINSTALLED="mas list"
|
|
||||||
INSTALLCOMMAND="mas install"
|
|
||||||
RECIPES=(
|
|
||||||
836505650 # Battery Monitor
|
|
||||||
420212497 # Byword
|
|
||||||
696977615 # Capo
|
|
||||||
411643860 # DaisyDisk
|
|
||||||
498944723 # JPEGmini
|
|
||||||
711830901 # OmniGraffle
|
|
||||||
429449079 # Patterns - RegEx Validation
|
|
||||||
445189367 # PopClip
|
|
||||||
803453959 # Slack
|
|
||||||
403388562 # Transmit
|
|
||||||
494803304 # WiFi Explorer
|
|
||||||
848311469 # Write
|
|
||||||
)
|
|
||||||
doInstall
|
|
||||||
|
|
||||||
success "Done installing app store apps"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installDevApps() {
|
|
||||||
unset LISTINSTALLED INSTALLCOMMAND RECIPES
|
|
||||||
|
|
||||||
notice "Checking for dev apps to install"
|
|
||||||
|
|
||||||
checkTaps
|
|
||||||
|
|
||||||
LISTINSTALLED="brew cask list"
|
|
||||||
INSTALLCOMMAND="brew cask install --appdir=/Applications"
|
|
||||||
RECIPES=(
|
|
||||||
charles
|
|
||||||
codekit
|
|
||||||
github
|
|
||||||
imagealpha
|
|
||||||
imageoptim
|
|
||||||
java
|
|
||||||
kaleidoscope
|
|
||||||
licecap # Movie screen captures
|
|
||||||
mamp # mac-based LAMP development stack
|
|
||||||
paw # REST IDE
|
|
||||||
tower # Mac GUI for git
|
|
||||||
)
|
|
||||||
|
|
||||||
# for item in "${RECIPES[@]}"; do
|
|
||||||
# info "$item"
|
|
||||||
# done
|
|
||||||
doInstall
|
|
||||||
|
|
||||||
success "Done installing dev apps"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installHomebrewPackages() {
|
|
||||||
unset LISTINSTALLED INSTALLCOMMAND RECIPES
|
|
||||||
|
|
||||||
notice "Checking for Homebrew packages to install..."
|
|
||||||
|
|
||||||
checkTaps
|
|
||||||
|
|
||||||
LISTINSTALLED="brew list"
|
|
||||||
INSTALLCOMMAND="brew install"
|
|
||||||
|
|
||||||
RECIPES=(
|
|
||||||
autoconf
|
|
||||||
automake
|
|
||||||
bash
|
|
||||||
bash-completion
|
|
||||||
colordiff
|
|
||||||
coreutils
|
|
||||||
ffmpeg
|
|
||||||
gifsicle
|
|
||||||
git
|
|
||||||
git-extras
|
|
||||||
git-flow
|
|
||||||
hub
|
|
||||||
hr
|
|
||||||
id3tool
|
|
||||||
imagemagick
|
|
||||||
jpegoptim
|
|
||||||
jq
|
|
||||||
lesspipe
|
|
||||||
libksba
|
|
||||||
libtool
|
|
||||||
libyaml
|
|
||||||
mackup
|
|
||||||
man2html
|
|
||||||
multimarkdown
|
|
||||||
node
|
|
||||||
openssl
|
|
||||||
optipng
|
|
||||||
pkg-config
|
|
||||||
pngcrush
|
|
||||||
p7zip
|
|
||||||
readline
|
|
||||||
rename
|
|
||||||
shellcheck # Bash linter
|
|
||||||
sl
|
|
||||||
source-highlight
|
|
||||||
ssh-copy-id
|
|
||||||
sqlite
|
|
||||||
tag
|
|
||||||
terminal-notifier
|
|
||||||
tldr # Better man pages
|
|
||||||
tree
|
|
||||||
unison # Rsynch like tool
|
|
||||||
)
|
|
||||||
doInstall
|
|
||||||
|
|
||||||
success "Done installing Homebrew packages"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installRuby() {
|
|
||||||
|
|
||||||
notice "Checking for RVM (Ruby Version Manager)..."
|
|
||||||
|
|
||||||
local RUBYVERSION="2.1.2" # Version of Ruby to install via RVM
|
|
||||||
|
|
||||||
# Check for RVM
|
|
||||||
if [ ! "$(type -P rvm)" ]; then
|
|
||||||
seek_confirmation "Couldn't find RVM. Install it?"
|
|
||||||
if is_confirmed; then
|
|
||||||
curl -L https://get.rvm.io | bash -s stable
|
|
||||||
source "${HOME}/.rvm/scripts/rvm"
|
|
||||||
source "${HOME}/.bash_profile"
|
|
||||||
#rvm get stable --autolibs=enable
|
|
||||||
rvm install ${RUBYVERSION}
|
|
||||||
rvm use ${RUBYVERSION} --default
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "RVM and Ruby are installed"
|
|
||||||
}
|
|
||||||
|
|
||||||
function installRubyGems() {
|
|
||||||
unset LISTINSTALLED INSTALLCOMMAND RECIPES
|
|
||||||
|
|
||||||
notice "Checking for Ruby gems..."
|
|
||||||
|
|
||||||
LISTINSTALLED="gem list | awk '{print $1}'"
|
|
||||||
INSTALLCOMMAND="gem install"
|
|
||||||
|
|
||||||
RECIPES=(
|
|
||||||
bundler
|
|
||||||
classifier
|
|
||||||
compass
|
|
||||||
digest
|
|
||||||
fileutils
|
|
||||||
jekyll
|
|
||||||
kramdown
|
|
||||||
kss
|
|
||||||
less
|
|
||||||
logger
|
|
||||||
mini_magick
|
|
||||||
rake
|
|
||||||
reduce
|
|
||||||
s3_website
|
|
||||||
sass
|
|
||||||
smusher
|
|
||||||
)
|
|
||||||
|
|
||||||
doInstall
|
|
||||||
|
|
||||||
success "Done installing Ruby Gems"
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureSSH() {
|
|
||||||
notice "Configuring SSH"
|
|
||||||
|
|
||||||
info "Checking for SSH key in ~/.ssh/id_rsa.pub, generating one if it doesn't exist"
|
|
||||||
[[ -f "${HOME}/.ssh/id_rsa.pub" ]] || ssh-keygen -t rsa
|
|
||||||
|
|
||||||
info "Copying public key to clipboard"
|
|
||||||
[[ -f "${HOME}/.ssh/id_rsa.pub" ]] && cat "${HOME}/.ssh/id_rsa.pub" | pbcopy
|
|
||||||
|
|
||||||
# Add SSH keys to Github
|
|
||||||
seek_confirmation "Add SSH key to Github?"
|
|
||||||
if is_confirmed; then
|
|
||||||
info "Paste the key into Github"
|
|
||||||
|
|
||||||
open https://github.com/account/ssh
|
|
||||||
|
|
||||||
seek_confirmation "Test Github Authentication via ssh?"
|
|
||||||
if is_confirmed; then
|
|
||||||
info "Note that even when successful, this will fail the script."
|
|
||||||
ssh -T git@github.com
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "SSH Configured"
|
|
||||||
}
|
|
||||||
|
|
||||||
function configureMackup() {
|
|
||||||
notice "Running mackup config..."
|
|
||||||
|
|
||||||
local DIRCFG="${HOME}/Dropbox/sharedConfiguration/Mackup"
|
|
||||||
|
|
||||||
installDropbox
|
|
||||||
|
|
||||||
dropboxFilesTest=(
|
|
||||||
"Dropbox/sharedConfiguration/Mackup/Library/Application Support/PaxGalaxia/net.txt"
|
|
||||||
"Dropbox/sharedConfiguration/Mackup/Pictures/DeviantartBackup/clouds2.jpg"
|
|
||||||
"Dropbox/sharedConfiguration/Mackup/Library/init/bash/aliases.bash"
|
|
||||||
"Dropbox/sharedConfiguration/Mackup/.mackup/my-files.cfg"
|
|
||||||
"Dropbox/sharedConfiguration/App Configuration Files/Alfred2/Alfred.alfredpreferences"
|
|
||||||
"Dropbox/sharedConfiguration/Mackup/Library/Preferences/com.dustinrue.ControlPlane.plist"
|
|
||||||
)
|
|
||||||
|
|
||||||
info "Confirming that Dropbox has synced by looking for files..."
|
|
||||||
info "(This might fail if the list of files is out of date)"
|
|
||||||
|
|
||||||
for dropboxFile in "${dropboxFilesTest[@]}"; do
|
|
||||||
verbose "Checking: $dropboxFile"
|
|
||||||
while [ ! -e "${HOME}/${dropboxFile}" ]; do
|
|
||||||
info " Waiting for Dropbox to Sync files..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
#Add some additional time just to be sure....
|
|
||||||
for ((i=1; i<=6; i++)); do
|
|
||||||
info " Waiting for Dropbox to Sync files..."
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
|
|
||||||
# Sync Complete
|
|
||||||
success "Dropbox has synced"
|
|
||||||
|
|
||||||
# Confirm Mackup exists
|
|
||||||
if [ ! "$(type -P mackup)" ]; then
|
|
||||||
installHomebrew
|
|
||||||
brew install mackup
|
|
||||||
fi
|
|
||||||
|
|
||||||
notice "Checking for Mackup config files..."
|
|
||||||
if [ ! -L "${HOME}/.mackup" ]; then
|
|
||||||
info "Symlinking ~/.mackup"
|
|
||||||
ln -s "${MACKUPDIR}/.mackup" "${HOME}/.mackup"
|
|
||||||
else
|
|
||||||
verbose "${HOME}/.mackup is symlinked"
|
|
||||||
fi
|
|
||||||
if [ ! -L "${HOME}/.mackup.cfg" ]; then
|
|
||||||
info "Symlinking ~/.mackup.cfg"
|
|
||||||
ln -s "${MACKUPDIR}"/.mackup.cfg "${HOME}"/.mackup.cfg
|
|
||||||
else
|
|
||||||
verbose "~${HOME}.mackup.cfg is symlinked"
|
|
||||||
fi
|
|
||||||
success "Mackup config files are symlinked"
|
|
||||||
|
|
||||||
seek_confirmation "Run Mackup Restore?"
|
|
||||||
if is_confirmed; then
|
|
||||||
mackup restore
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# ###################
|
|
||||||
# Run the script
|
|
||||||
# ###################
|
|
||||||
|
|
||||||
# Ask for the administrator password upfront
|
|
||||||
sudo -v
|
|
||||||
|
|
||||||
installCommandLineTools
|
|
||||||
installHomebrew
|
|
||||||
checkTaps
|
|
||||||
brewCleanup
|
|
||||||
installXcode
|
|
||||||
installDropbox
|
|
||||||
installHomebrewPackages
|
|
||||||
installCaskApps
|
|
||||||
installAppStoreApps
|
|
||||||
installDevApps
|
|
||||||
installRuby
|
|
||||||
installRubyGems
|
|
||||||
configureSSH
|
|
||||||
configureMackup
|
|
||||||
}
|
|
||||||
|
|
||||||
## SET SCRIPTNAME VARIABLE ##
|
|
||||||
scriptName=$(basename "$0")
|
|
||||||
|
|
||||||
function trapCleanup() {
|
|
||||||
# trapCleanup Function
|
|
||||||
# -----------------------------------
|
|
||||||
# Any actions that should be taken if the script is prematurely
|
|
||||||
# exited. Always call this function at the top of your script.
|
|
||||||
# -----------------------------------
|
|
||||||
echo ""
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
die "Exit trapped."
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeExit() {
|
|
||||||
# safeExit
|
|
||||||
# -----------------------------------
|
|
||||||
# Non destructive exit for when script exits naturally.
|
|
||||||
# Usage: Add this function at the end of every script.
|
|
||||||
# -----------------------------------
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
trap - INT TERM EXIT
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
function seek_confirmation() {
|
|
||||||
# Asks questions of a user and then does something with the answer.
|
|
||||||
# y/n are the only possible answers.
|
|
||||||
#
|
|
||||||
# USAGE:
|
|
||||||
# seek_confirmation "Ask a question"
|
|
||||||
# if is_confirmed; then
|
|
||||||
# some action
|
|
||||||
# else
|
|
||||||
# some other action
|
|
||||||
# fi
|
|
||||||
#
|
|
||||||
# Credt: https://github.com/kevva/dotfiles
|
|
||||||
# ------------------------------------------------------
|
|
||||||
|
|
||||||
input "$@"
|
|
||||||
if ${force}; then
|
|
||||||
notice "Forcing confirmation with '--force' flag set"
|
|
||||||
else
|
|
||||||
read -p " (y/n) " -n 1
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_confirmed() {
|
|
||||||
if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_confirmed() {
|
|
||||||
if [[ "${REPLY}" =~ ^[Nn]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set Flags
|
|
||||||
# -----------------------------------
|
|
||||||
# Flags which can be overridden by user input.
|
|
||||||
# Default values are below
|
|
||||||
# -----------------------------------
|
|
||||||
quiet=false
|
|
||||||
printLog=false
|
|
||||||
verbose=false
|
|
||||||
force=false
|
|
||||||
strict=false
|
|
||||||
debug=false
|
|
||||||
args=()
|
|
||||||
|
|
||||||
# Set Temp Directory
|
|
||||||
# -----------------------------------
|
|
||||||
# Create temp directory with three random numbers and the process ID
|
|
||||||
# in the name. This directory is removed automatically at exit.
|
|
||||||
# -----------------------------------
|
|
||||||
tmpDir="/tmp/${scriptName}.$RANDOM.$RANDOM.$RANDOM.$$"
|
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
|
||||||
die "Could not create temporary directory! Exiting."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
# -----------------------------------
|
|
||||||
# Log is only used when the '-l' flag is set.
|
|
||||||
#
|
|
||||||
# To never save a logfile change variable to '/dev/null'
|
|
||||||
# Save to Desktop use: $HOME/Desktop/${scriptBasename}.log
|
|
||||||
# Save to standard user log location use: $HOME/Library/Logs/${scriptBasename}.log
|
|
||||||
# -----------------------------------
|
|
||||||
logFile="${HOME}/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
|
|
||||||
# Options and Usage
|
|
||||||
# -----------------------------------
|
|
||||||
# Print usage
|
|
||||||
usage() {
|
|
||||||
echo -n "${scriptName} [OPTION]... [FILE]...
|
|
||||||
|
|
||||||
This is a script template. Edit this description to print help to users.
|
|
||||||
|
|
||||||
${bold}Options:${reset}
|
|
||||||
-u, --username Username for script
|
|
||||||
-p, --password User password
|
|
||||||
--force Skip all user interaction. Implied 'Yes' to all actions.
|
|
||||||
-q, --quiet Quiet (no output)
|
|
||||||
-l, --log Print log to file
|
|
||||||
-s, --strict Exit script with null variables. i.e 'set -o nounset'
|
|
||||||
-v, --verbose Output more information. (Items echoed to 'verbose')
|
|
||||||
-d, --debug Runs script in BASH debug mode (set -x)
|
|
||||||
-h, --help Display this help and exit
|
|
||||||
--version Output version information and exit
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
|
||||||
# --foo bar
|
|
||||||
optstring=h
|
|
||||||
unset options
|
|
||||||
while (($#)); do
|
|
||||||
case $1 in
|
|
||||||
# If option is of type -ab
|
|
||||||
-[!-]?*)
|
|
||||||
# Loop over each character starting with the second
|
|
||||||
for ((i=1; i < ${#1}; i++)); do
|
|
||||||
c=${1:i:1}
|
|
||||||
|
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
|
||||||
# the rest of the string its argument
|
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
|
||||||
options+=("${1:i+1}")
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
|
|
||||||
# If option is of type --foo=bar
|
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
|
||||||
# add --endopts for --
|
|
||||||
--) options+=(--endopts) ;;
|
|
||||||
# Otherwise, nothing special
|
|
||||||
*) options+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
set -- "${options[@]}"
|
|
||||||
unset options
|
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# -------------------------------------
|
|
||||||
# [[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
|
||||||
while [[ $1 = -?* ]]; do
|
|
||||||
case $1 in
|
|
||||||
-h|--help) usage >&2; safeExit ;;
|
|
||||||
--version) echo "$(basename $0) ${version}"; safeExit ;;
|
|
||||||
-u|--username) shift; username=${1} ;;
|
|
||||||
-p|--password) shift; echo "Enter Pass: "; stty -echo; read PASS; stty echo;
|
|
||||||
echo ;;
|
|
||||||
-v|--verbose) verbose=true ;;
|
|
||||||
-l|--log) printLog=true ;;
|
|
||||||
-q|--quiet) quiet=true ;;
|
|
||||||
-s|--strict) strict=true;;
|
|
||||||
-d|--debug) debug=true;;
|
|
||||||
--force) force=true ;;
|
|
||||||
--endopts) shift; break ;;
|
|
||||||
*) die "invalid option: '$1'." ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Store the remaining part as arguments.
|
|
||||||
args+=("$@")
|
|
||||||
|
|
||||||
|
|
||||||
# Logging and Colors
|
|
||||||
# -----------------------------------------------------
|
|
||||||
# Here we set the colors for our script feedback.
|
|
||||||
# Example usage: success "sometext"
|
|
||||||
#------------------------------------------------------
|
|
||||||
|
|
||||||
# Set Colors
|
|
||||||
bold=$(tput bold)
|
|
||||||
reset=$(tput sgr0)
|
|
||||||
purple=$(tput setaf 171)
|
|
||||||
red=$(tput setaf 1)
|
|
||||||
green=$(tput setaf 76)
|
|
||||||
tan=$(tput setaf 3)
|
|
||||||
blue=$(tput setaf 38)
|
|
||||||
underline=$(tput sgr 0 1)
|
|
||||||
|
|
||||||
function _alert() {
|
|
||||||
if [ "${1}" = "emergency" ]; then local color="${bold}${red}"; fi
|
|
||||||
if [ "${1}" = "error" ]; then local color="${bold}${red}"; fi
|
|
||||||
if [ "${1}" = "warning" ]; then local color="${red}"; fi
|
|
||||||
if [ "${1}" = "success" ]; then local color="${green}"; fi
|
|
||||||
if [ "${1}" = "debug" ]; then local color="${purple}"; fi
|
|
||||||
if [ "${1}" = "header" ]; then local color="${bold}""${tan}"; fi
|
|
||||||
if [ "${1}" = "input" ]; then local color="${bold}"; printLog="false"; fi
|
|
||||||
if [ "${1}" = "info" ] || [ "${1}" = "notice" ]; then local color=""; fi
|
|
||||||
# Don't use colors on pipes or non-recognized terminals
|
|
||||||
if [[ "${TERM}" != "xterm"* ]] || [ -t 1 ]; then color=""; reset=""; fi
|
|
||||||
|
|
||||||
# Print to $logFile
|
|
||||||
if ${printLog}; then
|
|
||||||
echo -e "$(date +"%m-%d-%Y %r") $(printf "[%9s]" "${1}") ${_message}" >> "${logFile}";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print to console when script is not 'quiet'
|
|
||||||
if ${quiet}; then
|
|
||||||
return
|
|
||||||
else
|
|
||||||
echo -e "$(date +"%r") ${color}$(printf "[%9s]" "${1}") ${_message}${reset}";
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function die () { local _message="${*} Exiting."; echo "$(_alert emergency)"; safeExit;}
|
|
||||||
function error () { local _message="${*}"; echo "$(_alert error)"; }
|
|
||||||
function warning () { local _message="${*}"; echo "$(_alert warning)"; }
|
|
||||||
function notice () { local _message="${*}"; echo "$(_alert notice)"; }
|
|
||||||
function info () { local _message="${*}"; echo "$(_alert info)"; }
|
|
||||||
function debug () { local _message="${*}"; echo "$(_alert debug)"; }
|
|
||||||
function success () { local _message="${*}"; echo "$(_alert success)"; }
|
|
||||||
function input() { local _message="${*}"; echo -n "$(_alert input)"; }
|
|
||||||
function header() { local _message="${*}"; echo "$(_alert header)"; }
|
|
||||||
|
|
||||||
# Log messages when verbose is set to "true"
|
|
||||||
verbose() { if ${verbose}; then debug "$@"; fi }
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
|
||||||
trap trapCleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Set IFS to preferred implementation
|
|
||||||
IFS=$' \n\t'
|
|
||||||
|
|
||||||
# Exit on error. Append '||true' when you run the script if you expect an error.
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
# Run in debug mode, if set
|
|
||||||
if ${debug}; then set -x ; fi
|
|
||||||
|
|
||||||
# Exit on empty variable
|
|
||||||
if ${strict}; then set -o nounset ; fi
|
|
||||||
|
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Run your script
|
|
||||||
mainScript
|
|
||||||
|
|
||||||
# Exit cleanly
|
|
||||||
safeExit
|
|
||||||
@@ -1,982 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
#
|
|
||||||
version="1.0.0" # Sets version variable
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
function mainScript() {
|
|
||||||
header "Beginning script to set OSX preferences"
|
|
||||||
info "This script runs a series of commands to pre-configure OSX."
|
|
||||||
|
|
||||||
function setComputerName() {
|
|
||||||
seek_confirmation "Would you like to set your computer name (as done via System Preferences >> Sharing)?"
|
|
||||||
if is_confirmed; then
|
|
||||||
input "What would you like the name to be?"
|
|
||||||
read COMPUTER_NAME
|
|
||||||
sudo scutil --set ComputerName "$COMPUTER_NAME"
|
|
||||||
sudo scutil --set HostName "$COMPUTER_NAME"
|
|
||||||
sudo scutil --set LocalHostName "$COMPUTER_NAME"
|
|
||||||
sudo defaults write /Library/Preferences/SystemConfiguration/com.apple.smb.server NetBIOSName -string "$COMPUTER_NAME"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function generalUITPrefs() {
|
|
||||||
seek_confirmation "Run General UI Tweaks?"
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
success "Disabled Sound Effects on Boot"
|
|
||||||
sudo nvram SystemAudioVolume=" "
|
|
||||||
|
|
||||||
success "Hide the Time Machine, Volume, User, and Bluetooth icons"
|
|
||||||
# Get the system Hardware UUID and use it for the next menubar stuff
|
|
||||||
for domain in ~/Library/Preferences/ByHost/com.apple.systemuiserver.*; do
|
|
||||||
defaults write "${domain}" dontAutoLoad -array \
|
|
||||||
"/System/Library/CoreServices/Menu Extras/TimeMachine.menu" \
|
|
||||||
"/System/Library/CoreServices/Menu Extras/Volume.menu" \
|
|
||||||
"/System/Library/CoreServices/Menu Extras/User.menu"
|
|
||||||
done
|
|
||||||
|
|
||||||
defaults write com.apple.systemuiserver menuExtras -array \
|
|
||||||
"/System/Library/CoreServices/Menu Extras/Bluetooth.menu" \
|
|
||||||
"/System/Library/CoreServices/Menu Extras/AirPort.menu" \
|
|
||||||
"/System/Library/CoreServices/Menu Extras/Battery.menu" \
|
|
||||||
"/System/Library/CoreServices/Menu Extras/Clock.menu"
|
|
||||||
|
|
||||||
success "Set highlight color to yellow"
|
|
||||||
defaults write NSGlobalDomain AppleHighlightColor -string '0.984300 0.929400 0.450900'
|
|
||||||
|
|
||||||
success "Set sidebar icon size to small"
|
|
||||||
defaults write NSGlobalDomain NSTableViewDefaultSizeMode -int 1
|
|
||||||
# Possible values for int: 1=small, 2=medium
|
|
||||||
|
|
||||||
success "Always show scrollbars"
|
|
||||||
defaults write NSGlobalDomain AppleShowScrollBars -string "Always"
|
|
||||||
# Possible values: `WhenScrolling`, `Automatic` and `Always`
|
|
||||||
|
|
||||||
#success "Disable transparency in the menu bar and elsewhere on Yosemite"
|
|
||||||
#defaults write com.apple.universalaccess reduceTransparency -bool true
|
|
||||||
|
|
||||||
success "Disable opening and closing window animations"
|
|
||||||
defaults write NSGlobalDomain NSAutomaticWindowAnimationsEnabled -bool false
|
|
||||||
|
|
||||||
success "Expand save panel by default"
|
|
||||||
defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode -bool true
|
|
||||||
defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode2 -bool true
|
|
||||||
|
|
||||||
success "Expand print panel by default"
|
|
||||||
defaults write NSGlobalDomain PMPrintingExpandedStateForPrint -bool true
|
|
||||||
defaults write NSGlobalDomain PMPrintingExpandedStateForPrint2 -bool true
|
|
||||||
|
|
||||||
success "Save to disk (not to iCloud) by default"
|
|
||||||
defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false
|
|
||||||
|
|
||||||
success "Automatically quit printer app once the print jobs complete"
|
|
||||||
defaults write com.apple.print.PrintingPrefs "Quit When Finished" -bool true
|
|
||||||
|
|
||||||
success "Disable the 'Are you sure you want to open this application?' dialog"
|
|
||||||
defaults write com.apple.LaunchServices LSQuarantine -bool false
|
|
||||||
|
|
||||||
success "General:Display ASCII control characters using caret notation in standard text views"
|
|
||||||
# Try e.g. `cd /tmp; unidecode "\x{0000}" > cc.txt; open -e cc.txt`
|
|
||||||
defaults write NSGlobalDomain NSTextShowsControlCharacters -bool true
|
|
||||||
|
|
||||||
success "Disable automatic termination of inactive apps"
|
|
||||||
defaults write NSGlobalDomain NSDisableAutomaticTermination -bool true
|
|
||||||
|
|
||||||
success "Disable Resume system-wide"
|
|
||||||
defaults write com.apple.systempreferences NSQuitAlwaysKeepsWindows -bool false
|
|
||||||
|
|
||||||
success "Set Help Viewer windows to non-floating mode"
|
|
||||||
defaults write com.apple.helpviewer DevMode -bool true
|
|
||||||
|
|
||||||
success "Reveal info when clicking the clock in the login window"
|
|
||||||
sudo defaults write /Library/Preferences/com.apple.loginwindow AdminHostInfo HostName
|
|
||||||
|
|
||||||
#success "Restart automatically if the computer freezes"
|
|
||||||
#systemsetup -setrestartfreeze on
|
|
||||||
|
|
||||||
#success "Never go into computer sleep mode"
|
|
||||||
#systemsetup -setcomputersleep Off > /dev/null
|
|
||||||
|
|
||||||
success "Check for software updates daily, not just once per week"
|
|
||||||
defaults write com.apple.SoftwareUpdate ScheduleFrequency -int 1
|
|
||||||
|
|
||||||
#success "Disable Notification Center and remove the menu bar icon"
|
|
||||||
#launchctl unload -w /System/Library/LaunchAgents/com.apple.notificationcenterui.plist 2> /dev/null
|
|
||||||
|
|
||||||
success "Disabled smart quotes as they are annoying when typing code"
|
|
||||||
defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false
|
|
||||||
|
|
||||||
success "Disabled smart dashes as they are annoying when typing code"
|
|
||||||
defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false
|
|
||||||
|
|
||||||
success "Removing duplicates in the 'Open With' menu"
|
|
||||||
#/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -kill -r -domain local -domain system -domain user
|
|
||||||
|
|
||||||
#success "Disable hibernation? (speeds up entering sleep mode)"
|
|
||||||
#sudo pmset -a hibernatemode 0
|
|
||||||
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function inputDevicePrefs() {
|
|
||||||
seek_confirmation "Run Trackpad, Mouse, Keyboard Tweaks?"
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
#success "Trackpad: enable tap to click for this user and for the login screen"
|
|
||||||
#defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true
|
|
||||||
#defaults -currentHost write NSGlobalDomain com.apple.mouse.tapBehavior -int 1
|
|
||||||
#defaults write NSGlobalDomain com.apple.mouse.tapBehavior -int 1
|
|
||||||
|
|
||||||
# success "Trackpad: map bottom right corner to right-click"
|
|
||||||
# defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadCornerSecondaryClick -int 2
|
|
||||||
# defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadRightClick -bool true
|
|
||||||
# defaults -currentHost write NSGlobalDomain com.apple.trackpad.trackpadCornerClickBehavior -int 1
|
|
||||||
# defaults -currentHost write NSGlobalDomain com.apple.trackpad.enableSecondaryClick -bool true
|
|
||||||
|
|
||||||
# success "Disable “natural” (Lion-style) scrolling"
|
|
||||||
# defaults write NSGlobalDomain com.apple.swipescrolldirection -bool false
|
|
||||||
|
|
||||||
success "Setting trackpad & mouse speed to a reasonable number"
|
|
||||||
defaults write -g com.apple.trackpad.scaling 2
|
|
||||||
defaults write -g com.apple.mouse.scaling 2.5
|
|
||||||
|
|
||||||
success "Increase sound quality for Bluetooth headphones/headsets"
|
|
||||||
defaults write com.apple.BluetoothAudioAgent "Apple Bitpool Min (editable)" -int 40
|
|
||||||
|
|
||||||
success "Enable full keyboard access for all controls"
|
|
||||||
# (e.g. enable Tab in modal dialogs)
|
|
||||||
defaults write NSGlobalDomain AppleKeyboardUIMode -int 3
|
|
||||||
|
|
||||||
success "Use scroll gesture with the Ctrl (^) modifier key to zoom"
|
|
||||||
defaults write com.apple.universalaccess closeViewScrollWheelToggle -bool true
|
|
||||||
defaults write com.apple.universalaccess HIDScrollZoomModifierMask -int 262144
|
|
||||||
# Follow the keyboard focus while zoomed in
|
|
||||||
defaults write com.apple.universalaccess closeViewZoomFollowsFocus -bool true
|
|
||||||
|
|
||||||
success "Disable press-and-hold for keys in favor of key repeat"
|
|
||||||
defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false
|
|
||||||
|
|
||||||
success "Set a blazingly fast keyboard repeat rate"
|
|
||||||
defaults write NSGlobalDomain KeyRepeat -int 0.02
|
|
||||||
|
|
||||||
success "Automatically illuminate built-in MacBook keyboard in low light"
|
|
||||||
defaults write com.apple.BezelServices kDim -bool true
|
|
||||||
|
|
||||||
success "Turn off keyboard illumination when computer is not used for 5 minutes"
|
|
||||||
defaults write com.apple.BezelServices kDimTime -int 300
|
|
||||||
|
|
||||||
success "Set language and text formats"
|
|
||||||
# Note: if you’re in the US, replace `EUR` with `USD`, `Centimeters` with
|
|
||||||
# `Inches`, `en_GB` with `en_US`, and `true` with `false`.
|
|
||||||
defaults write NSGlobalDomain AppleLanguages -array "en" "nl"
|
|
||||||
defaults write NSGlobalDomain AppleLocale -string "en_US@currency=USD"
|
|
||||||
defaults write NSGlobalDomain AppleMeasurementUnits -string "Inches"
|
|
||||||
defaults write NSGlobalDomain AppleMetricUnits -bool false
|
|
||||||
|
|
||||||
success "Set the timezone"
|
|
||||||
systemsetup -settimezone "America/New_York" > /dev/null
|
|
||||||
#see `systemsetup -listtimezones` for other values
|
|
||||||
|
|
||||||
#success "Disable spelling auto-correct"
|
|
||||||
#defaults write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool false
|
|
||||||
|
|
||||||
# Stop iTunes from responding to the keyboard media keys
|
|
||||||
#launchctl unload -w /System/Library/LaunchAgents/com.apple.rcd.plist 2> /dev/null
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function screenPrefs() {
|
|
||||||
seek_confirmation "Run Screen Configurations?"
|
|
||||||
if is_confirmed; then
|
|
||||||
success "Require password immediately after sleep or screen saver begins"
|
|
||||||
defaults write com.apple.screensaver askForPassword -int 1
|
|
||||||
defaults write com.apple.screensaver askForPasswordDelay -int 0
|
|
||||||
|
|
||||||
success "Save screenshots to the desktop"
|
|
||||||
defaults write com.apple.screencapture location -string "${HOME}/Desktop"
|
|
||||||
|
|
||||||
success "Save screenshots in PNG format"
|
|
||||||
defaults write com.apple.screencapture type -string "png"
|
|
||||||
# other options: BMP, GIF, JPG, PDF, TIFF, PNG
|
|
||||||
|
|
||||||
#success "Disable shadow in screenshots"
|
|
||||||
#defaults write com.apple.screencapture disable-shadow -bool true
|
|
||||||
|
|
||||||
success "Enable subpixel font rendering on non-Apple LCDs"
|
|
||||||
defaults write NSGlobalDomain AppleFontSmoothing -int 2
|
|
||||||
|
|
||||||
#success "Enabling HiDPI display modes (requires restart)"
|
|
||||||
#sudo defaults write /Library/Preferences/com.apple.windowserver DisplayResolutionEnabled -bool true
|
|
||||||
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function finderPrefs() {
|
|
||||||
seek_confirmation "Run Finder Tweaks?"
|
|
||||||
if is_confirmed; then
|
|
||||||
success "Finder: allow quitting via ⌘ + Q"
|
|
||||||
defaults write com.apple.finder QuitMenuItem -bool true
|
|
||||||
|
|
||||||
success "Finder: disable window animations and Get Info animations"
|
|
||||||
defaults write com.apple.finder DisableAllAnimations -bool true
|
|
||||||
|
|
||||||
success "Set Home Folder as the default location for new Finder windows"
|
|
||||||
# For other paths, use `PfLo` and `file:///full/path/here/`
|
|
||||||
defaults write com.apple.finder NewWindowTarget -string "PfHm"
|
|
||||||
defaults write com.apple.finder NewWindowTargetPath -string "file://${HOME}/"
|
|
||||||
|
|
||||||
success "Show icons for hard drives, servers, and removable media on the desktop"
|
|
||||||
defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool true
|
|
||||||
defaults write com.apple.finder ShowHardDrivesOnDesktop -bool true
|
|
||||||
defaults write com.apple.finder ShowMountedServersOnDesktop -bool true
|
|
||||||
defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool true
|
|
||||||
|
|
||||||
#success "Finder: show hidden files by default"
|
|
||||||
#defaults write com.apple.finder AppleShowAllFiles -bool true
|
|
||||||
|
|
||||||
success "Finder: show all filename extensions"
|
|
||||||
defaults write NSGlobalDomain AppleShowAllExtensions -bool true
|
|
||||||
|
|
||||||
success "Finder: show status bar"
|
|
||||||
defaults write com.apple.finder ShowStatusBar -bool true
|
|
||||||
|
|
||||||
success "Finder: show path bar"
|
|
||||||
defaults write com.apple.finder ShowPathbar -bool true
|
|
||||||
|
|
||||||
success "Finder: allow text selection in Quick Look"
|
|
||||||
defaults write com.apple.finder QLEnableTextSelection -bool true
|
|
||||||
|
|
||||||
#success "Display full POSIX path as Finder window title"
|
|
||||||
#defaults write com.apple.finder _FXShowPosixPathInTitle -bool true
|
|
||||||
|
|
||||||
success "When performing a search, search the current folder by default"
|
|
||||||
defaults write com.apple.finder FXDefaultSearchScope -string "SCcf"
|
|
||||||
|
|
||||||
success "Disable the warning when changing a file extension"
|
|
||||||
defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false
|
|
||||||
|
|
||||||
success "Enable spring loading for directories"
|
|
||||||
defaults write NSGlobalDomain com.apple.springing.enabled -bool true
|
|
||||||
|
|
||||||
success "Remove the spring loading delay for directories"
|
|
||||||
defaults write NSGlobalDomain com.apple.springing.delay -float 0
|
|
||||||
|
|
||||||
success "Avoid creating .DS_Store files on network volumes"
|
|
||||||
defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true
|
|
||||||
defaults write com.apple.desktopservices DSDontWriteUSBStores -bool true
|
|
||||||
|
|
||||||
success "Disable disk image verification"
|
|
||||||
defaults write com.apple.frameworks.diskimages skip-verify -bool true
|
|
||||||
defaults write com.apple.frameworks.diskimages skip-verify-locked -bool true
|
|
||||||
defaults write com.apple.frameworks.diskimages skip-verify-remote -bool true
|
|
||||||
|
|
||||||
# success "Automatically open a new Finder window when a volume is mounted"
|
|
||||||
# defaults write com.apple.frameworks.diskimages auto-open-ro-root -bool true
|
|
||||||
# defaults write com.apple.frameworks.diskimages auto-open-rw-root -bool true
|
|
||||||
# defaults write com.apple.finder OpenWindowForNewRemovableDisk -bool true
|
|
||||||
|
|
||||||
success "Show item info to the right of the icons on the desktop"
|
|
||||||
/usr/libexec/PlistBuddy -c "Set DesktopViewSettings:IconViewSettings:labelOnBottom false" ~/Library/Preferences/com.apple.finder.plist
|
|
||||||
|
|
||||||
success "Enable snap-to-grid for icons on the desktop and in other icon views"
|
|
||||||
/usr/libexec/PlistBuddy -c "Set DesktopViewSettings:IconViewSettings:arrangeBy grid" ~/Library/Preferences/com.apple.finder.plist
|
|
||||||
/usr/libexec/PlistBuddy -c "Set StandardViewSettings:IconViewSettings:arrangeBy grid" ~/Library/Preferences/com.apple.finder.plist
|
|
||||||
|
|
||||||
success "Increase grid spacing for icons on the desktop and in other icon views"
|
|
||||||
/usr/libexec/PlistBuddy -c "Set DesktopViewSettings:IconViewSettings:gridSpacing 100" ~/Library/Preferences/com.apple.finder.plist
|
|
||||||
/usr/libexec/PlistBuddy -c "Set StandardViewSettings:IconViewSettings:gridSpacing 100" ~/Library/Preferences/com.apple.finder.plist
|
|
||||||
|
|
||||||
success "Increase the size of icons on the desktop and in other icon views"
|
|
||||||
/usr/libexec/PlistBuddy -c "Set DesktopViewSettings:IconViewSettings:iconSize 40" ~/Library/Preferences/com.apple.finder.plist
|
|
||||||
/usr/libexec/PlistBuddy -c "Set StandardViewSettings:IconViewSettings:iconSize 40" ~/Library/Preferences/com.apple.finder.plist
|
|
||||||
|
|
||||||
success "Use column view in all Finder windows by default"
|
|
||||||
defaults write com.apple.finder FXPreferredViewStyle -string "clmv"
|
|
||||||
# Four-letter codes for the other view modes: `icnv`, `clmv`, `Flwv`, `Nlsv`
|
|
||||||
|
|
||||||
success "Disable the warning before emptying the Trash"
|
|
||||||
defaults write com.apple.finder WarnOnEmptyTrash -bool false
|
|
||||||
|
|
||||||
# success "Empty Trash securely by default"
|
|
||||||
# defaults write com.apple.finder EmptyTrashSecurely -bool true
|
|
||||||
|
|
||||||
success "Show the ~/Library folder"
|
|
||||||
chflags nohidden ${HOME}/Library
|
|
||||||
|
|
||||||
success "Show the /Volumes folder"
|
|
||||||
sudo chflags nohidden /Volumes
|
|
||||||
|
|
||||||
#success "Remove Dropbox’s green checkmark icons in Finder"
|
|
||||||
#file=/Applications/Dropbox.app/Contents/Resources/emblem-dropbox-uptodate.icns
|
|
||||||
#[ -e "${file}" ] && mv -f "${file}" "${file}.bak"
|
|
||||||
|
|
||||||
success "Expand File Info panes"
|
|
||||||
# “General”, “Open with”, and “Sharing & Permissions”
|
|
||||||
defaults write com.apple.finder FXInfoPanesExpanded -dict \
|
|
||||||
General -bool true \
|
|
||||||
OpenWith -bool true \
|
|
||||||
Privileges -bool true
|
|
||||||
|
|
||||||
# Enable AirDrop over Ethernet and on unsupported Macs running Lion
|
|
||||||
# defaults write com.apple.NetworkBrowser BrowseAllInterfaces -bool true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function dockDashPrefs() {
|
|
||||||
seek_confirmation "Configure Dock, Dashboard, Corners?"
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
success "Enable highlight hover effect for the grid view of a stack"
|
|
||||||
defaults write com.apple.dock mouse-over-hilite-stack -bool true
|
|
||||||
|
|
||||||
success "Change minimize/maximize window effect"
|
|
||||||
defaults write com.apple.dock mineffect -string "genie"
|
|
||||||
|
|
||||||
success "Set the icon size of Dock items to 36 pixels"
|
|
||||||
defaults write com.apple.dock tilesize -int 36
|
|
||||||
|
|
||||||
success "Show only open applications in the Dock"
|
|
||||||
defaults write com.apple.dock static-only -bool true
|
|
||||||
|
|
||||||
success "Minimize windows into their application’s icon"
|
|
||||||
defaults write com.apple.dock minimize-to-application -bool true
|
|
||||||
|
|
||||||
success "Enable spring loading for all Dock items"
|
|
||||||
defaults write com.apple.dock enable-spring-load-actions-on-all-items -bool true
|
|
||||||
|
|
||||||
success "Show indicator lights for open applications in the Dock"
|
|
||||||
defaults write com.apple.dock show-process-indicators -bool true
|
|
||||||
|
|
||||||
success "Wipe all (default) app icons from the Dock"
|
|
||||||
# This is only really useful when setting up a new Mac, or if you don’t use
|
|
||||||
# the Dock to launch apps.
|
|
||||||
defaults write com.apple.dock persistent-apps -array
|
|
||||||
|
|
||||||
success "Disable App Persistence (re-opening apps on login)"
|
|
||||||
defaults write -g ApplePersistence -bool no
|
|
||||||
|
|
||||||
success "Don’t animate opening applications from the Dock"
|
|
||||||
defaults write com.apple.dock launchanim -bool false
|
|
||||||
|
|
||||||
success "Speed up Mission Control animations"
|
|
||||||
defaults write com.apple.dock expose-animation-duration -float 0.1
|
|
||||||
|
|
||||||
# success "Don’t group windows by application in Mission Control"
|
|
||||||
# # (i.e. use the old Exposé behavior instead)
|
|
||||||
# defaults write com.apple.dock expose-group-by-app -bool false
|
|
||||||
|
|
||||||
success "Disable Dashboard"
|
|
||||||
defaults write com.apple.dashboard mcx-disabled -bool true
|
|
||||||
|
|
||||||
success "Don’t show Dashboard as a Space"
|
|
||||||
defaults write com.apple.dock dashboard-in-overlay -bool true
|
|
||||||
|
|
||||||
# success "Don’t automatically rearrange Spaces based on most recent use"
|
|
||||||
# defaults write com.apple.dock mru-spaces -bool false
|
|
||||||
|
|
||||||
success "Remove the auto-hiding Dock delay"
|
|
||||||
defaults write com.apple.dock autohide-delay -float 0
|
|
||||||
|
|
||||||
#success "Remove the animation when hiding/showing the Dock"
|
|
||||||
#defaults write com.apple.dock autohide-time-modifier -float 0
|
|
||||||
|
|
||||||
success "Automatically hide and show the Dock"
|
|
||||||
defaults write com.apple.dock autohide -bool true
|
|
||||||
|
|
||||||
success "Make Dock icons of hidden applications translucent"
|
|
||||||
defaults write com.apple.dock showhidden -bool true
|
|
||||||
|
|
||||||
|
|
||||||
# Add a spacer to the left side of the Dock (where the applications are)
|
|
||||||
#defaults write com.apple.dock persistent-apps -array-add '{tile-data={}; tile-type="spacer-tile";}'
|
|
||||||
# Add a spacer to the right side of the Dock (where the Trash is)
|
|
||||||
#defaults write com.apple.dock persistent-others -array-add '{tile-data={}; tile-type="spacer-tile";}'
|
|
||||||
|
|
||||||
success "Disabled hot corners"
|
|
||||||
# Possible values:
|
|
||||||
# 0: no-op
|
|
||||||
# 2: Mission Control
|
|
||||||
# 3: Show application windows
|
|
||||||
# 4: Desktop
|
|
||||||
# 5: Start screen saver
|
|
||||||
# 6: Disable screen saver
|
|
||||||
# 7: Dashboard
|
|
||||||
# 10: Put display to sleep
|
|
||||||
# 11: Launchpad
|
|
||||||
# 12: Notification Center
|
|
||||||
# Top left screen corner → Mission Control
|
|
||||||
defaults write com.apple.dock wvous-tl-corner -int 0
|
|
||||||
defaults write com.apple.dock wvous-tl-modifier -int 0
|
|
||||||
# Top right screen corner → Desktop
|
|
||||||
defaults write com.apple.dock wvous-tr-corner -int 0
|
|
||||||
defaults write com.apple.dock wvous-tr-modifier -int 0
|
|
||||||
# Bottom left screen corner → Start screen saver
|
|
||||||
defaults write com.apple.dock wvous-bl-corner -int 0
|
|
||||||
defaults write com.apple.dock wvous-bl-modifier -int 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function safariPrefs() {
|
|
||||||
seek_confirmation "Safari & Webkit tweaks?"
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
success "Privacy: don’t send search queries to Apple"
|
|
||||||
defaults write com.apple.Safari UniversalSearchEnabled -bool false
|
|
||||||
defaults write com.apple.Safari SuppressSearchSuggestions -bool true
|
|
||||||
|
|
||||||
success "Show the full URL in the address bar (note: this still hides the scheme)"
|
|
||||||
defaults write com.apple.Safari ShowFullURLInSmartSearchField -bool true
|
|
||||||
|
|
||||||
success "Set Safari’s home page to about:blank for faster loading"
|
|
||||||
defaults write com.apple.Safari HomePage -string "about:blank"
|
|
||||||
|
|
||||||
success "Prevent Safari from opening safe files automatically after downloading"
|
|
||||||
defaults write com.apple.Safari AutoOpenSafeDownloads -bool false
|
|
||||||
|
|
||||||
# success "Allow hitting the Backspace key to go to the previous page in history"
|
|
||||||
# defaults write com.apple.Safari com.apple.Safari.ContentPageGroupIdentifier.WebKit2BackspaceKeyNavigationEnabled -bool true
|
|
||||||
|
|
||||||
# # Hide Safari’s bookmarks bar by default
|
|
||||||
# defaults write com.apple.Safari ShowFavoritesBar -bool false
|
|
||||||
|
|
||||||
# # Hide Safari’s sidebar in Top Sites
|
|
||||||
# defaults write com.apple.Safari ShowSidebarInTopSites -bool false
|
|
||||||
|
|
||||||
# # Disable Safari’s thumbnail cache for History and Top Sites
|
|
||||||
# defaults write com.apple.Safari DebugSnapshotsUpdatePolicy -int 2
|
|
||||||
|
|
||||||
success "Enable Safari’s debug menu"
|
|
||||||
defaults write com.apple.Safari IncludeInternalDebugMenu -bool true
|
|
||||||
|
|
||||||
success "Make Safari’s search banners default to Contains instead of Starts With"
|
|
||||||
defaults write com.apple.Safari FindOnPageMatchesWordStartsOnly -bool false
|
|
||||||
|
|
||||||
success "Remove useless icons from Safari’s bookmarks bar"
|
|
||||||
defaults write com.apple.Safari ProxiesInBookmarksBar "()"
|
|
||||||
|
|
||||||
success "Enable the Develop menu and the Web Inspector in Safari"
|
|
||||||
defaults write com.apple.Safari IncludeDevelopMenu -bool true
|
|
||||||
defaults write com.apple.Safari WebKitDeveloperExtrasEnabledPreferenceKey -bool true
|
|
||||||
defaults write com.apple.Safari com.apple.Safari.ContentPageGroupIdentifier.WebKit2DeveloperExtrasEnabled -bool true
|
|
||||||
|
|
||||||
success "Add a context menu item for showing the Web Inspector in web views"
|
|
||||||
defaults write NSGlobalDomain WebKitDeveloperExtras -bool true
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function mailPrefs() {
|
|
||||||
seek_confirmation "Configure Mail.app?"
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
success "Disable send and reply animations in Mail.app"
|
|
||||||
defaults write com.apple.mail DisableReplyAnimations -bool true
|
|
||||||
defaults write com.apple.mail DisableSendAnimations -bool true
|
|
||||||
|
|
||||||
success "Copy sane email addresses to clipboard"
|
|
||||||
# Copy email addresses as `foo@example.com` instead of `Foo Bar <foo@example.com>` in Mail.app
|
|
||||||
defaults write com.apple.mail AddressesIncludeNameOnPasteboard -bool false
|
|
||||||
|
|
||||||
#success "Add the keyboard shortcut ⌘ + Enter to send an email in Mail.app"
|
|
||||||
#defaults write com.apple.mail NSUserKeyEquivalents -dict-add "Send" -string "@\\U21a9"
|
|
||||||
|
|
||||||
success "Display emails in threaded mode, sorted by date (newest at the top)"
|
|
||||||
defaults write com.apple.mail DraftsViewerAttributes -dict-add "DisplayInThreadedMode" -string "yes"
|
|
||||||
defaults write com.apple.mail DraftsViewerAttributes -dict-add "SortedDescending" -string "no"
|
|
||||||
defaults write com.apple.mail DraftsViewerAttributes -dict-add "SortOrder" -string "received-date"
|
|
||||||
|
|
||||||
#success "Disable inline attachments (just show the icons)"
|
|
||||||
#defaults write com.apple.mail DisableInlineAttachmentViewing -bool false
|
|
||||||
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function spotlightPrefs() {
|
|
||||||
seek_confirmation "Configure Spotlight?"
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
# Hide Spotlight tray-icon (and subsequent helper)
|
|
||||||
#sudo chmod 600 /System/Library/CoreServices/Search.bundle/Contents/MacOS/Search
|
|
||||||
|
|
||||||
success "Disabled Spotlight indexing for any new mounted volume"
|
|
||||||
# Use `sudo mdutil -i off "/Volumes/foo"` to stop indexing any volume.
|
|
||||||
sudo defaults write /.Spotlight-V100/VolumeConfiguration Exclusions -array "/Volumes"
|
|
||||||
|
|
||||||
success "Change indexing order and disable some file types"
|
|
||||||
# Yosemite-specific search results (remove them if your are using OS X 10.9 or older):
|
|
||||||
# MENU_DEFINITION
|
|
||||||
# MENU_CONVERSION
|
|
||||||
# MENU_EXPRESSION
|
|
||||||
# MENU_SPOTLIGHT_SUGGESTIONS (send search queries to Apple)
|
|
||||||
# MENU_WEBSEARCH (send search queries to Apple)
|
|
||||||
# MENU_OTHER
|
|
||||||
# defaults write com.apple.spotlight orderedItems -array \
|
|
||||||
# '{"enabled" = 1;"name" = "APPLICATIONS";}' \
|
|
||||||
# '{"enabled" = 1;"name" = "SYSTEM_PREFS";}' \
|
|
||||||
# '{"enabled" = 1;"name" = "DIRECTORIES";}' \
|
|
||||||
# '{"enabled" = 1;"name" = "PDF";}' \
|
|
||||||
# '{"enabled" = 1;"name" = "FONTS";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "DOCUMENTS";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MESSAGES";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "CONTACT";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "EVENT_TODO";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "IMAGES";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "BOOKMARKS";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MUSIC";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MOVIES";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "PRESENTATIONS";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "SPREADSHEETS";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "SOURCE";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MENU_DEFINITION";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MENU_OTHER";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MENU_CONVERSION";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MENU_EXPRESSION";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MENU_WEBSEARCH";}' \
|
|
||||||
# '{"enabled" = 0;"name" = "MENU_SPOTLIGHT_SUGGESTIONS";}'
|
|
||||||
# Load new settings before rebuilding the index
|
|
||||||
# killall mds > /dev/null 2>&1
|
|
||||||
# Make sure indexing is enabled for the main volume
|
|
||||||
#sudo mdutil -i on / > /dev/null
|
|
||||||
# Rebuild the index from scratch
|
|
||||||
#sudo mdutil -E / > /dev/null
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function terminalPrefs() {
|
|
||||||
seek_confirmation "Configure Terminal.app?"
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
success "Only use UTF-8 in Terminal.app"
|
|
||||||
defaults write com.apple.terminal StringEncodings -array 4
|
|
||||||
|
|
||||||
# Use a modified version of the Pro theme by default in Terminal.app
|
|
||||||
open "${HOME}/Dropbox/sharedConfiguration/App Configuration Files/Terminal/solarizedDark.terminal"
|
|
||||||
sleep 2 # Wait a bit to make sure the theme is loaded
|
|
||||||
defaults write com.apple.terminal "Default Window Settings" -string "solarizedDark"
|
|
||||||
defaults write com.apple.terminal "Startup Window Settings" -string "solarizedDark"
|
|
||||||
|
|
||||||
# Enable “focus follows mouse” for Terminal.app and all X11 apps
|
|
||||||
# i.e. hover over a window and start typing in it without clicking first
|
|
||||||
#defaults write com.apple.terminal FocusFollowsMouse -bool true
|
|
||||||
#defaults write org.x.X11 wm_ffm -bool true
|
|
||||||
fi
|
|
||||||
|
|
||||||
seek_confirmation "Configure iTerm2?"
|
|
||||||
if is_confirmed; then
|
|
||||||
success "Installed pretty iTerm colors"
|
|
||||||
open "${HOME}/Dropbox/sharedConfiguration/App Configuration Files/iTerm/nate.itermcolors"
|
|
||||||
|
|
||||||
success "Don't display the annoying prompt when quitting iTerm"
|
|
||||||
defaults write com.googlecode.iterm2 PromptOnQuit -bool false
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function timeMachinePrefs() {
|
|
||||||
seek_confirmation "Disable Time Machine?"
|
|
||||||
if is_confirmed; then
|
|
||||||
success "Prevent Time Machine from prompting to use new hard drives as backup volume"
|
|
||||||
defaults write com.apple.TimeMachine DoNotOfferNewDisksForBackup -bool true
|
|
||||||
|
|
||||||
success "Disable local Time Machine backups"
|
|
||||||
hash tmutil &> /dev/null && sudo tmutil disablelocal
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function applicationPrefs() {
|
|
||||||
seek_confirmation "Configure Activity Monitor?"
|
|
||||||
if is_confirmed; then
|
|
||||||
success "Show the main window when launching Activity Monitor"
|
|
||||||
defaults write com.apple.ActivityMonitor OpenMainWindow -bool true
|
|
||||||
|
|
||||||
success "Visualize CPU usage in the Activity Monitor Dock icon"
|
|
||||||
defaults write com.apple.ActivityMonitor IconType -int 5
|
|
||||||
|
|
||||||
success "Show all processes in Activity Monitor"
|
|
||||||
defaults write com.apple.ActivityMonitor ShowCategory -int 0
|
|
||||||
|
|
||||||
success "Sort Activity Monitor results by CPU usage"
|
|
||||||
defaults write com.apple.ActivityMonitor SortColumn -string "CPUUsage"
|
|
||||||
defaults write com.apple.ActivityMonitor SortDirection -int 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
seek_confirmation "Stop Photos from opening whenever a camera is connected?"
|
|
||||||
if is_confirmed; then
|
|
||||||
defaults -currentHost write com.apple.ImageCapture disableHotPlug -bool YES
|
|
||||||
fi
|
|
||||||
|
|
||||||
seek_confirmation "Configure Google Chrome?"
|
|
||||||
if is_confirmed; then
|
|
||||||
# Use the system-native print preview dialog
|
|
||||||
defaults write com.google.Chrome DisablePrintPreview -bool true
|
|
||||||
defaults write com.google.Chrome.canary DisablePrintPreview -bool true
|
|
||||||
fi
|
|
||||||
|
|
||||||
seek_confirmation "Configure Contacts, Calendar, TextEdit, Disk Util?"
|
|
||||||
if is_confirmed; then
|
|
||||||
success "Enable the debug menu in Address Book"
|
|
||||||
defaults write com.apple.addressbook ABShowDebugMenu -bool true
|
|
||||||
|
|
||||||
# Enable Dashboard dev mode (allows keeping widgets on the desktop)
|
|
||||||
# defaults write com.apple.dashboard devmode -bool true
|
|
||||||
|
|
||||||
# Enable the debug menu in iCal (pre-10.8)
|
|
||||||
# defaults write com.apple.iCal IncludeDebugMenu -bool true
|
|
||||||
|
|
||||||
success "Use plain text mode for new TextEdit documents"
|
|
||||||
defaults write com.apple.TextEdit RichText -int 0
|
|
||||||
success "Open and save files as UTF-8 in TextEdit"
|
|
||||||
defaults write com.apple.TextEdit PlainTextEncoding -int 4
|
|
||||||
defaults write com.apple.TextEdit PlainTextEncodingForWrite -int 4
|
|
||||||
|
|
||||||
success "Enable the debug menu in Disk Utility"
|
|
||||||
defaults write com.apple.DiskUtility DUDebugMenuEnabled -bool true
|
|
||||||
defaults write com.apple.DiskUtility advanced-image-options -bool true
|
|
||||||
fi
|
|
||||||
|
|
||||||
seek_confirmation "Configure Sublime Text 3 in Terminal?"
|
|
||||||
if is_confirmed; then
|
|
||||||
if [ ! -e "/Applications/Sublime Text.app" ]; then
|
|
||||||
error "We don't have Sublime Text.app. Get it installed and try again."
|
|
||||||
else
|
|
||||||
if [ ! -e "/usr/local/bin/subl" ]; then
|
|
||||||
ln -s "/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl" /usr/local/bin/subl
|
|
||||||
success "Symlink created."
|
|
||||||
else
|
|
||||||
notice "Symlink already exists. Nothing done."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
seek_confirmation "Configure Messages.app?"
|
|
||||||
if is_confirmed; then
|
|
||||||
success "Disable automatic emoji substitution in Messages.app? (i.e. use plain text smileys) (y/n)"
|
|
||||||
defaults write com.apple.messageshelper.MessageController SOInputLineSettings -dict-add "automaticEmojiSubstitutionEnablediMessage" -bool false
|
|
||||||
|
|
||||||
success "Disable smart quotes in Messages.app? (it's annoying for messages that contain code) (y/n)"
|
|
||||||
defaults write com.apple.messageshelper.MessageController SOInputLineSettings -dict-add "automaticQuoteSubstitutionEnabled" -bool false
|
|
||||||
|
|
||||||
success "Disabled continuous spell checking in Messages.app? (y/n)"
|
|
||||||
defaults write com.apple.messageshelper.MessageController SOInputLineSettings -dict-add "continuousSpellCheckingEnabled" -bool false
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function ssdPrefs() {
|
|
||||||
seek_confirmation "Confirm that you have an SSD Hard Drive and want to disable sudden motion sensor."
|
|
||||||
if is_confirmed; then
|
|
||||||
|
|
||||||
success "Remove the sleep image file to save disk space"
|
|
||||||
sudo rm /Private/var/vm/sleepimage
|
|
||||||
success "Create a zero-byte file instead…"
|
|
||||||
sudo touch /Private/var/vm/sleepimage
|
|
||||||
success "…and make sure it can’t be rewritten"
|
|
||||||
sudo chflags uchg /Private/var/vm/sleepimage
|
|
||||||
|
|
||||||
success "Disable the sudden motion sensor as it’s not useful for SSDs"
|
|
||||||
sudo pmset -a sms 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
seek_confirmation "Kill all effected applications?"
|
|
||||||
if is_confirmed; then
|
|
||||||
for app in "Activity Monitor" "Address Book" "Calendar" "Contacts" "cfprefsd" \
|
|
||||||
"Dock" "Finder" "Mail" "Messages" "Safari" "SystemUIServer" \
|
|
||||||
"Terminal" "iCal"; do
|
|
||||||
killall "${app}" > /dev/null 2>&1
|
|
||||||
done
|
|
||||||
success "Apps killed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
notice "Some of these changes require a logout/restart to take effect."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run the script
|
|
||||||
# ###############
|
|
||||||
|
|
||||||
# Ask for the administrator password upfront
|
|
||||||
sudo -v
|
|
||||||
setComputerName
|
|
||||||
generalUITPrefs
|
|
||||||
inputDevicePrefs
|
|
||||||
screenPrefs
|
|
||||||
finderPrefs
|
|
||||||
dockDashPrefs
|
|
||||||
safariPrefs
|
|
||||||
mailPrefs
|
|
||||||
spotlightPrefs
|
|
||||||
terminalPrefs
|
|
||||||
timeMachinePrefs
|
|
||||||
applicationPrefs
|
|
||||||
ssdPrefs
|
|
||||||
cleanup
|
|
||||||
}
|
|
||||||
|
|
||||||
## SET SCRIPTNAME VARIABLE ##
|
|
||||||
scriptName=$(basename "$0")
|
|
||||||
|
|
||||||
function trapCleanup() {
|
|
||||||
# trapCleanup Function
|
|
||||||
# -----------------------------------
|
|
||||||
# Any actions that should be taken if the script is prematurely
|
|
||||||
# exited. Always call this function at the top of your script.
|
|
||||||
# -----------------------------------
|
|
||||||
echo ""
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
die "Exit trapped."
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeExit() {
|
|
||||||
# safeExit
|
|
||||||
# -----------------------------------
|
|
||||||
# Non destructive exit for when script exits naturally.
|
|
||||||
# Usage: Add this function at the end of every script.
|
|
||||||
# -----------------------------------
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
trap - INT TERM EXIT
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
function seek_confirmation() {
|
|
||||||
# Asks questions of a user and then does something with the answer.
|
|
||||||
# y/n are the only possible answers.
|
|
||||||
#
|
|
||||||
# USAGE:
|
|
||||||
# seek_confirmation "Ask a question"
|
|
||||||
# if is_confirmed; then
|
|
||||||
# some action
|
|
||||||
# else
|
|
||||||
# some other action
|
|
||||||
# fi
|
|
||||||
#
|
|
||||||
# Credt: https://github.com/kevva/dotfiles
|
|
||||||
# ------------------------------------------------------
|
|
||||||
# echo ""
|
|
||||||
input "$@"
|
|
||||||
if ${force}; then
|
|
||||||
notice "Forcing confirmation with '--force' flag set"
|
|
||||||
else
|
|
||||||
read -p " (y/n) " -n 1
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_confirmed() {
|
|
||||||
if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
function is_not_confirmed() {
|
|
||||||
if [[ "${REPLY}" =~ ^[Nn]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set Flags
|
|
||||||
# -----------------------------------
|
|
||||||
# Flags which can be overridden by user input.
|
|
||||||
# Default values are below
|
|
||||||
# -----------------------------------
|
|
||||||
quiet=false
|
|
||||||
printLog=false
|
|
||||||
verbose=false
|
|
||||||
force=false
|
|
||||||
strict=false
|
|
||||||
debug=false
|
|
||||||
args=()
|
|
||||||
|
|
||||||
# Set Temp Directory
|
|
||||||
# -----------------------------------
|
|
||||||
# Create temp directory with three random numbers and the process ID
|
|
||||||
# in the name. This directory is removed automatically at exit.
|
|
||||||
# -----------------------------------
|
|
||||||
tmpDir="/tmp/${scriptName}.$RANDOM.$RANDOM.$RANDOM.$$"
|
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
|
||||||
die "Could not create temporary directory! Exiting."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
# -----------------------------------
|
|
||||||
# Log is only used when the '-l' flag is set.
|
|
||||||
#
|
|
||||||
# To never save a logfile change variable to '/dev/null'
|
|
||||||
# Save to Desktop use: $HOME/Desktop/${scriptBasename}.log
|
|
||||||
# Save to standard user log location use: $HOME/Library/Logs/${scriptBasename}.log
|
|
||||||
# -----------------------------------
|
|
||||||
logFile="${HOME}/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
|
|
||||||
# Options and Usage
|
|
||||||
# -----------------------------------
|
|
||||||
# Print usage
|
|
||||||
usage() {
|
|
||||||
echo -n "${scriptName} [OPTION]... [FILE]...
|
|
||||||
|
|
||||||
This script configures Mac OS default preferences. Common usage is to bootstrap
|
|
||||||
a new computer at first use.
|
|
||||||
|
|
||||||
|
|
||||||
${bold}Options:${reset}
|
|
||||||
|
|
||||||
--force Skip all user interaction. Implied 'Yes' to all actions.
|
|
||||||
-q, --quiet Quiet (no output)
|
|
||||||
-l, --log Print log to file
|
|
||||||
-s, --strict Exit script with null variables. i.e 'set -o nounset'
|
|
||||||
-v, --verbose Output more information. (Items echoed to 'verbose')
|
|
||||||
-d, --debug Runs script in BASH debug mode (set -x)
|
|
||||||
-h, --help Display this help and exit
|
|
||||||
--version Output version information and exit
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
|
||||||
# --foo bar
|
|
||||||
optstring=h
|
|
||||||
unset options
|
|
||||||
while (($#)); do
|
|
||||||
case $1 in
|
|
||||||
# If option is of type -ab
|
|
||||||
-[!-]?*)
|
|
||||||
# Loop over each character starting with the second
|
|
||||||
for ((i=1; i < ${#1}; i++)); do
|
|
||||||
c=${1:i:1}
|
|
||||||
|
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
|
||||||
# the rest of the string its argument
|
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
|
||||||
options+=("${1:i+1}")
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
|
|
||||||
# If option is of type --foo=bar
|
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
|
||||||
# add --endopts for --
|
|
||||||
--) options+=(--endopts) ;;
|
|
||||||
# Otherwise, nothing special
|
|
||||||
*) options+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
set -- "${options[@]}"
|
|
||||||
unset options
|
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# -------------------------------------
|
|
||||||
# [[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
|
||||||
while [[ $1 = -?* ]]; do
|
|
||||||
case $1 in
|
|
||||||
-h|--help) usage >&2; safeExit ;;
|
|
||||||
--version) echo "$(basename $0) ${version}"; safeExit ;;
|
|
||||||
|
|
||||||
-v|--verbose) verbose=true ;;
|
|
||||||
-l|--log) printLog=true ;;
|
|
||||||
-q|--quiet) quiet=true ;;
|
|
||||||
-s|--strict) strict=true;;
|
|
||||||
-d|--debug) debug=true;;
|
|
||||||
--force) force=true ;;
|
|
||||||
--endopts) shift; break ;;
|
|
||||||
*) die "invalid option: '$1'." ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Store the remaining part as arguments.
|
|
||||||
args+=("$@")
|
|
||||||
|
|
||||||
|
|
||||||
# Logging and Colors
|
|
||||||
# -----------------------------------------------------
|
|
||||||
# Here we set the colors for our script feedback.
|
|
||||||
# Example usage: success "sometext"
|
|
||||||
#------------------------------------------------------
|
|
||||||
|
|
||||||
# Set Colors
|
|
||||||
bold=$(tput bold)
|
|
||||||
reset=$(tput sgr0)
|
|
||||||
purple=$(tput setaf 171)
|
|
||||||
red=$(tput setaf 1)
|
|
||||||
green=$(tput setaf 76)
|
|
||||||
tan=$(tput setaf 3)
|
|
||||||
blue=$(tput setaf 38)
|
|
||||||
underline=$(tput sgr 0 1)
|
|
||||||
|
|
||||||
function _alert() {
|
|
||||||
if [ "${1}" = "emergency" ]; then local color="${bold}${red}"; fi
|
|
||||||
if [ "${1}" = "error" ]; then local color="${bold}${red}"; fi
|
|
||||||
if [ "${1}" = "warning" ]; then local color="${red}"; fi
|
|
||||||
if [ "${1}" = "success" ]; then local color="${green}"; fi
|
|
||||||
if [ "${1}" = "debug" ]; then local color="${purple}"; fi
|
|
||||||
if [ "${1}" = "header" ]; then local color="${bold}${tan}"; fi
|
|
||||||
if [ "${1}" = "input" ]; then local color="${bold}"; printLog="false"; fi
|
|
||||||
if [ "${1}" = "info" ] || [ "${1}" = "notice" ]; then local color=""; fi
|
|
||||||
# Don't use colors on pipes or non-recognized terminals
|
|
||||||
if [[ "${TERM}" != "xterm"* ]] || [ -t 1 ]; then color=""; reset=""; fi
|
|
||||||
|
|
||||||
# Print to $logFile
|
|
||||||
if ${printLog}; then
|
|
||||||
echo -e "$(date +"%m-%d-%Y %r") $(printf "[%9s]" "${1}") ${_message}" >> "${logFile}";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print to console when script is not 'quiet'
|
|
||||||
if ${quiet}; then
|
|
||||||
return
|
|
||||||
else
|
|
||||||
echo -e "$(date +"%r") ${color}$(printf "[%9s]" "${1}") ${_message}${reset}";
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function die () { local _message="${*} Exiting."; echo "$(_alert emergency)"; safeExit;}
|
|
||||||
function error () { local _message="${*}"; echo "$(_alert error)"; }
|
|
||||||
function warning () { local _message="${*}"; echo "$(_alert warning)"; }
|
|
||||||
function notice () { local _message="${*}"; echo "$(_alert notice)"; }
|
|
||||||
function info () { local _message="${*}"; echo "$(_alert info)"; }
|
|
||||||
function debug () { local _message="${*}"; echo "$(_alert debug)"; }
|
|
||||||
function success () { local _message="${*}"; echo "$(_alert success)"; }
|
|
||||||
function input() { local _message="${*}"; echo -n "$(_alert input)"; }
|
|
||||||
function header() { local _message="${*}"; echo "$(_alert header)"; }
|
|
||||||
|
|
||||||
# Log messages when verbose is set to "true"
|
|
||||||
verbose() { if ${verbose}; then debug "$@"; fi }
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
|
||||||
trap trapCleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Set IFS to preferred implementation
|
|
||||||
IFS=$' \n\t'
|
|
||||||
|
|
||||||
# Exit on error. Append '||true' when you run the script if you expect an error.
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
# Run in debug mode, if set
|
|
||||||
if ${debug}; then set -x ; fi
|
|
||||||
|
|
||||||
# Exit on empty variable
|
|
||||||
if ${strict}; then set -o nounset ; fi
|
|
||||||
|
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Run your script
|
|
||||||
mainScript
|
|
||||||
|
|
||||||
# Exit cleanly
|
|
||||||
safeExit
|
|
||||||
@@ -1,250 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
#
|
|
||||||
version="1.0.0" # Sets version variable
|
|
||||||
#
|
|
||||||
# HISTORY:
|
|
||||||
#
|
|
||||||
# * DATE - v1.0.0 - First Creation
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
|
|
||||||
function mainScript() {
|
|
||||||
echo -n
|
|
||||||
}
|
|
||||||
|
|
||||||
function trapCleanup() {
|
|
||||||
echo ""
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
die "Exit trapped. In function: '${FUNCNAME[*]}'"
|
|
||||||
}
|
|
||||||
|
|
||||||
function safeExit() {
|
|
||||||
# Delete temp files, if any
|
|
||||||
if [ -d "${tmpDir}" ] ; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
trap - INT TERM EXIT
|
|
||||||
exit
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set Base Variables
|
|
||||||
# ----------------------
|
|
||||||
scriptName=$(basename "$0")
|
|
||||||
|
|
||||||
# Set Flags
|
|
||||||
quiet=false
|
|
||||||
printLog=false
|
|
||||||
verbose=false
|
|
||||||
force=false
|
|
||||||
strict=false
|
|
||||||
debug=false
|
|
||||||
args=()
|
|
||||||
|
|
||||||
# Set Colors
|
|
||||||
bold=$(tput bold)
|
|
||||||
reset=$(tput sgr0)
|
|
||||||
purple=$(tput setaf 171)
|
|
||||||
red=$(tput setaf 1)
|
|
||||||
green=$(tput setaf 76)
|
|
||||||
tan=$(tput setaf 3)
|
|
||||||
blue=$(tput setaf 38)
|
|
||||||
underline=$(tput sgr 0 1)
|
|
||||||
|
|
||||||
# Set Temp Directory
|
|
||||||
tmpDir="/tmp/${scriptName}.$RANDOM.$RANDOM.$RANDOM.$$"
|
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
|
||||||
die "Could not create temporary directory! Exiting."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
# -----------------------------------
|
|
||||||
# Log is only used when the '-l' flag is set.
|
|
||||||
logFile="${HOME}/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
|
|
||||||
# Options and Usage
|
|
||||||
# -----------------------------------
|
|
||||||
usage() {
|
|
||||||
echo -n "${scriptName} [OPTION]... [FILE]...
|
|
||||||
|
|
||||||
This is a script template. Edit this description to print help to users.
|
|
||||||
|
|
||||||
${bold}Options:${reset}
|
|
||||||
-u, --username Username for script
|
|
||||||
-p, --password User password
|
|
||||||
--force Skip all user interaction. Implied 'Yes' to all actions.
|
|
||||||
-q, --quiet Quiet (no output)
|
|
||||||
-l, --log Print log to file
|
|
||||||
-s, --strict Exit script with null variables. i.e 'set -o nounset'
|
|
||||||
-v, --verbose Output more information. (Items echoed to 'verbose')
|
|
||||||
-d, --debug Runs script in BASH debug mode (set -x)
|
|
||||||
-h, --help Display this help and exit
|
|
||||||
--version Output version information and exit
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
|
||||||
# --foo bar
|
|
||||||
optstring=h
|
|
||||||
unset options
|
|
||||||
while (($#)); do
|
|
||||||
case $1 in
|
|
||||||
# If option is of type -ab
|
|
||||||
-[!-]?*)
|
|
||||||
# Loop over each character starting with the second
|
|
||||||
for ((i=1; i < ${#1}; i++)); do
|
|
||||||
c=${1:i:1}
|
|
||||||
|
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
|
||||||
# the rest of the string its argument
|
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
|
||||||
options+=("${1:i+1}")
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
|
|
||||||
# If option is of type --foo=bar
|
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
|
||||||
# add --endopts for --
|
|
||||||
--) options+=(--endopts) ;;
|
|
||||||
# Otherwise, nothing special
|
|
||||||
*) options+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
set -- "${options[@]}"
|
|
||||||
unset options
|
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# -------------------------------------
|
|
||||||
[[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
|
||||||
while [[ $1 = -?* ]]; do
|
|
||||||
case $1 in
|
|
||||||
-h|--help) usage >&2; safeExit ;;
|
|
||||||
--version) echo "$(basename $0) ${version}"; safeExit ;;
|
|
||||||
-u|--username) shift; username=${1} ;;
|
|
||||||
-p|--password) shift; echo "Enter Pass: "; stty -echo; read PASS; stty echo;
|
|
||||||
echo ;;
|
|
||||||
-v|--verbose) verbose=true ;;
|
|
||||||
-l|--log) printLog=true ;;
|
|
||||||
-q|--quiet) quiet=true ;;
|
|
||||||
-s|--strict) strict=true;;
|
|
||||||
-d|--debug) debug=true;;
|
|
||||||
--force) force=true ;;
|
|
||||||
--endopts) shift; break ;;
|
|
||||||
*) die "invalid option: '$1'." ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Store the remaining part as arguments.
|
|
||||||
args+=("$@")
|
|
||||||
|
|
||||||
|
|
||||||
# Logging & Feedback
|
|
||||||
# -----------------------------------------------------
|
|
||||||
function _alert() {
|
|
||||||
if [ "${1}" = "error" ]; then local color="${bold}${red}"; fi
|
|
||||||
if [ "${1}" = "warning" ]; then local color="${red}"; fi
|
|
||||||
if [ "${1}" = "success" ]; then local color="${green}"; fi
|
|
||||||
if [ "${1}" = "debug" ]; then local color="${purple}"; fi
|
|
||||||
if [ "${1}" = "header" ]; then local color="${bold}${tan}"; fi
|
|
||||||
if [ "${1}" = "input" ]; then local color="${bold}"; fi
|
|
||||||
if [ "${1}" = "info" ] || [ "${1}" = "notice" ]; then local color=""; fi
|
|
||||||
# Don't use colors on pipes or non-recognized terminals
|
|
||||||
if [[ "${TERM}" != "xterm"* ]] || [ -t 1 ]; then color=""; reset=""; fi
|
|
||||||
|
|
||||||
# Print to console when script is not 'quiet'
|
|
||||||
if ${quiet}; then return; else
|
|
||||||
echo -e "$(date +"%r") ${color}$(printf "[%7s]" "${1}") ${_message}${reset}";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Print to Logfile
|
|
||||||
if ${printLog} && [ "${1}" != "input" ]; then
|
|
||||||
color=""; reset="" # Don't use colors in logs
|
|
||||||
echo -e "$(date +"%m-%d-%Y %r") $(printf "[%7s]" "${1}") ${_message}" >> "${logFile}";
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function die () { local _message="${*} Exiting."; echo -e "$(_alert error)"; safeExit;}
|
|
||||||
function error () { local _message="${*}"; echo -e "$(_alert error)"; }
|
|
||||||
function warning () { local _message="${*}"; echo -e "$(_alert warning)"; }
|
|
||||||
function notice () { local _message="${*}"; echo -e "$(_alert notice)"; }
|
|
||||||
function info () { local _message="${*}"; echo -e "$(_alert info)"; }
|
|
||||||
function debug () { local _message="${*}"; echo -e "$(_alert debug)"; }
|
|
||||||
function success () { local _message="${*}"; echo -e "$(_alert success)"; }
|
|
||||||
function input() { local _message="${*}"; echo -n "$(_alert input)"; }
|
|
||||||
function header() { local _message="== ${*} == "; echo -e "$(_alert header)"; }
|
|
||||||
function verbose() { if ${verbose}; then debug "$@"; fi }
|
|
||||||
|
|
||||||
# SEEKING CONFIRMATION
|
|
||||||
# ------------------------------------------------------
|
|
||||||
function seek_confirmation() {
|
|
||||||
# echo ""
|
|
||||||
input "$@"
|
|
||||||
if "${force}"; then
|
|
||||||
notice "Forcing confirmation with '--force' flag set"
|
|
||||||
else
|
|
||||||
read -p " (y/n) " -n 1
|
|
||||||
echo ""
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
function is_confirmed() {
|
|
||||||
if "${force}"; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
if [[ "${REPLY}" =~ ^[Yy]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
function is_not_confirmed() {
|
|
||||||
if "${force}"; then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
if [[ "${REPLY}" =~ ^[Nn]$ ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
|
||||||
trap trapCleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Set IFS to preferred implementation
|
|
||||||
IFS=$' \n\t'
|
|
||||||
|
|
||||||
# Exit on error. Append '||true' when you run the script if you expect an error.
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
# Run in debug mode, if set
|
|
||||||
if ${debug}; then set -x ; fi
|
|
||||||
|
|
||||||
# Exit on empty variable
|
|
||||||
if ${strict}; then set -o nounset ; fi
|
|
||||||
|
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`, for example.
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Run your script
|
|
||||||
mainScript
|
|
||||||
|
|
||||||
# Exit cleanly
|
|
||||||
safeExit
|
|
||||||
4
syncScripts/.gitignore
vendored
4
syncScripts/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
*
|
|
||||||
!.gitignore
|
|
||||||
!syncTemplate.sh
|
|
||||||
!README.md
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# syncTemplate.sh
|
|
||||||
This sync script template provides a mechanism to use either [rsync][1] or [unision][2] to keep two directories in sync.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
1. Execute the template and follow the prompts to create a new script for your needs
|
|
||||||
2. Execute the new script to create a new configuration file.
|
|
||||||
3. Edit the information within that configuration file.
|
|
||||||
4. Execute the script again. It will optionally encrypt your configuration file to keep your passwords safe, and then sync your directories.
|
|
||||||
|
|
||||||
For help and additional options, run the script with the `-h` flag.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[1]: https://rsync.samba.org
|
|
||||||
[2]: http://www.cis.upenn.edu/~bcpierce/unison/
|
|
||||||
@@ -1,630 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
|
|
||||||
# ##################################################
|
|
||||||
# My Generic sync script.
|
|
||||||
#
|
|
||||||
version="2.1.0" # Sets version variable
|
|
||||||
#
|
|
||||||
scriptTemplateVersion="1.1.1" # Version of scriptTemplate.sh that this script is based on
|
|
||||||
# v.1.1.0 - Added 'debug' option
|
|
||||||
# v.1.1.1 - Moved all shared variables to Utils
|
|
||||||
#
|
|
||||||
# This script will give you the option of using rsync
|
|
||||||
# or Unison. Rsync is for one-way syncing, Unison is for
|
|
||||||
# two-way syncing.
|
|
||||||
#
|
|
||||||
# Depending on what flags are passed to the script and
|
|
||||||
# what information is written in the config file, this script
|
|
||||||
# will perform different behavior.
|
|
||||||
#
|
|
||||||
# USAGE:
|
|
||||||
#
|
|
||||||
# 1) IMPORTANT: Copy this script and rename it for your purpose before running.
|
|
||||||
# 2) Run the script. This will create a blank config file for you and then exit.
|
|
||||||
# 3) Enter your information within the config file
|
|
||||||
# 4) Run the script again.
|
|
||||||
#
|
|
||||||
# TO DO:
|
|
||||||
# * Add SSH functionality
|
|
||||||
#
|
|
||||||
# DISCLAIMER:
|
|
||||||
# I am a novice programmer and I bear no responsibility whatsoever
|
|
||||||
# if this (or any other) script that I write wipes your computer,
|
|
||||||
# destroys your data, crashes your car, or otherwise causes mayhem
|
|
||||||
# and destruction. USE AT YOUR OWN RISK.
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# HISTORY:
|
|
||||||
# * 2015-01-02 - v1.0.0 - First Creation
|
|
||||||
# * 2015-01-03 - v1.1.0 - Added support for using roots in Unison .prf
|
|
||||||
# * 2015-03-10 - v1.1.1 - Updated script template version
|
|
||||||
# - Removed $logFile from config. Default is now '~/library/logs/'
|
|
||||||
# * 2015-03-15 - v2.0.0 - Added support for encrypted config files.
|
|
||||||
# * 2015-03-21 - v2.1.0 - Added support for extended RSYNC configurations.
|
|
||||||
#
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
# Source Scripting Utilities
|
|
||||||
# Source Scripting Utilities
|
|
||||||
# -----------------------------------
|
|
||||||
# If these can't be found, update the path to the file
|
|
||||||
# -----------------------------------
|
|
||||||
scriptPath="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
||||||
if [ -f "${scriptPath}/../lib/utils.sh" ]; then
|
|
||||||
source "${scriptPath}/../lib/utils.sh"
|
|
||||||
else
|
|
||||||
echo "Please find the file util.sh and add a reference to it in this script. Exiting."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# trapCleanup Function
|
|
||||||
# -----------------------------------
|
|
||||||
# Any actions that should be taken if the script is prematurely
|
|
||||||
# exited. Always call this function at the top of your script.
|
|
||||||
# -----------------------------------
|
|
||||||
function trapCleanup() {
|
|
||||||
echo ""
|
|
||||||
if is_dir "${tmpDir}"; then
|
|
||||||
rm -r "${tmpDir}"
|
|
||||||
fi
|
|
||||||
die "Exit trapped." # Edit this if you like.
|
|
||||||
}
|
|
||||||
|
|
||||||
# Set Flags
|
|
||||||
# -----------------------------------
|
|
||||||
# Flags which can be overridden by user input.
|
|
||||||
# Default values are below
|
|
||||||
# -----------------------------------
|
|
||||||
quiet=0
|
|
||||||
printLog=0
|
|
||||||
verbose=0
|
|
||||||
force=0
|
|
||||||
strict=0
|
|
||||||
debug=0
|
|
||||||
editConfig=0
|
|
||||||
mountTest=0
|
|
||||||
|
|
||||||
# Set Temp Directory
|
|
||||||
# -----------------------------------
|
|
||||||
# Create temp directory with three random numbers and the process ID
|
|
||||||
# in the name. This directory is removed automatically at exit.
|
|
||||||
# -----------------------------------
|
|
||||||
tmpDir="/tmp/${scriptName}.$RANDOM.$RANDOM.$RANDOM.$$"
|
|
||||||
(umask 077 && mkdir "${tmpDir}") || {
|
|
||||||
die "Could not create temporary directory! Exiting."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
# -----------------------------------
|
|
||||||
# Log is only used when the '-l' flag is set.
|
|
||||||
#
|
|
||||||
# To never save a logfile change variable to '/dev/null'
|
|
||||||
# Save to Desktop use: $HOME/Desktop/${scriptBasename}.log
|
|
||||||
# Save to standard user log location use: $HOME/Library/Logs/${scriptBasename}.log
|
|
||||||
# -----------------------------------
|
|
||||||
logFile="$HOME/Library/Logs/${scriptBasename}.log"
|
|
||||||
|
|
||||||
|
|
||||||
# Configuration file(s)
|
|
||||||
# -----------------------------------
|
|
||||||
# This script calls for a configuration file.
|
|
||||||
# This is its location. Default is the location
|
|
||||||
# where it will be automatically created.`
|
|
||||||
# -----------------------------------
|
|
||||||
tmpConfig="${tmpDir}/${scriptName}.cfg"
|
|
||||||
newConfig="./${scriptName}.cfg"
|
|
||||||
encConfig="../etc/${scriptName}.cfg.enc"
|
|
||||||
|
|
||||||
############## Begin Script Functions Here ###################
|
|
||||||
|
|
||||||
# Create new copy of the script if template is being executed
|
|
||||||
function newCopy() {
|
|
||||||
if [ "${scriptName}" = "syncTemplate.sh" ]; then
|
|
||||||
input "name your new script:"
|
|
||||||
read newname
|
|
||||||
verbose "Copying SyncTemplate.sh to ${newname}"
|
|
||||||
cp "${scriptPath}"/"${scriptName}" "${scriptPath}"/"${newname}" && verbose "cp ${scriptPath}/${scriptName} ${scriptPath}/${newname}"
|
|
||||||
success "${newname} created."
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function encryptConfig() {
|
|
||||||
# If a non-encrypted config file exists (ie - it was being edited) we encrypt it
|
|
||||||
if is_file "${newConfig}"; then
|
|
||||||
verbose "${newConfig} exists"
|
|
||||||
seek_confirmation "Are you ready to encrypt your config file?"
|
|
||||||
if is_confirmed; then
|
|
||||||
if is_file "${encConfig}"; then
|
|
||||||
rm "${encConfig}" && verbose "Existing encoded config file exists. Running: rm ${encConfig}"
|
|
||||||
fi
|
|
||||||
if is_empty ${PASS}; then # Look for password from CLI
|
|
||||||
verbose "openssl enc -aes-256-cbc -salt -in ${newConfig} -out ${encConfig}"
|
|
||||||
openssl enc -aes-256-cbc -salt -in "${newConfig}" -out "${encConfig}"
|
|
||||||
else
|
|
||||||
verbose "openssl enc -aes-256-cbc -salt -in ${newConfig} -out ${encConfig} -k [PASSWORD]"
|
|
||||||
openssl enc -aes-256-cbc -salt -in "${newConfig}" -out "${encConfig}" -k ${PASS}
|
|
||||||
fi
|
|
||||||
rm "${newConfig}" && verbose "rm ${newConfig}"
|
|
||||||
success "Encoded the config file."
|
|
||||||
safeExit
|
|
||||||
else
|
|
||||||
warning "You need to encrypt your config file before proceeding"
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTempConfig() {
|
|
||||||
# If we find the encoded config file, we decrypt it to the temp location
|
|
||||||
if is_file "${encConfig}"; then
|
|
||||||
if is_empty ${PASS}; then # Look for password from CLI
|
|
||||||
verbose "openssl enc -aes-256-cbc -d -in ${encConfig} -out ${tmpConfig}"
|
|
||||||
openssl enc -aes-256-cbc -d -in "${encConfig}" -out "${tmpConfig}"
|
|
||||||
else
|
|
||||||
verbose "openssl enc -aes-256-cbc -d -in ${encConfig} -out ${tmpConfig} -k [PASSWORD]"
|
|
||||||
openssl enc -aes-256-cbc -d -in "${encConfig}" -out "${tmpConfig}" -k ${PASS}
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function sourceConfiguration() {
|
|
||||||
# Here we source the Config file or create a new one if none exists.
|
|
||||||
if is_file "${tmpConfig}"; then
|
|
||||||
source "${tmpConfig}" && verbose "source ${tmpConfig}"
|
|
||||||
else
|
|
||||||
seek_confirmation "Config file does not exist. Would you like to create one?"
|
|
||||||
if is_not_confirmed; then
|
|
||||||
die "No config file."
|
|
||||||
else
|
|
||||||
touch "${newConfig}" && verbose "touch ${newConfig}"
|
|
||||||
cat >"${newConfig}" <<EOL
|
|
||||||
# ##################################################
|
|
||||||
# CONFIG FILE FOR ${scriptName}
|
|
||||||
# CREATED ON ${now}
|
|
||||||
#
|
|
||||||
# Created by version "$version" of "SyncTemplate.sh"
|
|
||||||
# ##################################################
|
|
||||||
|
|
||||||
# METHOD
|
|
||||||
# ---------------------------
|
|
||||||
# This script will work with both Unison and rsync.
|
|
||||||
# Set the METHOD variable to either 'unison' or 'rsync'
|
|
||||||
METHOD=""
|
|
||||||
|
|
||||||
# ---------------------------
|
|
||||||
# Network Volume Mounting
|
|
||||||
# ---------------------------
|
|
||||||
# If one of the directies you need to sync is on a network drive set
|
|
||||||
# the variable NEEDMOUNT to 'true'
|
|
||||||
NEEDMOUNT="false"
|
|
||||||
|
|
||||||
# MOUNTPOINT is the address of the drive to be mounted.
|
|
||||||
# Use the format afp://username:password@address/mountname
|
|
||||||
MOUNTPOINT=""
|
|
||||||
|
|
||||||
# REMOTEVOLUME is the directory that the drive should be mounted
|
|
||||||
# into on the local computer. Typically this is in the /Volumes/ dir.
|
|
||||||
# and should be named the same as the mountname in the MOUNTPOINT.
|
|
||||||
# Use a complete path, not a relative path without a trailing slash.
|
|
||||||
REMOTEVOLUME=""
|
|
||||||
|
|
||||||
# ---------------------------
|
|
||||||
# Directories To Sync
|
|
||||||
# ---------------------------
|
|
||||||
# These are the COMPLETE paths two directories that will be synced.
|
|
||||||
# Be sure to include trailing slashes on directories.
|
|
||||||
SOURCEDIRECTORY=""
|
|
||||||
TARGETDIRECTORY=""
|
|
||||||
|
|
||||||
# ---------------------------
|
|
||||||
# UNISON PREFERENCES
|
|
||||||
# ---------------------------
|
|
||||||
# If you are using UNISON to sync your directories, fill in this section.
|
|
||||||
|
|
||||||
# Unison keeps its own config profiles which configure
|
|
||||||
# much of the script. These .prf files are located in '~/.unison/'
|
|
||||||
# more info: http://www.cis.upenn.edu/~bcpierce/unison/download/releases/stable/unison-manual.html
|
|
||||||
#
|
|
||||||
# If you wish to use a Unison profile change USERPROFILE to 'true'
|
|
||||||
# and add the profile name to UNISONPROFILE.
|
|
||||||
#
|
|
||||||
# If your Unison profile contains the 'roots' to by synced, change PROFILEROOTS to 'true'.
|
|
||||||
# If this remains 'false', the directories to by synced will be the ones specified above.
|
|
||||||
USEPROFILE="false"
|
|
||||||
PROFILEROOTS="false"
|
|
||||||
UNISONPROFILE=""
|
|
||||||
|
|
||||||
# ---------------------------
|
|
||||||
# RSYNC PREFENCES
|
|
||||||
# ---------------------------
|
|
||||||
# If you are using rsync, complete this section
|
|
||||||
|
|
||||||
# EXCLUDE sets rsync to exclude files from syncing based on a pattern.
|
|
||||||
# Defaults to null.
|
|
||||||
# If needed, add individual excludes in the format of "--exclude file1.txt --exclude file2.txt".
|
|
||||||
EXCLUDE=""
|
|
||||||
|
|
||||||
# EXCLUDELIST is a text file that contains all the rsync excludes.
|
|
||||||
# Anything listed within this file will be ignored during sync.
|
|
||||||
# Default is null.
|
|
||||||
# Set value to "--exclude-from=/some/file/location.txt" if needed
|
|
||||||
EXCLUDELIST=""
|
|
||||||
|
|
||||||
# DELETE sets the variable to delete files in the target directory that are deleted from
|
|
||||||
# the source directory. In effect, keeping them 'in-sync'. Defaults to equal "--delete"
|
|
||||||
# which sets that flag. Set to null to ensure all files on the target remain when deleted
|
|
||||||
# from the source.
|
|
||||||
DELETE="--delete"
|
|
||||||
|
|
||||||
# ---------------------------
|
|
||||||
# ADDITIONAL OPTIONS
|
|
||||||
# ---------------------------
|
|
||||||
|
|
||||||
# PUSHOVER is an online notification tool.
|
|
||||||
# If you want to receive notifications upon completion
|
|
||||||
# set the following value to "true"
|
|
||||||
PUSHOVERNOTIFY="false"
|
|
||||||
|
|
||||||
# CANONICALHOST is used to denote a sync hub which should never initiate a sync.
|
|
||||||
# Leave blank if not needed.
|
|
||||||
CANONICALHOST=""
|
|
||||||
EOL
|
|
||||||
success "Config file created. Edit the values before running this script again."
|
|
||||||
notice "The file is located at: ${newConfig}. Exiting."
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function editConfiguration() {
|
|
||||||
# If the '--config' is set to true, we create an editable config file for re-encryption
|
|
||||||
if [ "${editConfig}" == "1" ]; then
|
|
||||||
verbose "editConfig is true"
|
|
||||||
seek_confirmation "Would you like to edit your config file?"
|
|
||||||
if is_confirmed; then
|
|
||||||
if is_file "${tmpConfig}"; then
|
|
||||||
cp "${tmpConfig}" "${newConfig}" && verbose "cp ${tmpConfig} ${newConfig}"
|
|
||||||
success "Config file has been decrypted to ${newConfig}. Edit the file and rerun the script."
|
|
||||||
safeExit
|
|
||||||
else
|
|
||||||
die "Couldn't find ${tmpConfig}."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
notice "Exiting."
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
# HostCheck
|
|
||||||
# Confirm we can run this script. If a canonical host is set in
|
|
||||||
# the config file we check it here.
|
|
||||||
function hostCheck() {
|
|
||||||
if [ "${thisHost}" = "${CANONICALHOST}" ]; then
|
|
||||||
notice "We are currently on ${THISHOST} and can not proceed. Be sure to run this script on the non-canonical host. Exiting"
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# MethodCheck
|
|
||||||
# Confirm we have either Unison or Rsync specified
|
|
||||||
# in the config file. Exit if not.
|
|
||||||
function MethodCheck() {
|
|
||||||
if [ "${METHOD}" != "rsync" ] && [ "${METHOD}" != "unison" ]; then
|
|
||||||
die "Script aborted without a method specified in the config file."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function moutDrives() {
|
|
||||||
if [ "${NEEDMOUNT}" = "true" ] || [ "${NEEDMOUNT}" = "TRUE" ] || [ "${NEEDMOUNT}" = "True" ]; then
|
|
||||||
# Mount AFP volume
|
|
||||||
if is_not_dir "${REMOTEVOLUME}"; then
|
|
||||||
notice "Mounting drive"
|
|
||||||
mkdir "${REMOTEVOLUME}" && verbose "mkdir ${REMOTEVOLUME}"
|
|
||||||
if [ "${MOUTPW}" = "true" ]; then # if password prompt needed
|
|
||||||
mount_afp -i "${MOUNTPOINT}" "${REMOTEVOLUME}" && verbose "mount_afp -i ${MOUNTPOINT} ${REMOTEVOLUME}"
|
|
||||||
else
|
|
||||||
mount_afp "${MOUNTPOINT}" "${REMOTEVOLUME}" && verbose "mount_afp ${MOUNTPOINT} ${REMOTEVOLUME}"
|
|
||||||
fi
|
|
||||||
sleep 5
|
|
||||||
notice "${REMOTEVOLUME} Mounted"
|
|
||||||
else
|
|
||||||
notice "${REMOTEVOLUME} was already mounted."
|
|
||||||
fi
|
|
||||||
|
|
||||||
#Allow for debugging with only the mount function
|
|
||||||
if [ ${mountTest} = 1 ]; then
|
|
||||||
seek_confirmation "Are you ready to unmount the drive"
|
|
||||||
if is_confirmed; then
|
|
||||||
unmountDrives
|
|
||||||
safeExit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function unmountDrives() {
|
|
||||||
# Unmount the drive (if mounted)
|
|
||||||
if [ "${NEEDMOUNT}" = "true" ] || [ "${NEEDMOUNT}" = "TRUE" ]; then
|
|
||||||
unmountDrive "${REMOTEVOLUME}" && verbose "unmountDrive ${REMOTEVOLUME}"
|
|
||||||
notice "${REMOTEVOLUME} UnMounted"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function testSources() {
|
|
||||||
# Test for source directories.
|
|
||||||
# If they don't exist we can't continue
|
|
||||||
|
|
||||||
# test for target
|
|
||||||
if is_dir "${TARGETDIRECTORY}"; then
|
|
||||||
verbose "${TARGETDIRECTORY} exists"
|
|
||||||
else
|
|
||||||
if [ "${NEEDMOUNT}" = "true" ] || [ "${NEEDMOUNT}" = "TRUE" ]; then
|
|
||||||
unmountDrive "${REMOTEVOLUME}" && verbose "Unmounting ${REMOTEVOLUME}"
|
|
||||||
if is_dir "${REMOTEVOLUME}"; then
|
|
||||||
rm -r "${REMOTEVOLUME}" && verbose "rm -r ${REMOTEVOLUME}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
die "Target directory: ${TARGETDIRECTORY} does not exist."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Test for source directory
|
|
||||||
if is_dir "${SOURCEDIRECTORY}"; then
|
|
||||||
verbose "${SOURCEDIRECTORY} exists"
|
|
||||||
else
|
|
||||||
if [ "${NEEDMOUNT}" = "true" ] || [ "${NEEDMOUNT}" = "TRUE" ]; then
|
|
||||||
unmountDrive "${REMOTEVOLUME}" && verbose "Unmounting ${REMOTEVOLUME}"
|
|
||||||
if is_dir "${REMOTEVOLUME}"; then
|
|
||||||
rm -r "${REMOTEVOLUME}" && verbose "rm -r ${REMOTEVOLUME}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
die "Source directory: ${SOURCEDIRECTORY} does not exist."
|
|
||||||
fi
|
|
||||||
notice "Source directories passed filesystem check. Continuing."
|
|
||||||
}
|
|
||||||
|
|
||||||
function runRsync() {
|
|
||||||
# Populate logfile variable if "printlog=1"
|
|
||||||
if [ "${printLog}" = 1 ]; then
|
|
||||||
RSYNCLOG="--log-file=${logFile}"
|
|
||||||
else
|
|
||||||
RSYNCLOG=""
|
|
||||||
fi
|
|
||||||
if [ "${METHOD}" = "rsync" ]; then
|
|
||||||
notice "Commencing rsync"
|
|
||||||
verbose "rsync -vahh${DRYRUN}${COMPRESS} --progress --force ${DELETE} ${EXCLUDE} ${EXCLUDELIST} ${SOURCEDIRECTORY} ${TARGETDIRECTORY} ${RSYNCLOG}"
|
|
||||||
rsync -vahh${DRYRUN}${COMPRESS} --progress --force ${DELETE} ${EXCLUDE} ${EXCLUDELIST} "${SOURCEDIRECTORY}" "${TARGETDIRECTORY}" ${RSYNCLOG}
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function runUnison() {
|
|
||||||
if [ "${METHOD}" = "unison" ]; then
|
|
||||||
# Check if Unison is installed. It is not a standard package
|
|
||||||
if type_not_exists 'unison'; then
|
|
||||||
seek_confirmation "Unison not installed, try to install it with Homebrew?"
|
|
||||||
if is_confirmed; then
|
|
||||||
notice "Attempting to install Unison."
|
|
||||||
hasHomebrew
|
|
||||||
brew install unison
|
|
||||||
else
|
|
||||||
if [ "${NEEDMOUNT}" = "true" ] || [ "${NEEDMOUNT}" = "TRUE" ]; then
|
|
||||||
unmountDrive "${REMOTEVOLUME}" && verbose "unmountDrive ${REMOTEVOLUME}"
|
|
||||||
if is_dir "${REMOTEVOLUME}"; then
|
|
||||||
rm -r "${REMOTEVOLUME}" && verbose "rm -r ${REMOTEVOLUME}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
die "Can not continue without having Unison installed."
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run Unison
|
|
||||||
if [ "${PROFILEROOTS}" = "true" ]; then
|
|
||||||
# Throw error if we don't have enough information
|
|
||||||
if [ "${USEPROFILE}" = "false" ] || [ "${UNISONPROFILE}" = "" ]; then
|
|
||||||
die "We were missing the Unison Profile. Could not sync."
|
|
||||||
fi
|
|
||||||
# Run unison with a profile and no sources
|
|
||||||
notice "Commencing Unison"
|
|
||||||
verbose "unison ${UNISONPROFILE}" && unison "${UNISONPROFILE}"
|
|
||||||
else
|
|
||||||
if [ "${USEPROFILE}" = "true" ]; then
|
|
||||||
# Throw error if we can't find the profile
|
|
||||||
if [ "${UNISONPROFILE}" = "" ]; then
|
|
||||||
die "We were missing the Unison Profile. Could not sync."
|
|
||||||
fi
|
|
||||||
# Run unison with a profile and specified sources
|
|
||||||
notice "Commencing Unison"
|
|
||||||
verbose "unision ${UNISONPROFILE} ${SOURCEDIRECTORY} ${TARGETDIRECTORY}" && unison "${UNISONPROFILE}" "${SOURCEDIRECTORY}" "${TARGETDIRECTORY}"
|
|
||||||
else
|
|
||||||
# Run Unison without a profile
|
|
||||||
notice "Commencing Unison"
|
|
||||||
verbose "unison ${SOURCEDIRECTORY} ${TARGETDIRECTORY}" && unison "${SOURCEDIRECTORY}" "${TARGETDIRECTORY}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
function notifyPushover() {
|
|
||||||
if [ "${PUSHOVERNOTIFY}" = "true" ]; then
|
|
||||||
verbose "\"pushover ${SCRIPTNAME} Completed\" \"${SCRIPTNAME} was run in $(convertsecs $TOTALTIME)\""
|
|
||||||
pushover "${SCRIPTNAME} Completed" "${SCRIPTNAME} was run in $(convertsecs $TOTALTIME)"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
############## End Script Functions Here ###################
|
|
||||||
|
|
||||||
|
|
||||||
############## Begin Options and Usage ###################
|
|
||||||
|
|
||||||
|
|
||||||
# Print usage
|
|
||||||
usage() {
|
|
||||||
echo -n "${scriptName} [OPTION]... [FILE]...
|
|
||||||
|
|
||||||
This script will give you the option of using rsync
|
|
||||||
or Unison. Rsync is for one-way syncing, Unison is for
|
|
||||||
two-way syncing.
|
|
||||||
|
|
||||||
Depending on what flags are passed to the script and
|
|
||||||
what information is written in the config file, this script
|
|
||||||
will perform different behavior.
|
|
||||||
|
|
||||||
USAGE:
|
|
||||||
1) Copy this script and rename it for your purpose before running.
|
|
||||||
The script will do this for you when run.
|
|
||||||
2) Run the new script. This will create a blank config file
|
|
||||||
for you and then exit.
|
|
||||||
3) Enter your information within the config file
|
|
||||||
4) Run the script again.
|
|
||||||
|
|
||||||
This script requires an encoded config file located at: ${encConfig}
|
|
||||||
Ensure that the config file is correct before running.
|
|
||||||
If the config file is not found at all, the script will
|
|
||||||
create a new one for you.
|
|
||||||
|
|
||||||
To edit the configuration file, run the script with the '-c' flag.
|
|
||||||
|
|
||||||
Options:
|
|
||||||
-c, --config Decrypts the configuration file to allow it to be edited.
|
|
||||||
-d, --debug Prints commands to console. Runs no syncs.
|
|
||||||
-f, --force Rsync only. Skip all user interaction. Implied 'Yes' to all actions.
|
|
||||||
-h, --help Display this help and exit.
|
|
||||||
-l, --log Print log to file.
|
|
||||||
-n, --dryrun Rsync only. Dry run - will run everything without making any changes.
|
|
||||||
-m, --mounttest Will run the mount/unmount drive portion of the script and bypass all syncing.
|
|
||||||
-p, --password Prompts for the password which decrypts the configuration file.
|
|
||||||
-q, --quiet Quiet (no output)
|
|
||||||
-s, --strict Exit script with null variables. 'set -o nounset'
|
|
||||||
-v, --verbose Output more information. (Items echoed to 'verbose')
|
|
||||||
-z, --compress Rsync only. This will compress data before transferring.
|
|
||||||
Good for slow internet connections.
|
|
||||||
--version Output version information and exit
|
|
||||||
"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Iterate over options breaking -ab into -a -b when needed and --foo=bar into
|
|
||||||
# --foo bar
|
|
||||||
optstring=h
|
|
||||||
unset options
|
|
||||||
while (($#)); do
|
|
||||||
case $1 in
|
|
||||||
# If option is of type -ab
|
|
||||||
-[!-]?*)
|
|
||||||
# Loop over each character starting with the second
|
|
||||||
for ((i=1; i < ${#1}; i++)); do
|
|
||||||
c=${1:i:1}
|
|
||||||
|
|
||||||
# Add current char to options
|
|
||||||
options+=("-$c")
|
|
||||||
|
|
||||||
# If option takes a required argument, and it's not the last char make
|
|
||||||
# the rest of the string its argument
|
|
||||||
if [[ $optstring = *"$c:"* && ${1:i+1} ]]; then
|
|
||||||
options+=("${1:i+1}")
|
|
||||||
break
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
;;
|
|
||||||
# If option is of type --foo=bar
|
|
||||||
--?*=*) options+=("${1%%=*}" "${1#*=}") ;;
|
|
||||||
# add --endopts for --
|
|
||||||
--) options+=(--endopts) ;;
|
|
||||||
# Otherwise, nothing special
|
|
||||||
*) options+=("$1") ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
set -- "${options[@]}"
|
|
||||||
unset options
|
|
||||||
|
|
||||||
# Print help if no arguments were passed.
|
|
||||||
# Uncomment to force arguments when invoking the script
|
|
||||||
# [[ $# -eq 0 ]] && set -- "--help"
|
|
||||||
|
|
||||||
# Read the options and set stuff
|
|
||||||
while [[ $1 = -?* ]]; do
|
|
||||||
case $1 in
|
|
||||||
-h|--help) usage >&2; safeExit ;;
|
|
||||||
--version) echo "$(basename $0) $version"; safeExit ;;
|
|
||||||
-p|--password) shift; echo "Enter Pass: "; stty -echo; read PASS; stty echo;
|
|
||||||
echo ;;
|
|
||||||
-v|--verbose) verbose=1 ;;
|
|
||||||
-l|--log) printLog=1 ;;
|
|
||||||
-c|--config) editConfig=1 ;;
|
|
||||||
-d|--debug) debug=1 ;;
|
|
||||||
-q|--quiet) quiet=1 ;;
|
|
||||||
-s|--strict) strict=1;;
|
|
||||||
-f|--force) force=1 ;;
|
|
||||||
-n|--dryrun) DRYRUN=n ;;
|
|
||||||
-m|--mounttest) mountTest=1 ;;
|
|
||||||
-z|--compress) COMPRESS=z ;;
|
|
||||||
--endopts) shift; break ;;
|
|
||||||
*) warning "invalid option: $1.\n"; usage >&2; safeExit ;;
|
|
||||||
# *) die "invalid option: '$1'." ;;
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
############## End Options and Usage ###################
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ############# ############# #############
|
|
||||||
# ## TIME TO RUN THE SCRIPT ##
|
|
||||||
# ## ##
|
|
||||||
# ## You shouldn't need to edit anything ##
|
|
||||||
# ## beneath this line ##
|
|
||||||
# ## ##
|
|
||||||
# ############# ############# #############
|
|
||||||
|
|
||||||
# Trap bad exits with your cleanup function
|
|
||||||
trap trapCleanup EXIT INT TERM
|
|
||||||
|
|
||||||
# Exit on error. Append ||true if you expect an error.
|
|
||||||
set -o errexit
|
|
||||||
|
|
||||||
# Exit on empty variable
|
|
||||||
if [ "${strict}" == "1" ]; then
|
|
||||||
set -o nounset
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run in debug mode, if set
|
|
||||||
if [ "${debug}" == "1" ]; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Bash will remember & return the highest exitcode in a chain of pipes.
|
|
||||||
# This way you can catch the error in case mysqldump fails in `mysqldump |gzip`
|
|
||||||
set -o pipefail
|
|
||||||
|
|
||||||
# Set timer for script to start
|
|
||||||
STARTTIME=$(date +"%s")
|
|
||||||
|
|
||||||
header "${scriptName} Begun"
|
|
||||||
|
|
||||||
newCopy
|
|
||||||
encryptConfig
|
|
||||||
createTempConfig
|
|
||||||
editConfiguration
|
|
||||||
sourceConfiguration
|
|
||||||
hostCheck
|
|
||||||
MethodCheck
|
|
||||||
moutDrives
|
|
||||||
testSources
|
|
||||||
runRsync
|
|
||||||
runUnison
|
|
||||||
unmountDrives
|
|
||||||
|
|
||||||
# Time the script by logging the end time
|
|
||||||
ENDTIME=$(date +"%s")
|
|
||||||
TOTALTIME=$(($ENDTIME-$STARTTIME))
|
|
||||||
|
|
||||||
notifyPushover
|
|
||||||
header "${scriptName} completed in $(convertsecs $TOTALTIME)"
|
|
||||||
|
|
||||||
safeExit # Exit cleanly
|
|
||||||
5
tmp/.gitignore
vendored
5
tmp/.gitignore
vendored
@@ -1,5 +0,0 @@
|
|||||||
*
|
|
||||||
|
|
||||||
!.gitignore
|
|
||||||
!README.md
|
|
||||||
!*.sample
|
|
||||||
159
utilities/alerts.bash
Normal file
159
utilities/alerts.bash
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
# Colors
|
||||||
|
if tput setaf 1 &>/dev/null; then
|
||||||
|
bold=$(tput bold)
|
||||||
|
white=$(tput setaf 7)
|
||||||
|
reset=$(tput sgr0)
|
||||||
|
purple=$(tput setaf 171)
|
||||||
|
red=$(tput setaf 1)
|
||||||
|
green=$(tput setaf 76)
|
||||||
|
tan=$(tput setaf 3)
|
||||||
|
yellow=$(tput setaf 3)
|
||||||
|
blue=$(tput setaf 38)
|
||||||
|
underline=$(tput sgr 0 1)
|
||||||
|
else
|
||||||
|
bold="\033[4;37m"
|
||||||
|
white="\033[0;37m"
|
||||||
|
reset="\033[0m"
|
||||||
|
purple="\033[0;35m"
|
||||||
|
red="\033[0;31m"
|
||||||
|
green="\033[1;32m"
|
||||||
|
tan="\033[0;33m"
|
||||||
|
yellow="\033[0;33m"
|
||||||
|
blue="\033[0;34m"
|
||||||
|
underline="\033[4;37m"
|
||||||
|
fi
|
||||||
|
|
||||||
|
_alert_() {
|
||||||
|
# DESC: Controls all printing of messages to log files and stdout.
|
||||||
|
# ARGS: $1 (required) - The type of alert to print
|
||||||
|
# (success, header, notice, dryrun, debug, warning, error,
|
||||||
|
# fatal, info, input)
|
||||||
|
# $2 (required) - The message to be printed to stdout and/or a log file
|
||||||
|
# $3 (optional) - Pass '$LINENO' to print the line number where the _alert_ was triggered
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: [ALERTTYPE] "[MESSAGE]" "$LINENO"
|
||||||
|
# NOTES: - Requires the variable LOGFILE to be set prior to
|
||||||
|
# calling this function.
|
||||||
|
# - The colors of each alert type are set in this function
|
||||||
|
# - For specified alert types, the funcstac will be printed
|
||||||
|
|
||||||
|
local scriptName logLocation logName function_name color
|
||||||
|
local alertType="${1}"
|
||||||
|
local message="${2}"
|
||||||
|
local line="${3-}"
|
||||||
|
|
||||||
|
[ -z "${LOGFILE-}" ] && fatal "\$LOGFILE must be set"
|
||||||
|
[ ! -d "$(dirname "${LOGFILE}")" ] && mkdir -p "$(dirname "${LOGFILE}")"
|
||||||
|
|
||||||
|
if [ -z "${line}" ]; then
|
||||||
|
[[ "$1" =~ ^(fatal|error|debug|warning) && "${FUNCNAME[2]}" != "_trapCleanup_" ]] \
|
||||||
|
&& message="${message} $(_functionStack_)"
|
||||||
|
else
|
||||||
|
[[ "$1" =~ ^(fatal|error|debug) && "${FUNCNAME[2]}" != "_trapCleanup_" ]] \
|
||||||
|
&& message="${message} (line: $line) $(_functionStack_)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "${line}" ]; then
|
||||||
|
[[ "$1" =~ ^(warning|info|notice|dryrun) && "${FUNCNAME[2]}" != "_trapCleanup_" ]] \
|
||||||
|
&& message="${message} (line: $line)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${alertType}" =~ ^(error|fatal) ]]; then
|
||||||
|
color="${bold}${red}"
|
||||||
|
elif [ "${alertType}" = "warning" ]; then
|
||||||
|
color="${red}"
|
||||||
|
elif [ "${alertType}" = "success" ]; then
|
||||||
|
color="${green}"
|
||||||
|
elif [ "${alertType}" = "debug" ]; then
|
||||||
|
color="${purple}"
|
||||||
|
elif [ "${alertType}" = "header" ]; then
|
||||||
|
color="${bold}${tan}"
|
||||||
|
elif [[ "${alertType}" =~ ^(input|notice) ]]; then
|
||||||
|
color="${bold}"
|
||||||
|
elif [ "${alertType}" = "dryrun" ]; then
|
||||||
|
color="${blue}"
|
||||||
|
else
|
||||||
|
color=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
_writeToScreen_() {
|
||||||
|
|
||||||
|
("${QUIET}") && return 0 # Print to console when script is not 'quiet'
|
||||||
|
[[ ${VERBOSE} == false && "${alertType}" =~ ^(debug|verbose) ]] && return 0
|
||||||
|
|
||||||
|
if ! [[ -t 1 ]]; then # Don't use colors on non-recognized terminals
|
||||||
|
color=""
|
||||||
|
reset=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "$(date +"%r") ${color}$(printf "[%7s]" "${alertType}") ${message}${reset}"
|
||||||
|
}
|
||||||
|
_writeToScreen_
|
||||||
|
|
||||||
|
_writeToLog_() {
|
||||||
|
[[ "${alertType}" == "input" ]] && return 0
|
||||||
|
[[ "${LOGLEVEL}" =~ (off|OFF|Off) ]] && return 0
|
||||||
|
[[ ! -f "${LOGFILE}" ]] && touch "${LOGFILE}"
|
||||||
|
|
||||||
|
# Don't use colors in logs
|
||||||
|
if command -v gsed &>/dev/null; then
|
||||||
|
local cleanmessage="$(echo "${message}" | gsed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')"
|
||||||
|
else
|
||||||
|
local cleanmessage="$(echo "${message}" | sed -E 's/(\x1b)?\[(([0-9]{1,2})(;[0-9]{1,3}){0,2})?[mGK]//g')"
|
||||||
|
fi
|
||||||
|
echo -e "$(date +"%b %d %R:%S") $(printf "[%7s]" "${alertType}") [$(/bin/hostname)] ${cleanmessage}" >>"${LOGFILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Write specified log level data to LOGFILE
|
||||||
|
case "${LOGLEVEL:-ERROR}" in
|
||||||
|
ALL|all|All)
|
||||||
|
_writeToLog_
|
||||||
|
;;
|
||||||
|
DEBUG|debug|Debug)
|
||||||
|
_writeToLog_
|
||||||
|
;;
|
||||||
|
INFO|info|Info)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal|warning|info|notice|success) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
WARN|warn|Warn)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal|warning) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
ERROR|error|Error)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
FATAL|fatal|Fatal)
|
||||||
|
if [[ "${alertType}" =~ ^(die|fatal) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
OFF|off)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
if [[ "${alertType}" =~ ^(die|error|fatal) ]]; then
|
||||||
|
_writeToLog_
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
} # /_alert_
|
||||||
|
|
||||||
|
error() { _alert_ error "${1}" "${2-}"; }
|
||||||
|
warning() { _alert_ warning "${1}" "${2-}"; }
|
||||||
|
notice() { _alert_ notice "${1}" "${2-}"; }
|
||||||
|
info() { _alert_ info "${1}" "${2-}"; }
|
||||||
|
success() { _alert_ success "${1}" "${2-}"; }
|
||||||
|
dryrun() { _alert_ dryrun "${1}" "${2-}"; }
|
||||||
|
input() { _alert_ input "${1}" "${2-}"; }
|
||||||
|
header() { _alert_ header "== ${1} ==" "${2-}"; }
|
||||||
|
die() { _alert_ fatal "${1}" "${2-}"; _safeExit_ "1" ; }
|
||||||
|
fatal() { _alert_ fatal "${1}" "${2-}"; _safeExit_ "1" ; }
|
||||||
|
debug() { _alert_ debug "${1}" "${2-}"; }
|
||||||
|
verbose() { _alert_ debug "${1}" "${2-}"; }
|
||||||
98
utilities/arrays.bash
Normal file
98
utilities/arrays.bash
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
_inArray_() {
|
||||||
|
# DESC: Determine if a value is in an array
|
||||||
|
# ARGS: $1 (Required) - Value to search for
|
||||||
|
# $2 (Required) - Array written as ${ARRAY[@]}
|
||||||
|
# OUTS: true/false
|
||||||
|
# USAGE: if _inArray_ "VALUE" "${ARRAY[@]}"; then ...
|
||||||
|
|
||||||
|
[[ $# -lt 2 ]] && fatal 'Missing required argument to _inArray_()!'
|
||||||
|
|
||||||
|
local value="$1"
|
||||||
|
shift
|
||||||
|
for arrayItem in "$@"; do
|
||||||
|
[[ "${arrayItem}" == "${value}" ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_join_() {
|
||||||
|
# DESC: Joins items together with a user specified separator
|
||||||
|
# ARGS: $1 (Required) - Separator
|
||||||
|
# $@ (Required) - Items to be joined
|
||||||
|
# OUTS: Prints joined terms
|
||||||
|
# USAGE:
|
||||||
|
# _join_ , a "b c" d #a,b c,d
|
||||||
|
# _join_ / var local tmp #var/local/tmp
|
||||||
|
# _join_ , "${foo[@]}" #a,b,c
|
||||||
|
# NOTE: http://stackoverflow.com/questions/1527049/bash-join-elements-of-an-array
|
||||||
|
|
||||||
|
[[ $# -lt 2 ]] && fatal 'Missing required argument to _join_()!'
|
||||||
|
|
||||||
|
local IFS="${1}"
|
||||||
|
shift
|
||||||
|
echo "${*}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_setdiff_() {
|
||||||
|
# DESC: Return items that exist in ARRAY1 that are do not exist in ARRAY2
|
||||||
|
# ARGS: $1 (Required) - Array 1 in format ${ARRAY[*]}
|
||||||
|
# $2 (Required) - Array 2 in format ${ARRAY[*]}
|
||||||
|
# OUTS: Prints unique terms
|
||||||
|
# USAGE: _setdiff_ "${array1[*]}" "${array2[*]}"
|
||||||
|
# NOTE: http://stackoverflow.com/a/1617303/142339
|
||||||
|
|
||||||
|
[[ $# -lt 2 ]] && fatal 'Missing required argument to _setdiff_()!'
|
||||||
|
|
||||||
|
local debug skip a b
|
||||||
|
if [[ "$1" == 1 ]]; then
|
||||||
|
debug=1
|
||||||
|
shift
|
||||||
|
fi
|
||||||
|
if [[ "$1" ]]; then
|
||||||
|
local setdiffA setdiffB setdiffC
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
setdiffA=($1)
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
setdiffB=($2)
|
||||||
|
fi
|
||||||
|
setdiffC=()
|
||||||
|
for a in "${setdiffA[@]}"; do
|
||||||
|
skip=
|
||||||
|
for b in "${setdiffB[@]}"; do
|
||||||
|
[[ "$a" == "$b" ]] && skip=1 && break
|
||||||
|
done
|
||||||
|
[[ "$skip" ]] || setdiffC=("${setdiffC[@]}" "$a")
|
||||||
|
done
|
||||||
|
[[ "$debug" ]] && for a in setdiffA setdiffB setdiffC; do
|
||||||
|
#shellcheck disable=SC1087
|
||||||
|
echo "$a ($(eval echo "\${#$a[*]}")) $(eval echo "\${$a[*]}")" 1>&2
|
||||||
|
done
|
||||||
|
[[ "$1" ]] && echo "${setdiffC[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_removeDupes_() {
|
||||||
|
# DESC: Removes duplicate array elements.
|
||||||
|
# ARGS: $1 (Required) - Input array
|
||||||
|
# OUTS: Prints de-duped elements to standard out
|
||||||
|
# USAGE: _removeDups_ "${array@]}"
|
||||||
|
# NOTE: List order may not stay the same.
|
||||||
|
# https://github.com/dylanaraps/pure-bash-bible
|
||||||
|
declare -A tmp_array
|
||||||
|
|
||||||
|
for i in "$@"; do
|
||||||
|
[[ $i ]] && IFS=" " tmp_array["${i:- }"]=1
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s\n' "${!tmp_array[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_randomArrayElement_() {
|
||||||
|
# DESC: Selects a random item from an array
|
||||||
|
# ARGS: $1 (Required) - Input array
|
||||||
|
# OUTS: Prints result
|
||||||
|
# USAGE: _randomArrayElement_ "${array[@]}"
|
||||||
|
# NOTE: https://github.com/dylanaraps/pure-bash-bible
|
||||||
|
# Usage: random_array_element "array"
|
||||||
|
local arr=("$@")
|
||||||
|
printf '%s\n' "${arr[RANDOM % $#]}"
|
||||||
|
}
|
||||||
277
utilities/baseHelpers.bash
Normal file
277
utilities/baseHelpers.bash
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
|
||||||
|
_execute_() {
|
||||||
|
# DESC: Executes commands with safety and logging options
|
||||||
|
# ARGS: $1 (Required) - The command to be executed. Quotation marks MUST be escaped.
|
||||||
|
# $2 (Optional) - String to display after command is executed
|
||||||
|
# OPTS: -v Always print debug output from the execute function
|
||||||
|
# -p Pass a failed command with 'return 0'. This effecively bypasses set -e.
|
||||||
|
# -e Bypass _alert_ functions and use 'echo RESULT'
|
||||||
|
# -s Use '_alert_ success' for successful output. (default is 'info')
|
||||||
|
# -q Do not print output (QUIET mode)
|
||||||
|
# OUTS: None
|
||||||
|
# USE : _execute_ "cp -R \"~/dir/somefile.txt\" \"someNewFile.txt\"" "Optional message"
|
||||||
|
# _execute_ -sv "mkdir \"some/dir\""
|
||||||
|
# NOTE:
|
||||||
|
# If $DRYRUN=true no commands are executed
|
||||||
|
# If $VERBOSE=true the command's native output is printed to
|
||||||
|
# stderr and stdin. This can be forced with `_execute_ -v`
|
||||||
|
|
||||||
|
local localVerbose=false
|
||||||
|
local passFailures=false
|
||||||
|
local echoResult=false
|
||||||
|
local successResult=false
|
||||||
|
local quietResult=false
|
||||||
|
local opt
|
||||||
|
|
||||||
|
local OPTIND=1
|
||||||
|
while getopts ":vVpPeEsSqQ" opt; do
|
||||||
|
case $opt in
|
||||||
|
v | V) localVerbose=true ;;
|
||||||
|
p | P) passFailures=true ;;
|
||||||
|
e | E) echoResult=true ;;
|
||||||
|
s | S) successResult=true ;;
|
||||||
|
q | Q) quietResult=true ;;
|
||||||
|
*)
|
||||||
|
{
|
||||||
|
error "Unrecognized option '$1' passed to _execute_. Exiting."
|
||||||
|
_safeExit_
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
|
local cmd="${1:?_execute_ needs a command}"
|
||||||
|
local message="${2:-$1}"
|
||||||
|
|
||||||
|
local saveVerbose=$VERBOSE
|
||||||
|
if "${localVerbose}"; then
|
||||||
|
VERBOSE=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if "${DRYRUN}"; then
|
||||||
|
if "$quietResult"; then
|
||||||
|
VERBOSE=$saveVerbose
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [ -n "${2-}" ]; then
|
||||||
|
dryrun "${1} (${2})" "$(caller)"
|
||||||
|
else
|
||||||
|
dryrun "${1}" "$(caller)"
|
||||||
|
fi
|
||||||
|
elif ${VERBOSE}; then
|
||||||
|
if eval "${cmd}"; then
|
||||||
|
if "$echoResult"; then
|
||||||
|
echo "${message}"
|
||||||
|
elif "${successResult}"; then
|
||||||
|
success "${message}"
|
||||||
|
else
|
||||||
|
info "${message}"
|
||||||
|
fi
|
||||||
|
VERBOSE=$saveVerbose
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
if "$echoResult"; then
|
||||||
|
echo "warning: ${message}"
|
||||||
|
else
|
||||||
|
warning "${message}"
|
||||||
|
fi
|
||||||
|
VERBOSE=$saveVerbose
|
||||||
|
"${passFailures}" && return 0 || return 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if eval "${cmd}" &>/dev/null; then
|
||||||
|
if "$quietResult"; then
|
||||||
|
VERBOSE=$saveVerbose
|
||||||
|
return 0
|
||||||
|
elif "$echoResult"; then
|
||||||
|
echo "${message}"
|
||||||
|
elif "${successResult}"; then
|
||||||
|
success "${message}"
|
||||||
|
else
|
||||||
|
info "${message}"
|
||||||
|
fi
|
||||||
|
VERBOSE=$saveVerbose
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
if "$echoResult"; then
|
||||||
|
echo "error: ${message}"
|
||||||
|
else
|
||||||
|
warning "${message}"
|
||||||
|
fi
|
||||||
|
VERBOSE=$saveVerbose
|
||||||
|
"${passFailures}" && return 0 || return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_findBaseDir_() {
|
||||||
|
# DESC: Locates the real directory of the script being run. Similar to GNU readlink -n
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: Echo result to STDOUT
|
||||||
|
# USE : baseDir="$(_findBaseDir_)"
|
||||||
|
# cp "$(_findBaseDir_ "somefile.txt")" "other_file.txt"
|
||||||
|
|
||||||
|
local SOURCE
|
||||||
|
local DIR
|
||||||
|
|
||||||
|
# Is file sourced?
|
||||||
|
[[ $_ != "$0" ]] \
|
||||||
|
&& SOURCE="${BASH_SOURCE[1]}" \
|
||||||
|
|| SOURCE="${BASH_SOURCE[0]}"
|
||||||
|
|
||||||
|
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
|
||||||
|
DIR="$(cd -P "$(dirname "$SOURCE")" && pwd)"
|
||||||
|
SOURCE="$(readlink "$SOURCE")"
|
||||||
|
[[ $SOURCE != /* ]] && SOURCE="${DIR}/${SOURCE}" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
|
||||||
|
done
|
||||||
|
echo "$(cd -P "$(dirname "${SOURCE}")" && pwd)"
|
||||||
|
}
|
||||||
|
|
||||||
|
_checkBinary_() {
|
||||||
|
# DESC: Check if a binary exists in the search PATH
|
||||||
|
# ARGS: $1 (Required) - Name of the binary to check for existence
|
||||||
|
# OUTS: true/false
|
||||||
|
# USAGE: (_checkBinary_ ffmpeg ) && [SUCCESS] || [FAILURE]
|
||||||
|
if [[ $# -lt 1 ]]; then
|
||||||
|
error 'Missing required argument to _checkBinary_()!'
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v "$1" >/dev/null 2>&1; then
|
||||||
|
debug "Did not find dependency: '$1'"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_haveFunction_() {
|
||||||
|
# DESC: Tests if a function exists.
|
||||||
|
# ARGS: $1 (Required) - Function name
|
||||||
|
# OUTS: true/false
|
||||||
|
local f
|
||||||
|
f="$1"
|
||||||
|
|
||||||
|
if declare -f "$f" &>/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_pauseScript_() {
|
||||||
|
# DESC: Pause a script at any point and continue after user input
|
||||||
|
# ARGS: $1 (Optional) - String for customized message
|
||||||
|
# OUTS: None
|
||||||
|
|
||||||
|
local pauseMessage
|
||||||
|
pauseMessage="${1:-Paused}. Ready to continue?"
|
||||||
|
|
||||||
|
if _seekConfirmation_ "${pauseMessage}"; then
|
||||||
|
info "Continuing..."
|
||||||
|
else
|
||||||
|
notice "Exiting Script"
|
||||||
|
_safeExit_
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_progressBar_() {
|
||||||
|
# DESC: Prints a progress bar within a for/while loop
|
||||||
|
# ARGS: $1 (Required) - The total number of items counted
|
||||||
|
# $2 (Optional) - The optional title of the progress bar
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE:
|
||||||
|
# for number in $(seq 0 100); do
|
||||||
|
# sleep 1
|
||||||
|
# _progressBar_ "100" "Counting numbers"
|
||||||
|
# done
|
||||||
|
|
||||||
|
($QUIET) && return
|
||||||
|
($VERBOSE) && return
|
||||||
|
[ ! -t 1 ] && return # Do nothing if the output is not a terminal
|
||||||
|
[ $1 == 1 ] && return # Do nothing with a single element
|
||||||
|
|
||||||
|
local width bar_char perc num bar progressBarLine barTitle n
|
||||||
|
|
||||||
|
n="${1:?_progressBar_ needs input}"
|
||||||
|
((n = n - 1))
|
||||||
|
barTitle="${2:-Running Process}"
|
||||||
|
width=30
|
||||||
|
bar_char="#"
|
||||||
|
|
||||||
|
# Reset the count
|
||||||
|
[ -z "${progressBarProgress}" ] && progressBarProgress=0
|
||||||
|
tput civis # Hide the cursor
|
||||||
|
trap 'tput cnorm; exit 1' SIGINT
|
||||||
|
|
||||||
|
if [ ! "${progressBarProgress}" -eq $n ]; then
|
||||||
|
#echo "progressBarProgress: $progressBarProgress"
|
||||||
|
# Compute the percentage.
|
||||||
|
perc=$((progressBarProgress * 100 / $1))
|
||||||
|
# Compute the number of blocks to represent the percentage.
|
||||||
|
num=$((progressBarProgress * width / $1))
|
||||||
|
# Create the progress bar string.
|
||||||
|
bar=""
|
||||||
|
if [ ${num} -gt 0 ]; then
|
||||||
|
bar=$(printf "%0.s${bar_char}" $(seq 1 ${num}))
|
||||||
|
fi
|
||||||
|
# Print the progress bar.
|
||||||
|
progressBarLine=$(printf "%s [%-${width}s] (%d%%)" " ${barTitle}" "${bar}" "${perc}")
|
||||||
|
echo -ne "${progressBarLine}\r"
|
||||||
|
progressBarProgress=$((progressBarProgress + 1))
|
||||||
|
else
|
||||||
|
# Clear the progress bar when complete
|
||||||
|
# echo -ne "\033[0K\r"
|
||||||
|
tput el # Clear the line
|
||||||
|
|
||||||
|
unset progressBarProgress
|
||||||
|
fi
|
||||||
|
|
||||||
|
tput cnorm
|
||||||
|
}
|
||||||
|
|
||||||
|
_seekConfirmation_() {
|
||||||
|
# DESC: Seek user input for yes/no question
|
||||||
|
# ARGS: $1 (Optional) - Question being asked
|
||||||
|
# OUTS: true/false
|
||||||
|
# USAGE: _seekConfirmation_ "Do something?" && echo "okay" || echo "not okay"
|
||||||
|
# OR
|
||||||
|
# if _seekConfirmation_ "Answer this question"; then
|
||||||
|
# something
|
||||||
|
# fi
|
||||||
|
|
||||||
|
input "${1-}"
|
||||||
|
if "${FORCE}"; then
|
||||||
|
debug "Forcing confirmation with '--force' flag set"
|
||||||
|
echo -e ""
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
while true; do
|
||||||
|
read -r -p " (y/n) " yn
|
||||||
|
case $yn in
|
||||||
|
[Yy]*) return 0 ;;
|
||||||
|
[Nn]*) return 1 ;;
|
||||||
|
*) input "Please answer yes or no." ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_setPATH_() {
|
||||||
|
# DESC: Add directories to $PATH so script can find executables
|
||||||
|
# ARGS: $@ - One or more paths
|
||||||
|
# OUTS: $PATH
|
||||||
|
# USAGE: _setPATH_ "/usr/local/bin" "${HOME}/bin" "$(npm bin)"
|
||||||
|
local NEWPATH NEWPATHS USERPATH
|
||||||
|
|
||||||
|
for USERPATH in "$@"; do
|
||||||
|
NEWPATHS+=("$USERPATH")
|
||||||
|
done
|
||||||
|
|
||||||
|
for NEWPATH in "${NEWPATHS[@]}"; do
|
||||||
|
if ! echo "$PATH" | grep -Eq "(^|:)${NEWPATH}($|:)"; then
|
||||||
|
PATH="${NEWPATH}:${PATH}"
|
||||||
|
debug "Added '${tan}${NEWPATH}${purple}' to PATH"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
34
utilities/csv.bash
Normal file
34
utilities/csv.bash
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
_makeCSV_() {
|
||||||
|
# Creates a new CSV file if one does not already exist
|
||||||
|
# Takes passed arguments and writes them as a header line to the CSV
|
||||||
|
# Usage '_makeCSV_ column1 column2 column3'
|
||||||
|
|
||||||
|
# Set the location and name of the CSV File
|
||||||
|
if [ -z "${csvLocation}" ]; then
|
||||||
|
csvLocation="${HOME}/Desktop"
|
||||||
|
fi
|
||||||
|
if [ -z "${csvName}" ]; then
|
||||||
|
csvName="$(LC_ALL=C date +%Y-%m-%d)-${FUNCNAME[1]}.csv"
|
||||||
|
fi
|
||||||
|
csvFile="${csvLocation}/${csvName}"
|
||||||
|
|
||||||
|
# Overwrite existing file? If not overwritten, new content is added
|
||||||
|
# to the bottom of the existing file
|
||||||
|
if [ -f "${csvFile}" ]; then
|
||||||
|
if _seekConfirmation_ "${csvFile} already exists. Overwrite?"; then
|
||||||
|
rm "${csvFile}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_writeCSV_ "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
_writeCSV_() {
|
||||||
|
# Takes passed arguments and writes them as a comma separated line
|
||||||
|
# Usage '_writeCSV_ column1 column2 column3'
|
||||||
|
|
||||||
|
local csvInput=("$@")
|
||||||
|
saveIFS=$IFS
|
||||||
|
IFS=','
|
||||||
|
echo "${csvInput[*]}" >>"${csvFile}"
|
||||||
|
IFS=${saveIFS}
|
||||||
|
}
|
||||||
381
utilities/dates.bash
Normal file
381
utilities/dates.bash
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
_monthToNumber_() {
|
||||||
|
# DESC: Convert a month name to a number
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: Prints the number of the month to stdout
|
||||||
|
# USAGE: _monthToNumber_ "January"
|
||||||
|
|
||||||
|
local mon="$(echo "$1" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
case "$mon" in
|
||||||
|
january|jan|ja) echo 1 ;;
|
||||||
|
february|feb|fe) echo 2 ;;
|
||||||
|
march|mar|ma) echo 3 ;;
|
||||||
|
april|apr|ap) echo 4 ;;
|
||||||
|
may) echo 5 ;;
|
||||||
|
june|jun|ju) echo 6 ;;
|
||||||
|
july|jul) echo 7 ;;
|
||||||
|
august|aug|au) echo 8 ;;
|
||||||
|
september|sep|se) echo 9 ;;
|
||||||
|
october|oct) echo 10 ;;
|
||||||
|
november|nov|no) echo 11 ;;
|
||||||
|
december|dec|de) echo 12 ;;
|
||||||
|
*)
|
||||||
|
warning "month_monthToNumber_: Bad monthname: $1"
|
||||||
|
return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
_numberToMonth_() {
|
||||||
|
# DESC: Convert a month number to its name
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: Prints the name of the month to stdout
|
||||||
|
# USAGE: _numberToMonth_ 1
|
||||||
|
|
||||||
|
local mon="$1"
|
||||||
|
case "$mon" in
|
||||||
|
1|01) echo January ;;
|
||||||
|
2|02) echo February ;;
|
||||||
|
3|03) echo March ;;
|
||||||
|
4|04) echo April ;;
|
||||||
|
5|05) echo May ;;
|
||||||
|
6|06) echo June ;;
|
||||||
|
7|07) echo July ;;
|
||||||
|
8|08) echo August ;;
|
||||||
|
9|09) echo September ;;
|
||||||
|
10) echo October ;;
|
||||||
|
11) echo November ;;
|
||||||
|
12) echo December ;;
|
||||||
|
*)
|
||||||
|
warning "_numberToMonth_: Bad month number: $1"
|
||||||
|
return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
_parseDate_() {
|
||||||
|
# DESC: Takes a string as input and attempts to find a date within it
|
||||||
|
# to parse into component parts (day, month, year)
|
||||||
|
# ARGS: $1 (required) - A string
|
||||||
|
# OUTS: Returns error if no date found
|
||||||
|
# $_parseDate_found - The date found in the string
|
||||||
|
# $_parseDate_year - The year
|
||||||
|
# $_parseDate_month - The number month
|
||||||
|
# $_parseDate_monthName - The name of the month
|
||||||
|
# $_parseDate_day - The day
|
||||||
|
# $_parseDate_hour - The hour (if avail)
|
||||||
|
# $_parseDate_minute - The minute (if avail)
|
||||||
|
# USAGE: if _parseDate_ "[STRING]"; then ...
|
||||||
|
# NOTE: This function only recognizes dates from the year 2000 to 2029
|
||||||
|
# NOTE: Will recognize dates in the following formats separated by '-_ ./'
|
||||||
|
# * YYYY-MM-DD * Month DD, YYYY * DD Month, YYYY
|
||||||
|
# * Month, YYYY * Month, DD YY * MM-DD-YYYY
|
||||||
|
# * MMDDYYYY * YYYYMMDD * DDMMYYYY
|
||||||
|
# * YYYYMMDDHHMM * YYYYMMDDHH * DD-MM-YYYY
|
||||||
|
# * DD MM YY * MM DD YY
|
||||||
|
# TODO: Impelemt the following date formats
|
||||||
|
# * MMDDYY * YYMMDD * mon-DD-YY
|
||||||
|
|
||||||
|
[[ $# -eq 0 ]] && {
|
||||||
|
error 'Missing required argument to _parseDate_()!'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local date="${1:-$(date +%F)}"
|
||||||
|
_parseDate_found="" _parseDate_year="" _parseDate_month="" _parseDate_monthName=""
|
||||||
|
_parseDate_day="" _parseDate_hour="" _parseDate_minute=""
|
||||||
|
|
||||||
|
shopt -s nocasematch #Use case-insensitive regex
|
||||||
|
|
||||||
|
debug "_parseDate_() input ${tan}$date${purple}"
|
||||||
|
|
||||||
|
# YYYY MM DD or YYYY-MM-DD
|
||||||
|
pat="(.*[^0-9]|^)((20[0-2][0-9])[-\.\/_ ]+([ 0-9]{1,2})[-\.\/_ ]+([ 0-9]{1,2}))([^0-9].*|$)"
|
||||||
|
if [[ "${date}" =~ $pat ]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_year=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
_parseDate_month=$(( 10#${BASH_REMATCH[4]} ))
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[5]} ))
|
||||||
|
debug "regex match: ${tan}YYYY-MM-DD${purple}"
|
||||||
|
|
||||||
|
# Month DD, YYYY
|
||||||
|
elif [[ "${date}" =~ ((january|jan|ja|february|feb|fe|march|mar|ma|april|apr|ap|may|june|jun|july|jul|ju|august|aug|september|sep|october|oct|november|nov|december|dec)[-\./_ ]+([0-9]{1,2})(nd|rd|th|st)?,?[-\./_ ]+(20[0-2][0-9]))([^0-9].*|$) ]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[1]:-}"
|
||||||
|
_parseDate_month=$(_monthToNumber_ ${BASH_REMATCH[2]:-})
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month:-}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[3]:-} ))
|
||||||
|
_parseDate_year=$(( 10#${BASH_REMATCH[5]:-} ))
|
||||||
|
debug "regex match: ${tan}Month DD, YYYY${purple}"
|
||||||
|
|
||||||
|
# Month DD, YY
|
||||||
|
elif [[ "${date}" =~ ((january|jan|ja|february|feb|fe|march|mar|ma|april|apr|ap|may|june|jun|july|jul|ju|august|aug|september|sep|october|oct|november|nov|december|dec)[-\./_ ]+([0-9]{1,2})(nd|rd|th|st)?,?[-\./_ ]+([0-9]{2}))([^0-9].*|$) ]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[1]}"
|
||||||
|
_parseDate_month=$(_monthToNumber_ ${BASH_REMATCH[2]})
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
_parseDate_year="20$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
debug "regex match: ${tan}Month DD, YY${purple}"
|
||||||
|
|
||||||
|
# DD Month YYYY
|
||||||
|
elif [[ "${date}" =~ (.*[^0-9]|^)(([0-9]{2})[-\./_ ]+(january|jan|ja|february|feb|fe|march|mar|ma|april|apr|ap|may|june|jun|july|jul|ju|august|aug|september|sep|october|oct|november|nov|december|dec),?[-\./_ ]+(20[0-2][0-9]))([^0-9].*|$) ]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day=$(( 10#"${BASH_REMATCH[3]}" ))
|
||||||
|
_parseDate_month="$(_monthToNumber_ "${BASH_REMATCH[4]}")"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year=$(( 10#"${BASH_REMATCH[5]}" ))
|
||||||
|
debug "regex match: ${tan}DD Month, YYYY${purple}"
|
||||||
|
|
||||||
|
# MM-DD-YYYY or DD-MM-YYYY
|
||||||
|
elif [[ "${date}" =~ (.*[^0-9]|^)(([ 0-9]{1,2})[-\.\/_ ]+([ 0-9]{1,2})[-\.\/_ ]+(20[0-2][0-9]))([^0-9].*|$) ]]; then
|
||||||
|
|
||||||
|
if [[ $(( 10#${BASH_REMATCH[3]} )) -lt 13 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -gt 12 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 32
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_year=$(( 10#${BASH_REMATCH[5]} ))
|
||||||
|
_parseDate_month=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[4]} ))
|
||||||
|
debug "regex match: ${tan}MM-DD-YYYY${purple}"
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[3]} )) -gt 12 && \
|
||||||
|
$(( 10#${BASH_REMATCH[3]} )) -lt 32 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 13
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_year=$(( 10#${BASH_REMATCH[5]} ))
|
||||||
|
_parseDate_month=$(( 10#${BASH_REMATCH[4]} ))
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
debug "regex match: ${tan}DD-MM-YYYY${purple}"
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[3]} )) -lt 32 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 13
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_year=$(( 10#${BASH_REMATCH[5]} ))
|
||||||
|
_parseDate_month=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[4]} ))
|
||||||
|
debug "regex match: ${tan}MM-DD-YYYY${purple}"
|
||||||
|
else
|
||||||
|
shopt -u nocasematch
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
elif [[ "${date}" =~ (.*[^0-9]|^)(([0-9]{1,2})[-\.\/_ ]+([0-9]{1,2})[-\.\/_ ]+([0-9]{2}))([^0-9].*|$) ]]; then
|
||||||
|
|
||||||
|
if [[ $(( 10#${BASH_REMATCH[3]} )) -lt 13 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -gt 12 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 32
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_year="20$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_month=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[4]} ))
|
||||||
|
debug "regex match: ${tan}MM-DD-YYYY${purple}"
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[3]} )) -gt 12 && \
|
||||||
|
$(( 10#${BASH_REMATCH[3]} )) -lt 32 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 13
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_year="20$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_month=$(( 10#${BASH_REMATCH[4]} ))
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
debug "regex match: ${tan}DD-MM-YYYY${purple}"
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[3]} )) -lt 32 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 13
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_year="20$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_month=$(( 10#${BASH_REMATCH[3]} ))
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_day=$(( 10#${BASH_REMATCH[4]} ))
|
||||||
|
debug "regex match: ${tan}MM-DD-YYYY${purple}"
|
||||||
|
else
|
||||||
|
shopt -u nocasematch
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Month, YYYY
|
||||||
|
elif [[ "${date}" =~ ((january|jan|ja|february|feb|fe|march|mar|ma|april|apr|ap|may|june|jun|july|jul|ju|august|aug|september|sep|october|oct|november|nov|december|dec),?[-\./_ ]+(20[0-2][0-9]))([^0-9].*|$) ]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[1]}"
|
||||||
|
_parseDate_day="1"
|
||||||
|
_parseDate_month="$(_monthToNumber_ "${BASH_REMATCH[2]}")"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ $_parseDate_month)"
|
||||||
|
_parseDate_year="$(( 10#${BASH_REMATCH[3]} ))"
|
||||||
|
debug "regex match: ${tan}Month, YYYY${purple}"
|
||||||
|
|
||||||
|
# YYYYMMDDHHMM
|
||||||
|
elif [[ "${date}" =~ (.*[^0-9]|^)((20[0-2][0-9])([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2}))([^0-9].*|$) ]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day="$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_month="$(( 10#${BASH_REMATCH[4]} ))"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year="$(( 10#${BASH_REMATCH[3]} ))"
|
||||||
|
_parseDate_hour="$(( 10#${BASH_REMATCH[6]} ))"
|
||||||
|
_parseDate_minute="$(( 10#${BASH_REMATCH[7]} ))"
|
||||||
|
debug "regex match: ${tan}YYYYMMDDHHMM${purple}"
|
||||||
|
|
||||||
|
# YYYYMMDDHH 1 2 3 4 5 6
|
||||||
|
elif [[ "${date}" =~ (.*[^0-9]|^)((20[0-2][0-9])([0-9]{2})([0-9]{2})([0-9]{2}))([^0-9].*|$) ]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day="$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_month="$(( 10#${BASH_REMATCH[4]} ))"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year="$(( 10#${BASH_REMATCH[3]} ))"
|
||||||
|
_parseDate_hour="${BASH_REMATCH[6]}"
|
||||||
|
_parseDate_minute="00"
|
||||||
|
debug "regex match: ${tan}YYYYMMDDHHMM${purple}"
|
||||||
|
|
||||||
|
# MMDDYYYY or YYYYMMDD or DDMMYYYY
|
||||||
|
# 1 2 3 4 5 6
|
||||||
|
elif [[ "${date}" =~ (.*[^0-9]|^)(([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2}))([^0-9].*|$) ]]; then
|
||||||
|
|
||||||
|
# MMDDYYYY
|
||||||
|
if [[ $(( 10#${BASH_REMATCH[5]} )) -eq 20 && \
|
||||||
|
$(( 10#${BASH_REMATCH[3]} )) -lt 13 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 32
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day="$(( 10#${BASH_REMATCH[4]} ))"
|
||||||
|
_parseDate_month="$(( 10#${BASH_REMATCH[3]} ))"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year="${BASH_REMATCH[5]}${BASH_REMATCH[6]}"
|
||||||
|
debug "regex match: ${tan}MMDDYYYY${purple}"
|
||||||
|
# DDMMYYYY
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[5]} )) -eq 20 && \
|
||||||
|
$(( 10#${BASH_REMATCH[3]} )) -gt 12 && \
|
||||||
|
$(( 10#${BASH_REMATCH[3]} )) -lt 32 && \
|
||||||
|
$(( 10#${BASH_REMATCH[4]} )) -lt 13
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day="$(( 10#${BASH_REMATCH[3]} ))"
|
||||||
|
_parseDate_month="$(( 10#${BASH_REMATCH[4]} ))"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year="${BASH_REMATCH[5]}${BASH_REMATCH[6]}"
|
||||||
|
debug "regex match: ${tan}DDMMYYYY${purple}"
|
||||||
|
# YYYYMMDD
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[3]} )) -eq 20 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[6]} )) -gt 12 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[6]} )) -lt 32 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[5]} )) -lt 13 \
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day="$(( 10#${BASH_REMATCH[6]} ))"
|
||||||
|
_parseDate_month="$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year="${BASH_REMATCH[3]}${BASH_REMATCH[4]}"
|
||||||
|
debug "regex match: ${tan}YYYYMMDD${purple}"
|
||||||
|
# YYYYDDMM
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[3]} )) -eq 20 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[5]} )) -gt 12 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[5]} )) -lt 32 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[6]} )) -lt 13 \
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day="$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_month="$(( 10#${BASH_REMATCH[6]} ))"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year="${BASH_REMATCH[3]}${BASH_REMATCH[4]}"
|
||||||
|
debug "regex match: ${tan}YYYYMMDD${purple}"
|
||||||
|
# Assume YYYMMDD
|
||||||
|
elif [[ $(( 10#${BASH_REMATCH[3]} )) -eq 20 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[6]} )) -lt 32 \
|
||||||
|
&& $(( 10#${BASH_REMATCH[5]} )) -lt 13 \
|
||||||
|
]]; then
|
||||||
|
_parseDate_found="${BASH_REMATCH[2]}"
|
||||||
|
_parseDate_day="$(( 10#${BASH_REMATCH[6]} ))"
|
||||||
|
_parseDate_month="$(( 10#${BASH_REMATCH[5]} ))"
|
||||||
|
_parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
_parseDate_year="${BASH_REMATCH[3]}${BASH_REMATCH[4]}"
|
||||||
|
debug "regex match: ${tan}YYYYMMDD${purple}"
|
||||||
|
else
|
||||||
|
shopt -u nocasematch
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# # MMDD or DDYY
|
||||||
|
# elif [[ "$date" =~ .*(([0-9]{2})([0-9]{2})).* ]]; then
|
||||||
|
# debug "regex match: ${tan}MMDD or DDMM${purple}"
|
||||||
|
# _parseDate_found="${BASH_REMATCH[1]}"
|
||||||
|
|
||||||
|
# # Figure out if days are months or vice versa
|
||||||
|
# if [[ $(( 10#${BASH_REMATCH[2]} )) -gt 12 \
|
||||||
|
# && $(( 10#${BASH_REMATCH[2]} )) -lt 32 \
|
||||||
|
# && $(( 10#${BASH_REMATCH[3]} )) -lt 13 \
|
||||||
|
# ]]; then
|
||||||
|
# _parseDate_day="$(( 10#${BASH_REMATCH[2]} ))"
|
||||||
|
# _parseDate_month="$(( 10#${BASH_REMATCH[3]} ))"
|
||||||
|
# _parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
# _parseDate_year="$(date +%Y )"
|
||||||
|
# elif [[ $(( 10#${BASH_REMATCH[2]} )) -lt 13 \
|
||||||
|
# && $(( 10#${BASH_REMATCH[3]} )) -lt 32 \
|
||||||
|
# ]]; then
|
||||||
|
# _parseDate_day="$(( 10#${BASH_REMATCH[3]} ))"
|
||||||
|
# _parseDate_month="$(( 10#${BASH_REMATCH[2]} ))"
|
||||||
|
# _parseDate_monthName="$(_numberToMonth_ "${_parseDate_month}")"
|
||||||
|
# _parseDate_year="$(date +%Y )"
|
||||||
|
# else
|
||||||
|
# shopt -u nocasematch
|
||||||
|
# return 1
|
||||||
|
# fi
|
||||||
|
else
|
||||||
|
shopt -u nocasematch
|
||||||
|
return 1
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -z ${_parseDate_year:-} ]] && { shopt -u nocasematch; return 1 ; }
|
||||||
|
(( _parseDate_month >= 1 && _parseDate_month <= 12 )) || { shopt -u nocasematch; return 1 ; }
|
||||||
|
(( _parseDate_day >= 1 && _parseDate_day <= 31 )) || { shopt -u nocasematch; return 1 ; }
|
||||||
|
|
||||||
|
debug "${tan}\$_parseDate_found: ${_parseDate_found}${purple}"
|
||||||
|
debug "${tan}\$_parseDate_year: ${_parseDate_year}${purple}"
|
||||||
|
debug "${tan}\$_parseDate_month: ${_parseDate_month}${purple}"
|
||||||
|
debug "${tan}\$_parseDate_monthName: ${_parseDate_monthName}${purple}"
|
||||||
|
debug "${tan}\$_parseDate_day: ${_parseDate_day}${purple}"
|
||||||
|
[[ -z ${_parseDate_hour:-} ]] || debug "${tan}\$_parseDate_hour: ${_parseDate_hour}${purple}"
|
||||||
|
[[ -z ${_parseDate_inute:-} ]] || debug "${tan}\$_parseDate_minute: ${_parseDate_minute}${purple}"
|
||||||
|
|
||||||
|
shopt -u nocasematch
|
||||||
|
|
||||||
|
# Output results for BATS tests
|
||||||
|
if [ "${automated_test_in_progress:-}" ]; then
|
||||||
|
echo "_parseDate_found: ${_parseDate_found}"
|
||||||
|
echo "_parseDate_year: ${_parseDate_year}"
|
||||||
|
echo "_parseDate_month: ${_parseDate_month}"
|
||||||
|
echo "_parseDate_monthName: ${_parseDate_monthName}"
|
||||||
|
echo "_parseDate_day: ${_parseDate_day}"
|
||||||
|
echo "_parseDate_hour: ${_parseDate_hour}"
|
||||||
|
echo "_parseDate_minute: ${_parseDate_minute}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_formatDate_() {
|
||||||
|
# DESC: Reformats dates into user specified formats
|
||||||
|
# ARGS: $1 (Required) - Date to be formatted
|
||||||
|
# $2 (Optional) - Format in any format accepted by bash's date command. Examples listed below.
|
||||||
|
# %F - YYYY-MM-DD
|
||||||
|
# %D - MM/DD/YY
|
||||||
|
# %a - Name of weekday in short (like Sun, Mon, Tue, Wed, Thu, Fri, Sat)
|
||||||
|
# %A - Name of weekday in full (like Sunday, Monday, Tuesday)
|
||||||
|
# '+%m %d, %Y' - 12 27, 2019
|
||||||
|
# OUTS: Echo result to STDOUT
|
||||||
|
# USAGE: _formatDate_ "Jan 10, 2019" "%D"
|
||||||
|
# NOTE: Defaults to YYYY-MM-DD or $(date +%F)
|
||||||
|
|
||||||
|
[[ $# -eq 0 ]] && {
|
||||||
|
error 'Missing required argument to _formatDate_()'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local d="${1}"
|
||||||
|
local format="${2:-%F}"
|
||||||
|
format="${format//+/}"
|
||||||
|
|
||||||
|
if command -v gdate >/dev/null 2>&1; then
|
||||||
|
gdate -d "${d}" "+${format}"
|
||||||
|
else
|
||||||
|
date -d "${d}" "+${format}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
651
utilities/files.bash
Normal file
651
utilities/files.bash
Normal file
@@ -0,0 +1,651 @@
|
|||||||
|
|
||||||
|
_listFiles_() {
|
||||||
|
# DESC: Find files in a directory. Use either glob or regex
|
||||||
|
# ARGS: $1 (Required) - 'g|glob' or 'r|regex'
|
||||||
|
# $2 (Required) - pattern to match
|
||||||
|
# $3 (Optional) - directory
|
||||||
|
# OUTS: Prints files to STDOUT
|
||||||
|
# NOTE: Searches are NOT case sensitive and MUST be quoted
|
||||||
|
# USAGE: _listFiles_ glob "*.txt" "some/backup/dir"
|
||||||
|
# _listFiles_ regex ".*\.txt" "some/backup/dir"
|
||||||
|
# readarry -t array < <(_listFiles_ g "*.txt")
|
||||||
|
|
||||||
|
[[ $# -lt 2 ]] && {
|
||||||
|
error 'Missing required argument to _listFiles_()!'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local t="${1}"
|
||||||
|
local p="${2}"
|
||||||
|
local d="${3:-.}"
|
||||||
|
local fileMatch e
|
||||||
|
|
||||||
|
case "$t" in
|
||||||
|
glob | Glob | g | G)
|
||||||
|
while read -r fileMatch; do
|
||||||
|
echo "${e}"
|
||||||
|
done < <(find "${d}" -iname "${p}" -type f -maxdepth 1 | sort)
|
||||||
|
;;
|
||||||
|
regex | Regex | r | R)
|
||||||
|
while read -r fileMatch; do
|
||||||
|
e="$(realpath "${fileMatch}")"
|
||||||
|
echo "${e}"
|
||||||
|
done < <(find "${d}" -iregex "${p}" -type f -maxdepth 1 | sort)
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Could not determine if search was glob or regex"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_backupFile_() {
|
||||||
|
# DESC: Creates a backup of a specified file with .bak extension or
|
||||||
|
# optionally to a specified directory
|
||||||
|
# ARGS: $1 (Required) - Source file
|
||||||
|
# $2 (Optional) - Destination dir name used only with -d flag (defaults to ./backup)
|
||||||
|
# OPTS: -d - Move files to a backup direcory
|
||||||
|
# -m - Replaces copy (default) with move, effectively removing the
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: _backupFile_ "sourcefile.txt" "some/backup/dir"
|
||||||
|
# NOTE: dotfiles have their leading '.' removed in their backup
|
||||||
|
|
||||||
|
local opt
|
||||||
|
local OPTIND=1
|
||||||
|
local useDirectory=false
|
||||||
|
local moveFile=false
|
||||||
|
|
||||||
|
while getopts ":dDmM" opt; do
|
||||||
|
case ${opt} in
|
||||||
|
d | D) useDirectory=true ;;
|
||||||
|
m | M) moveFile=true ;;
|
||||||
|
*)
|
||||||
|
{
|
||||||
|
error "Unrecognized option '$1' passed to _makeSymlink_" "${LINENO}"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _backupFile_()!'
|
||||||
|
|
||||||
|
local s="${1}"
|
||||||
|
local d="${2:-backup}"
|
||||||
|
local n # New filename (created by _uniqueFilename_)
|
||||||
|
|
||||||
|
# Error handling
|
||||||
|
[ ! "$(declare -f "_execute_")" ] \
|
||||||
|
&& {
|
||||||
|
warning "need function _execute_"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
[ ! "$(declare -f "_uniqueFileName_")" ] \
|
||||||
|
&& {
|
||||||
|
warning "need function _uniqueFileName_"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
[ ! -e "$s" ] \
|
||||||
|
&& {
|
||||||
|
warning "Source '${s}' not found"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ ${useDirectory} == true ]; then
|
||||||
|
|
||||||
|
[ ! -d "${d}" ] \
|
||||||
|
&& _execute_ "mkdir -p \"${d}\"" "Creating backup directory"
|
||||||
|
|
||||||
|
if [ -e "$s" ]; then
|
||||||
|
n="$(basename "${s}")"
|
||||||
|
n="$(_uniqueFileName_ "${d}/${s#.}")"
|
||||||
|
if [ ${moveFile} == true ]; then
|
||||||
|
_execute_ "mv \"${s}\" \"${d}/${n##*/}\"" "Moving: '${s}' to '${d}/${n##*/}'"
|
||||||
|
else
|
||||||
|
_execute_ "cp -R \"${s}\" \"${d}/${n##*/}\"" "Backing up: '${s}' to '${d}/${n##*/}'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
n="$(_uniqueFileName_ "${s}.bak")"
|
||||||
|
if [ ${moveFile} == true ]; then
|
||||||
|
_execute_ "mv \"${s}\" \"${n}\"" "Moving '${s}' to '${n}'"
|
||||||
|
else
|
||||||
|
_execute_ "cp -R \"${s}\" \"${n}\"" "Backing up '${s}' to '${n}'"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanFilename_() {
|
||||||
|
# DESC: Cleans a filename of all non-alphanumeric (or user specified)
|
||||||
|
# characters and overwrites original
|
||||||
|
# ARGS: $1 (Required) - File to be cleaned
|
||||||
|
# $2 (optional) - Additional characters to be cleaned separated by commas
|
||||||
|
# OUTS: Overwrites file with new new and prints name of new file
|
||||||
|
# USAGE: _cleanFilename_ "FILENAME.TXT" "^,&,*"
|
||||||
|
# NOTE: IMPORTANT - This will overwrite the original file
|
||||||
|
# IMPORTANT - All spaces and underscores will be replaced by dashes (-)
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _cleanFilename_()!'
|
||||||
|
|
||||||
|
local arrayToClean
|
||||||
|
local fileToClean="$(realpath "$1")"
|
||||||
|
local optionalUserInput="${2-}"
|
||||||
|
|
||||||
|
IFS=',' read -r -a arrayToClean <<<"$optionalUserInput"
|
||||||
|
|
||||||
|
[ ! -f "${fileToClean}" ] \
|
||||||
|
&& {
|
||||||
|
warning "_cleanFileName_ ${fileToClean}: File doesn't exist"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local dir="$(realpath -d "${fileToClean}")"
|
||||||
|
local extension="${fileToClean##*.}"
|
||||||
|
local baseFileName="$(basename "${fileToClean%.*}")"
|
||||||
|
|
||||||
|
for i in "${arrayToClean[@]}"; do
|
||||||
|
baseFileName="$(echo "${baseFileName}" | sed "s/$i//g")"
|
||||||
|
done
|
||||||
|
|
||||||
|
baseFileName="$(echo "${baseFileName}" | tr -dc '[:alnum:]-_ ' | sed 's/ /-/g')"
|
||||||
|
|
||||||
|
local final="${dir}/${baseFileName}.${extension}"
|
||||||
|
|
||||||
|
if [ "${fileToClean}" != "${final}" ]; then
|
||||||
|
final="$(_uniqueFileName_ "${final}")"
|
||||||
|
if ${VERBOSE}; then
|
||||||
|
_execute_ "mv \"${fileToClean}\" \"${final}\""
|
||||||
|
else
|
||||||
|
_execute_ -q "mv \"${fileToClean}\" \"${final}\""
|
||||||
|
fi
|
||||||
|
echo "${final}"
|
||||||
|
else
|
||||||
|
echo "${fileToClean}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_parseFilename_() {
|
||||||
|
# DESC: Break a filename into its component parts which and place them into prefixed
|
||||||
|
# variables (dir, basename, extension, full path, etc.)
|
||||||
|
# with _parseFile...
|
||||||
|
# ARGS: $1 (Required) - A file
|
||||||
|
# OUTS: $_parsedFileFull - File and its real path (ie, resolve symlinks)
|
||||||
|
# $_parseFilePath - Path to the file
|
||||||
|
# $_parseFileName - Name of the file WITH extension
|
||||||
|
# $_parseFileBase - Name of file WITHOUT extension
|
||||||
|
# $_parseFileExt - The extension of the file (from _ext_())
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _parseFilename_()!'
|
||||||
|
local fileToParse="${1}"
|
||||||
|
|
||||||
|
[[ -f "${fileToParse}" ]] || {
|
||||||
|
error "Can't locate good file to parse at: ${fileToParse}"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# Ensure we are working with a real file, not a symlink
|
||||||
|
_parsedFileFull="$(realpath "${fileToParse}")" \
|
||||||
|
&& debug "${tan}\${_parsedFileFull}: ${_parsedFileFull-}${purple}"
|
||||||
|
|
||||||
|
# use the basename of the userFile going forward since the path is now in $filePath
|
||||||
|
_parseFileName=$(basename "${fileToParse}") \
|
||||||
|
&& debug "${tan}\$_parseFileName: ${_parseFileName}${purple}"
|
||||||
|
|
||||||
|
# Grab the filename without the extension
|
||||||
|
_parseFileBase="${_parseFileName%.*}" \
|
||||||
|
&& debug "${tan}\$_parseFileBase: ${_parseFileBase}${purple}"
|
||||||
|
|
||||||
|
# Grab the extension
|
||||||
|
if [[ "${fileToParse}" =~ .*\.[a-zA-Z]{2,4}$ ]]; then
|
||||||
|
_parseFileExt="$(_ext_ "${_parseFileName}")"
|
||||||
|
else
|
||||||
|
_parseFileExt=".${_parseFileName##*.}"
|
||||||
|
fi
|
||||||
|
debug "${tan}\$_parseFileExt: ${_parseFileExt}${purple}"
|
||||||
|
|
||||||
|
# Grab the directory
|
||||||
|
_parseFilePath="${_parsedFileFull%/*}" \
|
||||||
|
&& debug "${tan}\${_parseFilePath}: ${_parseFilePath}${purple}"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_decryptFile_() {
|
||||||
|
# DESC: Decrypts a file with openSSL
|
||||||
|
# ARGS: $1 (Required) - File to be decrypted
|
||||||
|
# $2 (Optional) - Name of output file (defaults to $1.decrypt)
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: _decryptFile_ "somefile.txt.enc" "decrypted_somefile.txt"
|
||||||
|
# NOTE: If a variable '$PASS' has a value, we will use that as the password
|
||||||
|
# to decrypt the file. Otherwise we will ask
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _decryptFile_()!'
|
||||||
|
|
||||||
|
local fileToDecrypt decryptedFile defaultName
|
||||||
|
fileToDecrypt="${1:?_decryptFile_ needs a file}"
|
||||||
|
defaultName="${fileToDecrypt%.enc}"
|
||||||
|
decryptedFile="${2:-$defaultName.decrypt}"
|
||||||
|
|
||||||
|
[ ! "$(declare -f "_execute_")" ] \
|
||||||
|
&& {
|
||||||
|
echo "need function _execute_"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ ! -f "$fileToDecrypt" ] && return 1
|
||||||
|
|
||||||
|
if [ -z "${PASS}" ]; then
|
||||||
|
_execute_ "openssl enc -aes-256-cbc -d -in \"${fileToDecrypt}\" -out \"${decryptedFile}\"" "Decrypt ${fileToDecrypt}"
|
||||||
|
else
|
||||||
|
_execute_ "openssl enc -aes-256-cbc -d -in \"${fileToDecrypt}\" -out \"${decryptedFile}\" -k \"${PASS}\"" "Decrypt ${fileToDecrypt}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_encryptFile_() {
|
||||||
|
# DESC: Encrypts a file using openSSL
|
||||||
|
# ARGS: $1 (Required) - Input file
|
||||||
|
# $2 (Optional) - Name of output file (defaults to $1.enc)
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: _encryptFile_ "somefile.txt" "encrypted_somefile.txt"
|
||||||
|
# NOTE: If a variable '$PASS' has a value, we will use that as the password
|
||||||
|
# for the encrypted file. Otherwise we will ask.
|
||||||
|
|
||||||
|
local fileToEncrypt encryptedFile defaultName
|
||||||
|
|
||||||
|
fileToEncrypt="${1:?_encodeFile_ needs a file}"
|
||||||
|
defaultName="${fileToEncrypt%.decrypt}"
|
||||||
|
encryptedFile="${2:-$defaultName.enc}"
|
||||||
|
|
||||||
|
[ ! -f "$fileToEncrypt" ] && return 1
|
||||||
|
|
||||||
|
[ ! "$(declare -f "_execute_")" ] \
|
||||||
|
&& {
|
||||||
|
echo "need function _execute_"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -z "${PASS}" ]; then
|
||||||
|
_execute_ "openssl enc -aes-256-cbc -salt -in \"${fileToEncrypt}\" -out \"${encryptedFile}\"" "Encrypt ${fileToEncrypt}"
|
||||||
|
else
|
||||||
|
_execute_ "openssl enc -aes-256-cbc -salt -in \"${fileToEncrypt}\" -out \"${encryptedFile}\" -k \"${PASS}\"" "Encrypt ${fileToEncrypt}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_ext_() {
|
||||||
|
# DESC: Extract the extension from a filename
|
||||||
|
# ARGS: $1 (Required) - Input file
|
||||||
|
# OPTS: -n - optional flag for number of extension levels (Ex: -n2)
|
||||||
|
# OUTS: Print extension to STDOUT
|
||||||
|
# USAGE:
|
||||||
|
# _ext_ foo.txt #==> txt
|
||||||
|
# _ext_ -n2 foo.tar.gz #==> tar.gz
|
||||||
|
# _ext_ foo.tar.gz #==> tar.gz
|
||||||
|
# _ext_ -n1 foo.tar.gz #==> gz
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _ext_()!'
|
||||||
|
|
||||||
|
local levels
|
||||||
|
local option
|
||||||
|
local filename
|
||||||
|
local exts
|
||||||
|
local ext
|
||||||
|
local fn
|
||||||
|
local i
|
||||||
|
|
||||||
|
unset OPTIND
|
||||||
|
while getopts ":n:" option; do
|
||||||
|
case $option in
|
||||||
|
n) levels=$OPTARG ;;
|
||||||
|
*) continue ;;
|
||||||
|
esac
|
||||||
|
done && shift $((OPTIND - 1))
|
||||||
|
|
||||||
|
filename=${1##*/}
|
||||||
|
|
||||||
|
[[ $filename == *.* ]] || return
|
||||||
|
|
||||||
|
fn=$filename
|
||||||
|
|
||||||
|
# Detect some common multi-extensions
|
||||||
|
if [[ ! ${levels-} ]]; then
|
||||||
|
case $(tr '[:upper:]' '[:lower:]' <<<"${filename}") in
|
||||||
|
*.tar.gz | *.tar.bz2) levels=2 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
levels=${levels:-1}
|
||||||
|
|
||||||
|
for ((i = 0; i < levels; i++)); do
|
||||||
|
ext=${fn##*.}
|
||||||
|
exts=${ext}${exts-}
|
||||||
|
fn=${fn%$ext}
|
||||||
|
[[ "$exts" == "${filename}" ]] && return
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "$exts"
|
||||||
|
}
|
||||||
|
|
||||||
|
_extract_() {
|
||||||
|
# DESC: Extract a compressed file
|
||||||
|
# ARGS: $1 (Required) - Input file
|
||||||
|
# $2 (optional) - Input 'v' to show verbose output
|
||||||
|
# OUTS: None
|
||||||
|
|
||||||
|
local filename
|
||||||
|
local foldername
|
||||||
|
local fullpath
|
||||||
|
local didfolderexist
|
||||||
|
local vv
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _extract_()!'
|
||||||
|
|
||||||
|
[[ "${2-}" == "v" ]] && vv="v"
|
||||||
|
|
||||||
|
if [ -f "$1" ]; then
|
||||||
|
case "$1" in
|
||||||
|
*.tar.bz2 | *.tbz | *.tbz2) tar "x${vv}jf" "$1" ;;
|
||||||
|
*.tar.gz | *.tgz) tar "x${vv}zf" "$1" ;;
|
||||||
|
*.tar.xz)
|
||||||
|
xz --decompress "$1"
|
||||||
|
set -- "$@" "${1:0:-3}"
|
||||||
|
;;
|
||||||
|
*.tar.Z)
|
||||||
|
uncompress "$1"
|
||||||
|
set -- "$@" "${1:0:-2}"
|
||||||
|
;;
|
||||||
|
*.bz2) bunzip2 "$1" ;;
|
||||||
|
*.deb) dpkg-deb -x${vv} "$1" "${1:0:-4}" ;;
|
||||||
|
*.pax.gz)
|
||||||
|
gunzip "$1"
|
||||||
|
set -- "$@" "${1:0:-3}"
|
||||||
|
;;
|
||||||
|
*.gz) gunzip "$1" ;;
|
||||||
|
*.pax) pax -r -f "$1" ;;
|
||||||
|
*.pkg) pkgutil --expand "$1" "${1:0:-4}" ;;
|
||||||
|
*.rar) unrar x "$1" ;;
|
||||||
|
*.rpm) rpm2cpio "$1" | cpio -idm${vv} ;;
|
||||||
|
*.tar) tar "x${vv}f" "$1" ;;
|
||||||
|
*.txz)
|
||||||
|
mv "$1" "${1:0:-4}.tar.xz"
|
||||||
|
set -- "$@" "${1:0:-4}.tar.xz"
|
||||||
|
;;
|
||||||
|
*.xz) xz --decompress "$1" ;;
|
||||||
|
*.zip | *.war | *.jar) unzip "$1" ;;
|
||||||
|
*.Z) uncompress "$1" ;;
|
||||||
|
*.7z) 7za x "$1" ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_json2yaml_() {
|
||||||
|
# DESC: Convert JSON to YAML
|
||||||
|
# ARGS: $1 (Required) - JSON file
|
||||||
|
# OUTS: None
|
||||||
|
|
||||||
|
python -c 'import sys, yaml, json; yaml.safe_dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)' <"${1:?_json2yaml_ needs a file}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_makeSymlink_() {
|
||||||
|
# DESC: Creates a symlink and backs up a file which may be overwritten by the new symlink. If the
|
||||||
|
# exact same symlink already exists, nothing is done.
|
||||||
|
# Default behavior will create a backup of a file to be overwritten
|
||||||
|
# ARGS: $1 (Required) - Source file
|
||||||
|
# $2 (Required) - Destination
|
||||||
|
# $3 (Optional) - Backup directory for files which may be overwritten (defaults to 'backup')
|
||||||
|
# OPTS: -n - Do not create a backup if target already exists
|
||||||
|
# -s - Use sudo when removing old files to make way for new symlinks
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: _makeSymlink_ "/dir/someExistingFile" "/dir/aNewSymLink" "/dir/backup/location"
|
||||||
|
# NOTE: This function makes use of the _execute_ function
|
||||||
|
|
||||||
|
local opt
|
||||||
|
local OPTIND=1
|
||||||
|
local backupOriginal=true
|
||||||
|
local useSudo=false
|
||||||
|
|
||||||
|
while getopts ":nNsS" opt; do
|
||||||
|
case $opt in
|
||||||
|
n | N) backupOriginal=false ;;
|
||||||
|
s | S) useSudo=true ;;
|
||||||
|
*)
|
||||||
|
{
|
||||||
|
error "Unrecognized option '$1' passed to _makeSymlink_" "$LINENO"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
|
if ! command -v realpath >/dev/null 2>&1; then
|
||||||
|
error "We must have 'realpath' installed and available in \$PATH to run."
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
notice "Install coreutils using homebrew and rerun this script."
|
||||||
|
info "\t$ brew install coreutils"
|
||||||
|
fi
|
||||||
|
_safeExit_ 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ $# -lt 2 ]] && fatal 'Missing required argument to _makeSymlink_()!'
|
||||||
|
|
||||||
|
local s="$1"
|
||||||
|
local d="$2"
|
||||||
|
local b="${3-}"
|
||||||
|
local o
|
||||||
|
|
||||||
|
# Fix files where $HOME is written as '~'
|
||||||
|
d="${d/\~/$HOME}"
|
||||||
|
s="${s/\~/$HOME}"
|
||||||
|
b="${b/\~/$HOME}"
|
||||||
|
|
||||||
|
[ ! -e "$s" ] \
|
||||||
|
&& {
|
||||||
|
error "'$s' not found"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
[ -z "$d" ] \
|
||||||
|
&& {
|
||||||
|
error "'${d}' not specified"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
[ ! "$(declare -f "_execute_")" ] \
|
||||||
|
&& {
|
||||||
|
echo "need function _execute_"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
[ ! "$(declare -f "_backupFile_")" ] \
|
||||||
|
&& {
|
||||||
|
echo "need function _backupFile_"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create destination directory if needed
|
||||||
|
[ ! -d "${d%/*}" ] \
|
||||||
|
&& _execute_ "mkdir -p \"${d%/*}\""
|
||||||
|
|
||||||
|
if [ ! -e "${d}" ]; then
|
||||||
|
_execute_ "ln -fs \"${s}\" \"${d}\"" "symlink ${s} → ${d}"
|
||||||
|
elif [ -h "${d}" ]; then
|
||||||
|
o="$(realpath "${d}")"
|
||||||
|
|
||||||
|
[[ "${o}" == "${s}" ]] && {
|
||||||
|
if [ "${DRYRUN}" == true ]; then
|
||||||
|
dryrun "Symlink already exists: ${s} → ${d}"
|
||||||
|
else
|
||||||
|
info "Symlink already exists: ${s} → ${d}"
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${backupOriginal}" == true ]]; then
|
||||||
|
_backupFile_ "${d}" "${b:-backup}"
|
||||||
|
fi
|
||||||
|
if [[ "${DRYRUN}" == false ]]; then
|
||||||
|
if [[ "${useSudo}" == true ]]; then
|
||||||
|
command rm -rf "${d}"
|
||||||
|
else
|
||||||
|
command rm -rf "${d}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_execute_ "ln -fs \"${s}\" \"${d}\"" "symlink ${s} → ${d}"
|
||||||
|
elif [ -e "${d}" ]; then
|
||||||
|
if [[ "${backupOriginal}" == true ]]; then
|
||||||
|
_backupFile_ "${d}" "${b:-backup}"
|
||||||
|
fi
|
||||||
|
if [[ "${DRYRUN}" == false ]]; then
|
||||||
|
if [[ "${useSudo}" == true ]]; then
|
||||||
|
sudo command rm -rf "${d}"
|
||||||
|
else
|
||||||
|
command rm -rf "${d}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
_execute_ "ln -fs \"${s}\" \"${d}\"" "symlink ${s} → ${d}"
|
||||||
|
else
|
||||||
|
warning "Error linking: ${s} → ${d}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_parseYAML_() {
|
||||||
|
# DESC: Convert a YANML file into BASH variables for use in a shell script
|
||||||
|
# ARGS: $1 (Required) - Source YAML file
|
||||||
|
# $2 (Required) - Prefix for the variables to avoid namespace collisions
|
||||||
|
# OUTS: Prints variables and arrays derived from YAML File
|
||||||
|
# USAGE: To source into a script
|
||||||
|
# _parseYAML_ "sample.yml" "CONF_" > tmp/variables.txt
|
||||||
|
# source "tmp/variables.txt"
|
||||||
|
#
|
||||||
|
# NOTE: https://gist.github.com/DinoChiesa/3e3c3866b51290f31243
|
||||||
|
# https://gist.github.com/epiloque/8cf512c6d64641bde388
|
||||||
|
|
||||||
|
|
||||||
|
local yamlFile="${1:?_parseYAML_ needs a file}"
|
||||||
|
local prefix="${2-}"
|
||||||
|
|
||||||
|
[ ! -s "${yamlFile}" ] && return 1
|
||||||
|
|
||||||
|
local s='[[:space:]]*'
|
||||||
|
local w='[a-zA-Z0-9_]*'
|
||||||
|
local fs="$(echo @ | tr @ '\034')"
|
||||||
|
sed -ne "s|^\(${s}\)\(${w}\)${s}:${s}\"\(.*\)\"${s}\$|\1${fs}\2${fs}\3|p" \
|
||||||
|
-e "s|^\(${s}\)\(${w}\)${s}[:-]${s}\(.*\)${s}\$|\1${fs}\2${fs}\3|p" "${yamlFile}" \
|
||||||
|
| awk -F"${fs}" '{
|
||||||
|
indent = length($1)/2;
|
||||||
|
if (length($2) == 0) { conj[indent]="+";} else {conj[indent]="";}
|
||||||
|
vname[indent] = $2;
|
||||||
|
for (i in vname) {if (i > indent) {delete vname[i]}}
|
||||||
|
if (length($3) > 0) {
|
||||||
|
vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")}
|
||||||
|
printf("%s%s%s%s=(\"%s\")\n", "'"${prefix}"'",vn, $2, conj[indent-1],$3);
|
||||||
|
}
|
||||||
|
}' | sed 's/_=/+=/g' | sed 's/[[:space:]]*#.*"/"/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
_readFile_() {
|
||||||
|
# DESC: Prints each line of a file
|
||||||
|
# ARGS: $1 (Required) - Input file
|
||||||
|
# OUTS: Prints contents of file
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _readFile_()!'
|
||||||
|
|
||||||
|
local result
|
||||||
|
local c="$1"
|
||||||
|
|
||||||
|
[ ! -f "$c" ] \
|
||||||
|
&& {
|
||||||
|
echo "'$c' not found"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while read -r result; do
|
||||||
|
echo "${result}"
|
||||||
|
done <"${c}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_sourceFile_() {
|
||||||
|
# DESC: Source a file into a script
|
||||||
|
# ARGS: $1 (Required) - File to be sourced
|
||||||
|
# OUTS: None
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _sourceFile_()!'
|
||||||
|
|
||||||
|
local c="$1"
|
||||||
|
|
||||||
|
[ ! -f "$c" ] \
|
||||||
|
&& {
|
||||||
|
fatal "Attempted to source '$c' Not found"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
source "$c"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_uniqueFileName_() {
|
||||||
|
# DESC: Ensure a file to be created has a unique filename to avoid overwriting other files
|
||||||
|
# ARGS: $1 (Required) - Name of file to be created
|
||||||
|
# $2 (Optional) - Separation characted (Defaults to a period '.')
|
||||||
|
# OUTS: Prints unique filename to STDOUT
|
||||||
|
# USAGE: _uniqueFileName_ "/some/dir/file.txt" "-"
|
||||||
|
|
||||||
|
local fullfile="${1:?_uniqueFileName_ needs a file}"
|
||||||
|
local spacer="${2:-.}"
|
||||||
|
local directory
|
||||||
|
local filename
|
||||||
|
local extension
|
||||||
|
local newfile
|
||||||
|
local n
|
||||||
|
|
||||||
|
if ! command -v realpath >/dev/null 2>&1; then
|
||||||
|
error "We must have 'realpath' installed and available in \$PATH to run."
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
notice "Install coreutils using homebrew and rerun this script."
|
||||||
|
info "\t$ brew install coreutils"
|
||||||
|
fi
|
||||||
|
_safeExit_ 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find directories with realpath if input is an actual file
|
||||||
|
if [ -e "${fullfile}" ]; then
|
||||||
|
fullfile="$(realpath "${fullfile}")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
directory="$(dirname "${fullfile}")"
|
||||||
|
filename="$(basename "${fullfile}")"
|
||||||
|
|
||||||
|
# Extract extensions only when they exist
|
||||||
|
if [[ "${filename}" =~ \.[a-zA-Z]{2,4}$ ]]; then
|
||||||
|
extension=".${filename##*.}"
|
||||||
|
filename="${filename%.*}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
newfile="${directory}/${filename}${extension-}"
|
||||||
|
|
||||||
|
if [ -e "${newfile}" ]; then
|
||||||
|
n=1
|
||||||
|
while [[ -e "${directory}/${filename}${extension-}${spacer}${n}" ]]; do
|
||||||
|
((n++))
|
||||||
|
done
|
||||||
|
newfile="${directory}/${filename}${extension-}${spacer}${n}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "${newfile}"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_yaml2json_() {
|
||||||
|
# DESC: Convert a YAML file to JSON
|
||||||
|
# ARGS: $1 (Required) - Input YAML file
|
||||||
|
# OUTS: None
|
||||||
|
|
||||||
|
python -c 'import sys, yaml, json; json.dump(yaml.load(sys.stdin), sys.stdout, indent=4)' <"${1:?_yaml2json_ needs a file}"
|
||||||
|
}
|
||||||
39
utilities/macOS.bash
Normal file
39
utilities/macOS.bash
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Functions for use on computers running MacOS
|
||||||
|
|
||||||
|
_haveScriptableFinder_() {
|
||||||
|
# DESC: Determine whether we can script the Finder or not
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: true/false
|
||||||
|
|
||||||
|
local finder_pid
|
||||||
|
finder_pid="$(pgrep -f /System/Library/CoreServices/Finder.app | head -n 1)"
|
||||||
|
|
||||||
|
if [[ (${finder_pid} -gt 1) && ("${STY-}" == "") ]]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_guiInput_() {
|
||||||
|
# DESC: Ask for user input using a Mac dialog box
|
||||||
|
# ARGS: $1 (Optional) - Text in dialogue box (Default: Password)
|
||||||
|
# OUTS: None
|
||||||
|
# NOTE: https://github.com/herrbischoff/awesome-osx-command-line/blob/master/functions.md
|
||||||
|
if _haveScriptableFinder_; then
|
||||||
|
guiPrompt="${1:-Password:}"
|
||||||
|
guiInput=$(
|
||||||
|
osascript &>/dev/null <<EOF
|
||||||
|
tell application "System Events"
|
||||||
|
activate
|
||||||
|
text returned of (display dialog "${guiPrompt}" default answer "" with hidden answer)
|
||||||
|
end tell
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
echo -n "${guiInput}"
|
||||||
|
else
|
||||||
|
error "No GUI input without macOS"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
71
utilities/numbers.bash
Normal file
71
utilities/numbers.bash
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
|
||||||
|
_fromSeconds_() {
|
||||||
|
# DESC: Convert seconds to HH:MM:SS
|
||||||
|
# ARGS: $1 (Required) - Time in seconds
|
||||||
|
# OUTS: Print HH:MM:SS to STDOUT
|
||||||
|
# USAGE: _convertSecs_ "SECONDS"
|
||||||
|
# To compute the time it takes a script to run:
|
||||||
|
# STARTTIME=$(date +"%s")
|
||||||
|
# ENDTIME=$(date +"%s")
|
||||||
|
# TOTALTIME=$(($ENDTIME-$STARTTIME)) # human readable time
|
||||||
|
# _convertSecs_ "$TOTALTIME"
|
||||||
|
|
||||||
|
((h = ${1} / 3600))
|
||||||
|
((m = (${1} % 3600) / 60))
|
||||||
|
((s = ${1} % 60))
|
||||||
|
printf "%02d:%02d:%02d\n" $h $m $s
|
||||||
|
}
|
||||||
|
|
||||||
|
_toSeconds_() {
|
||||||
|
# DESC: Converts HH:MM:SS to seconds
|
||||||
|
# ARGS: $1 (Required) - Time in HH:MM:SS
|
||||||
|
# OUTS: Print seconds to STDOUT
|
||||||
|
# USAGE: _toSeconds_ "01:00:00"
|
||||||
|
# NOTE: Acceptable Input Formats
|
||||||
|
# 24 12 09
|
||||||
|
# 12,12,09
|
||||||
|
# 12;12;09
|
||||||
|
# 12:12:09
|
||||||
|
# 12-12-09
|
||||||
|
# 12H12M09S
|
||||||
|
# 12h12m09s
|
||||||
|
|
||||||
|
local saveIFS
|
||||||
|
|
||||||
|
if [[ "$1" =~ [0-9]{1,2}(:|,|-|_|,| |[hHmMsS])[0-9]{1,2}(:|,|-|_|,| |[hHmMsS])[0-9]{1,2} ]]; then
|
||||||
|
saveIFS="$IFS"
|
||||||
|
IFS=":,;-_, HhMmSs" read -r h m s <<< "$1"
|
||||||
|
IFS="$saveIFS"
|
||||||
|
else
|
||||||
|
h="$1"
|
||||||
|
m="$2"
|
||||||
|
s="$3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo $(( 10#$h * 3600 + 10#$m * 60 + 10#$s ))
|
||||||
|
}
|
||||||
|
|
||||||
|
_countdown_() {
|
||||||
|
# DESC: Sleep for a specified amount of time
|
||||||
|
# ARGS: $1 (Optional) - Total seconds to sleep for(Default is 10)
|
||||||
|
# $2 (Optional) - Increment to count down
|
||||||
|
# $3 (Optional) - Message to print at each increment (default is ...)
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: _countdown_ 10 1 "Waiting for cache to invalidate"
|
||||||
|
|
||||||
|
local i ii t
|
||||||
|
local n=${1:-10}
|
||||||
|
local stime=${2:-1}
|
||||||
|
local message="${3:-...}"
|
||||||
|
((t = n + 1))
|
||||||
|
|
||||||
|
for ((i = 1; i <= n; i++)); do
|
||||||
|
((ii = t - i))
|
||||||
|
if declare -f "info" &>/dev/null 2>&1; then
|
||||||
|
info "${message} ${ii}"
|
||||||
|
else
|
||||||
|
echo "${message} ${ii}"
|
||||||
|
fi
|
||||||
|
sleep $stime
|
||||||
|
done
|
||||||
|
}
|
||||||
148
utilities/services.bash
Normal file
148
utilities/services.bash
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
_haveInternet_() {
|
||||||
|
# DESC: Tests to see if there is an active Internet connection
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: _haveInternet_ && [SOMETHING]
|
||||||
|
# NOTE: https://stackoverflow.com/questions/929368/
|
||||||
|
|
||||||
|
if command -v fping &>/dev/null; then
|
||||||
|
fping 1.1.1.1 &>/dev/null \
|
||||||
|
&& return 0 \
|
||||||
|
|| return 1
|
||||||
|
elif ping -t 2 -c 1 1 1.1.1.1 &>/dev/null; then
|
||||||
|
return 0
|
||||||
|
elif command -v route &>/dev/null; then
|
||||||
|
local GATEWAY="$(route -n get default | grep gateway)"
|
||||||
|
ping -t 2 -c 1 "$(echo "${GATEWAY}" | cut -d ':' -f 2)" &>/dev/null \
|
||||||
|
&& return 0 \
|
||||||
|
|| return 1
|
||||||
|
elif command -v ip &>/dev/null; then
|
||||||
|
ping -t 2 -c 1 "$(ip r | grep default | cut -d ' ' -f 3)" &>/dev/null \
|
||||||
|
&& return 0 \
|
||||||
|
|| return 1
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_httpStatus_() {
|
||||||
|
# DESC: Report the HTTP status of a specified URL
|
||||||
|
# ARGS: $1 (Required) - URL (will work fine without https:// prefix)
|
||||||
|
# $2 (Optional) - Seconds to wait until timeout (Default is 3)
|
||||||
|
# $3 (Optional) - either '--code' or '--status' (default)
|
||||||
|
# $4 (optional) - CURL opts separated by spaces (Use -L to follow redirects)
|
||||||
|
# OUTS: Prints output to STDOUT
|
||||||
|
# USAGE: _httpStatus_ URL [timeout] [--code or --status] [curl opts]
|
||||||
|
# NOTE: https://gist.github.com/rsvp/1171304
|
||||||
|
#
|
||||||
|
# Example: $ _httpStatus_ bit.ly
|
||||||
|
# 301 Redirection: Moved Permanently
|
||||||
|
#
|
||||||
|
# Example: $ _httpStatus_ www.google.com 100 --code
|
||||||
|
local code
|
||||||
|
local status
|
||||||
|
|
||||||
|
local saveIFS=${IFS}
|
||||||
|
IFS=$' \n\t'
|
||||||
|
|
||||||
|
local url=${1:?_httpStatus_ needs an url}
|
||||||
|
local timeout=${2:-'3'}
|
||||||
|
local flag=${3:-'--status'}
|
||||||
|
local arg4=${4:-''}
|
||||||
|
local arg5=${5:-''}
|
||||||
|
local arg6=${6:-''}
|
||||||
|
local arg7=${7:-''}
|
||||||
|
local curlops="${arg4} ${arg5} ${arg6} ${arg7}"
|
||||||
|
|
||||||
|
# __________ get the CODE which is numeric:
|
||||||
|
code=$(echo "$(curl --write-out %{http_code} --silent --connect-timeout "${timeout}" \
|
||||||
|
--no-keepalive "${curlops}" --output /dev/null "${url}")")
|
||||||
|
|
||||||
|
# __________ get the STATUS (from code) which is human interpretable:
|
||||||
|
case $code in
|
||||||
|
000) status="Not responding within ${timeout} seconds" ;;
|
||||||
|
100) status="Informational: Continue" ;;
|
||||||
|
101) status="Informational: Switching Protocols" ;;
|
||||||
|
200) status="Successful: OK within ${timeout} seconds" ;;
|
||||||
|
201) status="Successful: Created" ;;
|
||||||
|
202) status="Successful: Accepted" ;;
|
||||||
|
203) status="Successful: Non-Authoritative Information" ;;
|
||||||
|
204) status="Successful: No Content" ;;
|
||||||
|
205) status="Successful: Reset Content" ;;
|
||||||
|
206) status="Successful: Partial Content" ;;
|
||||||
|
300) status="Redirection: Multiple Choices" ;;
|
||||||
|
301) status="Redirection: Moved Permanently" ;;
|
||||||
|
302) status="Redirection: Found residing temporarily under different URI" ;;
|
||||||
|
303) status="Redirection: See Other" ;;
|
||||||
|
304) status="Redirection: Not Modified" ;;
|
||||||
|
305) status="Redirection: Use Proxy" ;;
|
||||||
|
306) status="Redirection: status not defined" ;;
|
||||||
|
307) status="Redirection: Temporary Redirect" ;;
|
||||||
|
400) status="Client Error: Bad Request" ;;
|
||||||
|
401) status="Client Error: Unauthorized" ;;
|
||||||
|
402) status="Client Error: Payment Required" ;;
|
||||||
|
403) status="Client Error: Forbidden" ;;
|
||||||
|
404) status="Client Error: Not Found" ;;
|
||||||
|
405) status="Client Error: Method Not Allowed" ;;
|
||||||
|
406) status="Client Error: Not Acceptable" ;;
|
||||||
|
407) status="Client Error: Proxy Authentication Required" ;;
|
||||||
|
408) status="Client Error: Request Timeout within ${timeout} seconds" ;;
|
||||||
|
409) status="Client Error: Conflict" ;;
|
||||||
|
410) status="Client Error: Gone" ;;
|
||||||
|
411) status="Client Error: Length Required" ;;
|
||||||
|
412) status="Client Error: Precondition Failed" ;;
|
||||||
|
413) status="Client Error: Request Entity Too Large" ;;
|
||||||
|
414) status="Client Error: Request-URI Too Long" ;;
|
||||||
|
415) status="Client Error: Unsupported Media Type" ;;
|
||||||
|
416) status="Client Error: Requested Range Not Satisfiable" ;;
|
||||||
|
417) status="Client Error: Expectation Failed" ;;
|
||||||
|
500) status="Server Error: Internal Server Error" ;;
|
||||||
|
501) status="Server Error: Not Implemented" ;;
|
||||||
|
502) status="Server Error: Bad Gateway" ;;
|
||||||
|
503) status="Server Error: Service Unavailable" ;;
|
||||||
|
504) status="Server Error: Gateway Timeout within ${timeout} seconds" ;;
|
||||||
|
505) status="Server Error: HTTP Version Not Supported" ;;
|
||||||
|
*) die "httpstatus: status not defined." ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case ${flag} in
|
||||||
|
--status) echo "${code} ${status}" ;;
|
||||||
|
-s) echo "${code} ${status}" ;;
|
||||||
|
--code) echo "${code}" ;;
|
||||||
|
-c) echo "${code}" ;;
|
||||||
|
*) echo " httpstatus: bad flag" && _safeExit_ ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
IFS="${saveIFS}"
|
||||||
|
}
|
||||||
|
|
||||||
|
_pushover_() {
|
||||||
|
# DESC: Sends a notification via Pushover
|
||||||
|
# ARGS: $1 (Required) - Title of notification
|
||||||
|
# $2 (Required) - Body of notification
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: _pushover_ "Title Goes Here" "Message Goes Here"
|
||||||
|
# NOTE: The variables for the two API Keys must have valid values
|
||||||
|
# Credit: http://ryonsherman.blogspot.com/2012/10/shell-script-to-send-pushover.html
|
||||||
|
|
||||||
|
local PUSHOVERURL
|
||||||
|
local API_KEY
|
||||||
|
local USER_KEY
|
||||||
|
local DEVICE
|
||||||
|
local TITLE
|
||||||
|
local MESSAGE
|
||||||
|
|
||||||
|
PUSHOVERURL="https://api.pushover.net/1/messages.json"
|
||||||
|
API_KEY="${PUSHOVER_API_KEY}"
|
||||||
|
USER_KEY="${PUSHOVER_USER_KEY}"
|
||||||
|
DEVICE=""
|
||||||
|
TITLE="${1}"
|
||||||
|
MESSAGE="${2}"
|
||||||
|
curl \
|
||||||
|
-F "token=${API_KEY}" \
|
||||||
|
-F "user=${USER_KEY}" \
|
||||||
|
-F "device=${DEVICE}" \
|
||||||
|
-F "title=${TITLE}" \
|
||||||
|
-F "message=${MESSAGE}" \
|
||||||
|
"${PUSHOVERURL}" >/dev/null 2>&1
|
||||||
|
}
|
||||||
289
utilities/textProcessing.bash
Normal file
289
utilities/textProcessing.bash
Normal file
@@ -0,0 +1,289 @@
|
|||||||
|
# Transform text using these functions
|
||||||
|
# Some were adapted from https://github.com/jmcantrell/bashful
|
||||||
|
|
||||||
|
_cleanString_() {
|
||||||
|
# DESC: Cleans a string of text
|
||||||
|
# ARGS: $1 (Required) - String to be cleaned
|
||||||
|
# $2 (optional) - Specific characters to be cleaned (separated by commas,
|
||||||
|
# escape regex special chars)
|
||||||
|
# OPTS: -l Forces all text to lowercase
|
||||||
|
# -u Forces all text to uppercase
|
||||||
|
# -a Removes all non-alphanumeric characters except for spaces and dashes
|
||||||
|
# -p Replace one character with another (separated by commas) (escape regex characters)
|
||||||
|
# -s In combination with -a, replaces characters with a space
|
||||||
|
# OUTS: Prints result to STDOUT
|
||||||
|
# USAGE: _cleanString_ [OPT] [STRING] [CHARS TO REPLACE]
|
||||||
|
# _cleanString_ -p " ,-" [STRING] [CHARS TO REPLACE]
|
||||||
|
# NOTES: Always cleaned:
|
||||||
|
# - leading white space
|
||||||
|
# - trailing white space
|
||||||
|
# - multiple spaces become a single space
|
||||||
|
# - remove spaces before and aftrer -_
|
||||||
|
|
||||||
|
local opt
|
||||||
|
local lc=false
|
||||||
|
local uc=false
|
||||||
|
local alphanumeric=false
|
||||||
|
local replace=false
|
||||||
|
local us=false
|
||||||
|
|
||||||
|
local OPTIND=1
|
||||||
|
while getopts ":lLuUaAsSpP" opt; do
|
||||||
|
case $opt in
|
||||||
|
l | L) lc=true ;;
|
||||||
|
u | U) uc=true ;;
|
||||||
|
a | A) alphanumeric=true ;;
|
||||||
|
s | S) us=true ;;
|
||||||
|
p | P)
|
||||||
|
shift
|
||||||
|
local pairs=()
|
||||||
|
IFS=',' read -r -a pairs <<<"$1"
|
||||||
|
replace=true ;;
|
||||||
|
*)
|
||||||
|
{
|
||||||
|
error "Unrecognized option '$1' passed to _execute. Exiting."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
shift $((OPTIND - 1))
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && fatal 'Missing required argument to _cleanString_()!'
|
||||||
|
|
||||||
|
local string="${1}"
|
||||||
|
local userChars="${2:-}"
|
||||||
|
|
||||||
|
local arrayToClean=()
|
||||||
|
IFS=',' read -r -a arrayToClean <<<"${userChars}"
|
||||||
|
|
||||||
|
# trim trailing/leading white space and duplicate spaces/tabs
|
||||||
|
string="$(echo "${string}" | awk '{$1=$1};1')"
|
||||||
|
|
||||||
|
local i
|
||||||
|
for i in "${arrayToClean[@]}"; do
|
||||||
|
debug "cleaning: $i"
|
||||||
|
string="$(echo "${string}" | sed "s/$i//g")"
|
||||||
|
done
|
||||||
|
|
||||||
|
("${lc}") \
|
||||||
|
&& string="$(echo "${string}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
|
||||||
|
("${uc}") \
|
||||||
|
&& string="$(echo "${string}" | tr '[:lower:]' '[:upper:]')"
|
||||||
|
|
||||||
|
if "${alphanumeric}" && "${us}"; then
|
||||||
|
string="$(echo "${string}" | tr -c '[:alnum:] -' ' ')"
|
||||||
|
elif "${alphanumeric}"; then
|
||||||
|
string="$(echo "${string}" | sed "s/[^a-zA-Z0-9 -]//g")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if "${replace}"; then
|
||||||
|
string="$(echo "${string}" | sed "s/${pairs[0]}/${pairs[1]}/g")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# trim trailing/leading white space and duplicate dashes
|
||||||
|
string="$(echo "${string}" | tr -s '-')"
|
||||||
|
string="$(echo "${string}" | sed -E 's/([-_]) /\1/g' | sed -E 's/ ([-_])/\1/g')"
|
||||||
|
string="$(echo "${string}" | awk '{$1=$1};1')"
|
||||||
|
|
||||||
|
printf "%s\n" "${string}"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_stopWords_() {
|
||||||
|
# DESC: Removes common stopwords from a string
|
||||||
|
# ARGS: $1 (Required) - String to parse
|
||||||
|
# $2 (Optional) - Additional stopwords (comma separated)
|
||||||
|
# OUTS: Prints cleaned string to STDOUT
|
||||||
|
# USAGE: cleanName="$(_stopWords_ "[STRING]" "[MORE,STOP,WORDS]")"
|
||||||
|
# NOTE: Requires a stopwords file in sed format (expected at: ~/.sed/stopwords.sed)
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && {
|
||||||
|
warning 'Missing required argument to _stripCommonWords_!'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[ "$(command -v gsed)" ] || {
|
||||||
|
error "Can not continue without gsed. Use '${YELLOW}brew install gnu-sed${reset}'"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local string="${1}"
|
||||||
|
|
||||||
|
local sedFile="${HOME}/.sed/stopwords.sed"
|
||||||
|
if [ -f "${sedFile}" ]; then
|
||||||
|
string="$(echo "${string}" | gsed -f "${sedFile}")"
|
||||||
|
else
|
||||||
|
debug "Missing sedfile in _stopWords_()"
|
||||||
|
fi
|
||||||
|
|
||||||
|
declare -a localStopWords=()
|
||||||
|
IFS=',' read -r -a localStopWords <<<"${2-}"
|
||||||
|
|
||||||
|
if [[ ${#localStopWords[@]} -gt 0 ]]; then
|
||||||
|
for w in "${localStopWords[@]}"; do
|
||||||
|
string="$(echo "$string" | gsed -E "s/$w//gI")"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove double spaces and trim left/right
|
||||||
|
string="$(echo "$string" | sed -E 's/[ ]{2,}/ /g' | _ltrim_ | _rtrim_)"
|
||||||
|
|
||||||
|
echo "${string}"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_escape_() {
|
||||||
|
# DESC: Escapes a string by adding \ before special chars
|
||||||
|
# ARGS: $@ (Required) - String to be escaped
|
||||||
|
# OUTS: Prints output to STDOUT
|
||||||
|
# USAGE: _escape_ "Some text here"
|
||||||
|
|
||||||
|
# shellcheck disable=2001
|
||||||
|
echo "${@}" | sed 's/[]\.|$[ (){}?+*^]/\\&/g'
|
||||||
|
}
|
||||||
|
|
||||||
|
_htmlDecode_() {
|
||||||
|
# DESC: Decode HTML characters with sed
|
||||||
|
# ARGS: $1 (Required) - String to be decoded
|
||||||
|
# OUTS: Prints output to STDOUT
|
||||||
|
# USAGE: _htmlDecode_ <string>
|
||||||
|
# NOTE: Must have a sed file containing replacements
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && {
|
||||||
|
error 'Missing required argument to _htmlDecode_()!'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local sedFile
|
||||||
|
sedFile="${HOME}/.sed/htmlDecode.sed"
|
||||||
|
|
||||||
|
[ -f "${sedFile}" ] \
|
||||||
|
&& { echo "${1}" | sed -f "${sedFile}"; } \
|
||||||
|
|| return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_htmlEncode_() {
|
||||||
|
# DESC: Encode HTML characters with sed
|
||||||
|
# ARGS: $1 (Required) - String to be encoded
|
||||||
|
# OUTS: Prints output to STDOUT
|
||||||
|
# USAGE: _htmlEncode_ <string>
|
||||||
|
# NOTE: Must have a sed file containing replacements
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && {
|
||||||
|
error 'Missing required argument to _htmlEncode_()!'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local sedFile
|
||||||
|
sedFile="${HOME}/.sed/htmlEncode.sed"
|
||||||
|
|
||||||
|
[ -f "${sedFile}" ] \
|
||||||
|
&& { echo "${1}" | sed -f "${sedFile}"; } \
|
||||||
|
|| return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
_lower_() {
|
||||||
|
# DESC: Convert a string to lowercase
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: text=$(_lower_ <<<"$1")
|
||||||
|
# echo "STRING" | _lower_
|
||||||
|
tr '[:upper:]' '[:lower:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
_upper_() {
|
||||||
|
# DESC: Convert a string to uppercase
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: text=$(_upper_ <<<"$1")
|
||||||
|
# echo "STRING" | _upper_
|
||||||
|
tr '[:lower:]' '[:upper:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
_ltrim_() {
|
||||||
|
# DESC: Removes all leading whitespace (from the left)
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: text=$(_ltrim_ <<<"$1")
|
||||||
|
# echo "STRING" | _ltrim_
|
||||||
|
local char=${1:-[:space:]}
|
||||||
|
sed "s%^[${char//%/\\%}]*%%"
|
||||||
|
}
|
||||||
|
|
||||||
|
_regex_() {
|
||||||
|
# DESC: Use regex to validate and parse strings
|
||||||
|
# ARGS: $1 (Required) - Input String
|
||||||
|
# $2 (Required) - Regex pattern
|
||||||
|
# OUTS: Prints string matching regex
|
||||||
|
# Returns error if no part of string did not match regex
|
||||||
|
# USAGE: regex "#FFFFFF" '^(#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3}))$' || echo "no match found"
|
||||||
|
# NOTE: This example only prints the first matching group. When using multiple capture
|
||||||
|
# groups some modification is needed.
|
||||||
|
# https://github.com/dylanaraps/pure-bash-bible
|
||||||
|
if [[ $1 =~ $2 ]]; then
|
||||||
|
printf '%s\n' "${BASH_REMATCH[1]}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_rtrim_() {
|
||||||
|
# DESC: Removes all leading whitespace (from the right)
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: text=$(_rtrim_ <<<"$1")
|
||||||
|
# echo "STRING" | _rtrim_
|
||||||
|
local char=${1:-[:space:]}
|
||||||
|
sed "s%[${char//%/\\%}]*$%%"
|
||||||
|
}
|
||||||
|
|
||||||
|
_trim_() {
|
||||||
|
# DESC: Removes all leading/trailing whitespace
|
||||||
|
# ARGS: None
|
||||||
|
# OUTS: None
|
||||||
|
# USAGE: text=$(_trim_ <<<"$1")
|
||||||
|
# echo "STRING" | _trim_
|
||||||
|
awk '{$1=$1;print}'
|
||||||
|
}
|
||||||
|
|
||||||
|
_urlEncode_() {
|
||||||
|
# DESC: URL encode a string
|
||||||
|
# ARGS: $1 (Required) - String to be encoded
|
||||||
|
# OUTS: Prints output to STDOUT
|
||||||
|
# USAGE: _urlEncode_ <string>
|
||||||
|
# NOTE: https://gist.github.com/cdown/1163649
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && {
|
||||||
|
error 'Missing required argument to _urlEncode_()!'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local LANG=C
|
||||||
|
local i
|
||||||
|
|
||||||
|
for ((i = 0; i < ${#1}; i++)); do
|
||||||
|
if [[ ${1:$i:1} =~ ^[a-zA-Z0-9\.\~_-]$ ]]; then
|
||||||
|
printf "${1:$i:1}"
|
||||||
|
else
|
||||||
|
printf '%%%02X' "'${1:$i:1}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_urlDecode_() {
|
||||||
|
# DESC: Decode a URL encoded string
|
||||||
|
# ARGS: $1 (Required) - String to be decoded
|
||||||
|
# OUTS: Prints output to STDOUT
|
||||||
|
# USAGE: _urlDecode_ <string>
|
||||||
|
|
||||||
|
[[ $# -lt 1 ]] && {
|
||||||
|
error 'Missing required argument to _urlDecode_()!'
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local url_encoded="${1//+/ }"
|
||||||
|
printf '%b' "${url_encoded//%/\\x}"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user