├── README.md ├── _where.bash └── common.bash /README.md: -------------------------------------------------------------------------------- 1 | # where 2 | 3 | Brett Terpstra 2015, WTF license 4 | 5 | ## Description 6 | 7 | For people who spread bash functions and aliases across multiple sourced 8 | files and then can't track down where they defined them 9 | 10 | ## Installation 11 | 12 | Place both _where.bash and common.bash in the same folder in your $PATH. 13 | 14 | ### Option 1: Hook `source` 15 | 16 | This option will hook the Bash default `source` command and index shell scripts whenenver they're sourced from another file. 17 | 18 | Add the following to your .bash_profile before any source commands are run: 19 | 20 | export WHERE_HOOK_SOURCE=true 21 | export WHERE_EXPIRATION=86400 # once a day 22 | source /path/to/_where.bash 23 | 24 | If you choose this option, see **Database refresh throttling** below. 25 | 26 | ### Option 2: Curated indexing 27 | 28 | 1. Source _where.sh in your .bash_profile prior to sourcing other files 29 | 30 | source /path/to/_where.bash 31 | 32 | 2. Add the following to the bottom of specific files to be indexed: 33 | 34 | _where_from $BASH_SOURCE 35 | 36 | Indexing every file you source can slow down login, so option 2 may be ideal. 37 | 38 | You can add the necessary line to every file in a folder and subfolders 39 | using the `_where_add` function: 40 | 41 | $ _where_add ~/scripts{,/**}/*.{,ba}sh 42 | 43 | Remove the lines using the _where_clean function: 44 | 45 | $ _where_clean ~/scripts{,/**}/*.{,ba}sh 46 | 47 | ## Usage 48 | 49 | where [-kav] [function_name|search_pattern] 50 | 51 | Once the database is built, you can use the `where` command to find your 52 | functions. Running `where` with no arguments will output a list of all 53 | registered plugins and aliases. 54 | 55 | Add an argument to filter for a specific function or alias. By default 56 | only exact matches will return. If an exact match is found, just the 57 | file path of the originating script will be returned. 58 | 59 | ### Options 60 | 61 | -k Show all functions and aliases containing filter text 62 | -a Show all functions and aliases fuzzy matched 63 | -v Verbose output 64 | -n Suppress line number in paths 65 | -E Edit result 66 | -h Show this screen 67 | 68 | The -k switch turns on "apropos" mode, which lets you find any function 69 | containing the filter string in its name. 70 | 71 | The -a switch takes "apropos" a step further, using the filter argument 72 | as a fuzzy search string. It will match any functions/arguments containing 73 | the characters in the search string in sequence, but they do not need 74 | to be contiguous. 75 | 76 | If -a is specified, -k is ignored in the arguments. 77 | 78 | -E causes $EDITOR to be opened with the path to the file containing 79 | the searched function. -E does not work with -k or -a. 80 | 81 | ### Aliases 82 | 83 | `where?` is equivalent to `where -k` 84 | `where*` is equivalent to `where -a` 85 | 86 | ## Configuration 87 | 88 | ### Database location 89 | 90 | You can customize the location of the text file `where` uses with the 91 | environment variable `WHERE_FUNCTIONS_FROM_DB`. Set it before sourcing 92 | the script in `~/.bash_profile`: 93 | 94 | export WHERE_FUNCTIONS_FROM_DB=/new/path/filename 95 | 96 | ### Source hook 97 | 98 | To enable `where` to automatically index any file sourced in your 99 | configuration, set the `WHERE_HOOK_SOURCE` variable to true before 100 | sourcing `_where.bash` in `~/.bash_profile`: 101 | 102 | export WHERE_HOOK_SOURCE=true 103 | 104 | ### Database refresh throttling 105 | 106 | Set an expiration threshold on the database with `WHERE_EXPIRATION`. 107 | The threshold is in seconds, where one hour is 3600. If `where` is 108 | initialized within the threshold since last update, it won't index 109 | the files again. 110 | 111 | export WHERE_EXPIRATION=3600 112 | 113 | You can force a database refresh with `_where_reset` on the command line. This will clear your database and set an update marker for the current time. 114 | -------------------------------------------------------------------------------- /_where.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | source "${BASH_SOURCE%/*}/common.bash" 3 | # where 1.0.5 by Brett Terpstra 2015, WTF license 4 | 5 | #### Description 6 | # For people who spread bash functions and aliases across multiple sourced 7 | # files and then can't track down where they defined them 8 | 9 | #### Installation 10 | # 11 | ## Option 1: Hook source 12 | # 13 | # This option will hook the Bash default `source` command and index shell 14 | # scripts whenenver they're sourced from another file. 15 | # 16 | # Add the following to your .bash_profile before any 17 | # source commands are run: 18 | # 19 | # export WHERE_HOOK_SOURCE=true 20 | # source /path/to/_where.bash 21 | # 22 | # If using this option, see the WHERE_EXPIRATION configuration option. 23 | # 24 | ## Option 2: 25 | # 1. Source _where.sh in your .bash_profile prior to sourcing other files 26 | # 27 | # source /path/to/_where.bash 28 | # 29 | # 2. Add the following to the bottom of specific files to be indexed: 30 | # 31 | # _where_from $BASH_SOURCE 32 | # 33 | # Indexing every file you source can slow down login, so option 2 may be ideal. 34 | # You can add the necessary line to every file in a folder and subfolders 35 | # using the _where_add function: 36 | # 37 | # $ _where_add ~/scripts{,/**}/*.{,ba}sh 38 | # 39 | # Remove the lines using the _where_clean function: 40 | # 41 | # $ _where_clean ~/scripts{,/**}/*.{,ba}sh 42 | # 43 | #### Usage 44 | # 45 | # where function_name 46 | # 47 | # Once the database is built, you can use the `where` command to find your 48 | # functions. Running `where` with no arguments will output a list of all 49 | # registered plugins and aliases. 50 | # 51 | # Add an argument to filter for a specific function or alias. By default 52 | # only exact matches will return. If an exact match is found, just the 53 | # file path of the originating script will be returned. 54 | # 55 | ## Options 56 | # 57 | # -k Show all functions and aliases containing filter text 58 | # -a Show all functions and aliases fuzzy matched 59 | # -h Show this screen 60 | # -v Verbose output 61 | # -n Suppress line number in paths 62 | # -E Edit result 63 | # 64 | # The -k switch turns on "apropos" mode, which lets you find any function 65 | # containing the filter string in its name. 66 | # 67 | # The -a switch takes "apropos" a step further, using the filter argument 68 | # as a fuzzy search string. It will match any functions/arguments containing 69 | # the characters in the search string in sequence, but they do not need 70 | # to be contiguous. 71 | # 72 | # If -a is specified, -k is ignored in the arguments. 73 | # 74 | # -E causes $EDITOR to be opened with the path to the file containing 75 | # the searched function. -E does not work with -k or -a. 76 | # 77 | ## Aliases 78 | # 79 | # `where?` is equivalent to `where -k` 80 | # `where*` is equivalent to `where -a` 81 | # 82 | #### Configuration 83 | # 84 | ## Database location 85 | # 86 | # You can customize the location of the text file `where` uses with the 87 | # environment variable $WHERE_FUNCTIONS_FROM_DB. Set it before sourcing 88 | # the script in ~/.bash_profile: 89 | # 90 | # export WHERE_FUNCTIONS_FROM_DB=/new/path/filename 91 | # 92 | ## Source hook 93 | # 94 | # To enable `where` to automatically index any file sourced in your 95 | # configuration, set the WHERE_HOOK_SOURCE variable to true before 96 | # sourcing _where.bash in ~/.bash_profile: 97 | # 98 | # export WHERE_HOOK_SOURCE=true 99 | # 100 | ## Database refresh throttling 101 | # 102 | # Set an expiration threshhold on the database with WHERE_EXPIRATION. 103 | # The threshhold is in seconds, where one day is 3600. If `where` is 104 | # initialized within the thresshold since last update, it won't index 105 | # the files again. 106 | # 107 | # export WHERE_EXPIRATION=3600 108 | # 109 | #### End 110 | 111 | _where_debug() { 112 | "${DEBUG:-false}" && __color_out "%b_white%where: %purple%$*" 113 | } 114 | 115 | _where_updated() { 116 | awk '/^[0-9]+$/{print}' "$WHERE_FUNCTIONS_FROM_DB" 117 | } 118 | 119 | # Check the last index date, only update based on WHERE_EXPIRATION 120 | _where_db_fresh() { 121 | if [[ ! -e $WHERE_FUNCTIONS_FROM_DB || $(( $(wc -l < "$WHERE_FUNCTIONS_FROM_DB")<=1 )) == 1 || ${WHERE_EXPIRATION:-0} == 0 ]]; then 122 | _where_debug "no database, no expiration set, or expiration 0" 123 | WHERE_DB_EXPIRED=true 124 | return 1 125 | fi 126 | local last_update=$(_where_updated) 127 | if [[ $last_update == "" ]]; then 128 | _where_debug "No timestamp in index" 129 | WHERE_DB_EXPIRED=true 130 | return 1 131 | fi 132 | 133 | _where_debug "last update: `date -r $last_update`" 134 | _where_debug "time since update: $(( $(date '+%s')-$last_update ))" 135 | if [ $(( $(date '+%s')-$last_update )) -ge ${WHERE_EXPIRATION:-0} ]; then 136 | _where_debug "%red%Expired (threshhold ${WHERE_EXPIRATION:-})" 137 | WHERE_DB_EXPIRED=true 138 | return 1 139 | fi 140 | WHERE_DB_EXPIRED=false 141 | _where_debug "%green%database fresh" 142 | return 0 143 | } 144 | 145 | _where_reset() { 146 | local dbtmp 147 | if [[ ${1:-soft} == "hard" ]]; then 148 | __color_out "%b_white%where: %b_red%Clearing function index" 149 | : > "$WHERE_FUNCTIONS_FROM_DB" 150 | else 151 | __color_out "%b_white%where: %b_red%Resetting function index" 152 | dbtmp="$(mktemp -t WHERE_DB.XXXXXX)" || return 153 | trap "rm -f -- '$dbtmp'" RETURN 154 | awk '!/^[0-9]+$/{print}' "$WHERE_FUNCTIONS_FROM_DB" > "$dbtmp" 155 | mv -f "$dbtmp" "$WHERE_FUNCTIONS_FROM_DB" 156 | trap - RETURN 157 | fi 158 | } 159 | 160 | _where_set_update() { 161 | local dbtmp 162 | dbtmp="$(mktemp -t WHERE_DB.XXXXXX)" || return 163 | trap "rm -f -- '$dbtmp'" RETURN 164 | date '+%s' > "$dbtmp" 165 | awk '!/^[0-9]+$/{print}' "$WHERE_FUNCTIONS_FROM_DB" >> "$dbtmp" 166 | mv -f "$dbtmp" "$WHERE_FUNCTIONS_FROM_DB" 167 | trap - RETURN 168 | } 169 | 170 | # Convert a string into a fuzzy-match regular expression for _where 171 | # Separates each character and adds ".*" after, removing spaces 172 | # @param 1: (Required) string to convert 173 | _where_to_regex () 174 | { 175 | _regex_escape "$*" | sed -E 's/([[:alnum:]]) */\1[^:]*/g' 176 | } 177 | 178 | # "where" database function 179 | # Parses for function and alias definitions to add to text list 180 | # Existing definitions with same name are replaced 181 | # Database file index format: 182 | # func_or_alias_name:(function|alias):path_to_source 183 | # @param 1: (Required) single file path to parse and index 184 | _where_from() { 185 | local needle dbtmp 186 | local srcfile=${1:-} 187 | 188 | [[ ! -e $srcfile ]] && return 1 189 | touch "$WHERE_FUNCTIONS_FROM_DB" 190 | >&2 __color_out -n "\033[K%white%Indexing %red%$1...\r" 191 | # create a temp file and clean on return 192 | dbtmp="$(mktemp -t WHERE_DB.XXXXXX)" || return 193 | trap "rm -f -- '$dbtmp'" RETURN 194 | 195 | IFS=$'\n' cat "$srcfile" | awk '/^(function )?[_[:alnum:]]+ *\(\)/{gsub(/(function | *\(.+)/,"");print $1":"NR}' | while read f 196 | do 197 | declare -a farr=( $(echo $f|sed -E 's/:/ /g') ) 198 | 199 | needle=$(_regex_escape ${farr[0]}) 200 | grep -vE "^$needle:" "$WHERE_FUNCTIONS_FROM_DB" > "$dbtmp" 201 | echo "${farr[0]}:function:$srcfile:${farr[1]}" >> "$dbtmp" 202 | sort -u "$dbtmp" -o "$WHERE_FUNCTIONS_FROM_DB" 203 | done 204 | 205 | IFS=$'\n' cat "$srcfile" | awk '/^alias/{gsub(/(^\s*alias |=.*$)/,"");print $1":"NR}' | while read f 206 | do 207 | declare -a farr=( $(echo $f|sed -E 's/:/ /g') ) 208 | 209 | needle=$(_regex_escape ${farr[0]}) 210 | grep -vE "^$needle:" "$WHERE_FUNCTIONS_FROM_DB" > "$dbtmp" 211 | echo "${farr[0]}:alias:$srcfile:${farr[1]}" >> "$dbtmp" 212 | sort -u "$dbtmp" -o "$WHERE_FUNCTIONS_FROM_DB" 213 | done 214 | 215 | rm -f -- "$dbtmp" 216 | trap - RETURN 217 | >&2 echo -ne "\033[K" 218 | _where_set_update 219 | } 220 | 221 | # Filter to ouput columnar "where" query results, in color if supported 222 | # Takes input from STDIN, no arguments 223 | _where_results_color() { 224 | if which tput > /dev/null 2>&1 && [[ $(tput -T$TERM colors) -ge 8 ]]; then 225 | cat | awk -F ':' '{sub(/^/,"\033[33m",$1);sub(/^/,"\033[31m",$2);sub(/\/Users\/[^\/]*/,"~",$3);sub(/^/,"\033[1;37m",$3);print $1"|"$2"|"$3"\033[1;36m:"$4"\033[0m"}' | column -s '|' -t 226 | else 227 | cat | column -s ' ' -t 228 | fi 229 | } 230 | 231 | # Main "where" function for querying database 232 | # @argument 1: (optional) string to filter results 233 | # @option: [-k] apropos, any entry name containing exact filter string 234 | # @option: [-a] fuzzy filter, any entry name containing characters from 235 | # filter string in sequence but not required to be contiguous 236 | # -a overrides -k 237 | # @option: [-v] verbose output 238 | # @option: [-n] suppress line numbers 239 | # No filter string shows full index 240 | # Filter string with no option (-k, -a) shows exact name matches only 241 | where() { 242 | local needle cmd_type res res_array lnum 243 | local fuzzy=false 244 | local apropos=false 245 | local verbose=false 246 | local edit=false 247 | local linenumbers=true 248 | IFS='' read -r -d '' helpstring <<'ENDHELPSTRING' 249 | where: Show where a function or alias is originally defined 250 | Use -h for help 251 | ENDHELPSTRING 252 | 253 | IFS='' read -r -d '' helpoptions <<'ENDOPTIONSHELP' 254 | where: Show where a function or alias is originally defined 255 | %b_white%where [-kav] [function_name|search_pattern] 256 | %yellow%Options: 257 | %b_white%-k %yellow%Show all functions and aliases containing filter text 258 | %b_white%-a %yellow%Show all functions and aliases fuzzy matched 259 | %b_white%-h %yellow%Show this screen 260 | %b_white%-v %yellow%Verbose output 261 | %b_white%-E %yellow%Edit result 262 | %b_white%-n %yellow%Suppress line number in paths 263 | 264 | ENDOPTIONSHELP 265 | 266 | if [ $# == 0 ]; then 267 | awk '!/^[0-9]+$/{print}' "$WHERE_FUNCTIONS_FROM_DB" | _where_results_color 268 | # cat "$WHERE_FUNCTIONS_FROM_DB" | _where_results_color 269 | return 0 270 | fi 271 | 272 | DEBUG=false 273 | local OPTIND=1 274 | while getopts "kahdvEn" opt; do 275 | case $opt in 276 | h) __color_out "$helpoptions"; return;; 277 | k) apropos=true ;; 278 | a) fuzzy=true ;; 279 | d) DEBUG=true ;; 280 | v) verbose=true ;; 281 | E) edit=true ;; 282 | n) linenumbers=false ;; 283 | *) 284 | __color_out "$helpstring" >&2 285 | return 1 286 | esac 287 | done 288 | shift $((OPTIND-1)) 289 | 290 | if $fuzzy; then 291 | needle="^[^:]*$(_where_to_regex $1)[^:]*:" 292 | elif $apropos; then 293 | needle="^[^:]*$1[^:]*:" 294 | else 295 | cmd_type=$(builtin type -t $1) 296 | if [[ $cmd_type != "function" && $cmd_type != "alias" ]]; then 297 | _where_fallback $1 298 | return $? 299 | else 300 | needle=$1 301 | fi 302 | fi 303 | 304 | _where_debug "Searching for '$needle'" 305 | 306 | if $fuzzy || $apropos; then 307 | if [[ $(grep -Ec $needle $WHERE_FUNCTIONS_FROM_DB) > 0 ]]; then 308 | grep -E $needle $WHERE_FUNCTIONS_FROM_DB | _where_results_color 309 | return 0 310 | fi 311 | else 312 | if [[ $(grep -Ec "^$needle:" $WHERE_FUNCTIONS_FROM_DB) > 0 ]]; then 313 | res=$(grep -E "^$needle:" $WHERE_FUNCTIONS_FROM_DB) 314 | declare -a res_array=( $(echo $res|sed -E 's/:/ /g') ) 315 | # __color_out "%yellow%${res_array[1]} %b_white%${res_array[0]} %yellow%defined in: %b_white%${res_array[2]}" 316 | lnum="" 317 | if $verbose; then 318 | if $linenumbers; then lnum=" on line ${res_array[3]}"; fi 319 | echo "${res_array[1]} ${res_array} is defined in ${res_array[2]}$lnum" 320 | else 321 | if $linenumbers; then lnum=":${res_array[3]}"; fi 322 | echo "${res_array[2]}$lnum" 323 | fi 324 | if [[ $edit == true && ! -z $EDITOR ]]; then 325 | if $linenumbers; then lnum=":${res_array[3]}"; fi 326 | $EDITOR "${res_array[2]}$lnum" 327 | fi 328 | return 0 329 | fi 330 | fi 331 | 332 | _where_fallback $1 333 | } 334 | 335 | _where_fallback() { 336 | local res pre 337 | local cmd_type=$(builtin type -t $1) 338 | if [[ $cmd_type == "" ]]; then 339 | __color_out "%red%No match found for %b_white%$1" 340 | return 1 341 | elif [[ $cmd_type == "function" ]]; then 342 | __color_out "%red%Function %b_white%$1 %red%not found in where's index" 343 | return 1 344 | elif [[ $cmd_type == "file" ]]; then 345 | pre="%yellow%File: %b_white%" 346 | res=$(builtin type -p $1 2> /dev/null) 347 | else 348 | pre="%yellow%Other: %b_white%" 349 | res=$(builtin type $1 2> /dev/null) 350 | fi 351 | 352 | if [[ $res != "" ]]; then 353 | __color_out "${pre}${res}" 354 | else 355 | return 1 356 | fi 357 | } 358 | 359 | # Util function to remove _where_from $BASH_SOURCE lines from specified files 360 | # Example: _where_clean ~/scripts{,/**}/*.{,ba}sh 361 | _where_clean() { 362 | for f in $@; do 363 | >&2 __color_out -n "%b_red%Cleaning %b_white%$f...\r" 364 | # safely create a temp file 365 | t=$(mktemp -t where_clean.XXXXXX) 366 | trap "rm -f -- '$t'" RETURN 367 | grep -v "^_where_from \$BASH_SOURCE" $f > $t 368 | mv -f "$t" "$f" 369 | trap - RETURN 370 | done 371 | >&2 echo -ne "\033[K" 372 | } 373 | 374 | # Util function to add lines to individual files to index them when sourced 375 | # Can be used instead of hooking builtin source to only index given files 376 | # Example: _where_add ~/scripts{,/**}/*.{,ba}sh 377 | _where_add() { 378 | for f in $@; do 379 | _where_clean $f 380 | >&2 __color_out -n "%b_green%Initializing %b_white%$f...\r" 381 | echo -e "\n_where_from \$BASH_SOURCE" >> $f 382 | done 383 | >&2 echo -ne "\033[K" 384 | } 385 | 386 | ## Initialization 387 | # If no WHERE_FUNCTIONS_FROM_DB env var is set, use default 388 | : "${WHERE_FUNCTIONS_FROM_DB:=${XDG_CACHE_HOME:-$HOME/.where_functions}${XDG_CACHE_HOME:+/where/functionsdb}}" 389 | 390 | if [[ "${BASH_SOURCE[0]}" != "$0" ]] 391 | then # We are being sourced. 392 | 393 | mkdir -p "${WHERE_FUNCTIONS_FROM_DB%/*}" 394 | 395 | # Aliases for apropos and fuzzy search 396 | alias where?="where -k" 397 | alias where*="where -a" 398 | 399 | # hook source builtin to index source bash files 400 | if [[ ${WHERE_HOOK_SOURCE:-false} == true ]] 401 | then 402 | function source() { 403 | builtin source $@ 404 | [[ ${WHERE_HOOK_SOURCE:-} == true && $WHERE_DB_EXPIRED == true ]] || return 0 405 | 406 | for f in $@; do 407 | if [[ $f =~ \.(ba)?sh$ && $(grep -cE "^_where_from \$BASH_SOURCE" $f) == 0 ]]; then 408 | for f in $@; do 409 | _where_from $f 410 | done 411 | fi 412 | done 413 | } 414 | fi 415 | 416 | # If this is the first time _where has been sourced in this session, expire the db 417 | _where_db_fresh || _where_reset 418 | 419 | # Add functions from self to index 420 | _where_from "${BASH_SOURCE}" 421 | _where_from "${BASH_SOURCE%/*}/common.bash" 422 | else # We are the executing script. 423 | 424 | where "$@" 425 | fi 426 | -------------------------------------------------------------------------------- /common.bash: -------------------------------------------------------------------------------- 1 | # Shared util for color output using templates 2 | # Template format: 3 | # %colorname%: text following colored normal weight 4 | # %b% or %b_colorname%: bold foreground 5 | # %u% or %u_colorname%: underline foreground 6 | # %s% or %s_colorname%: bold foreground and background 7 | # %n% or %n_colorname%: return to normal weight 8 | # %reset%: reset foreground and background to default 9 | # 10 | # %reset% is only used within a string, it will automatically 11 | # be sent after any output 12 | # Color names (prefix bg to set background): 13 | # black 14 | # red 15 | # green 16 | # yellow 17 | # cyan 18 | # purple 19 | # blue 20 | # white 21 | # @option: -n no newline 22 | # @param 1: (Required) template string to process and output 23 | __color_out () { 24 | local newline="" 25 | OPTIND=1 26 | while getopts "n" opt; do 27 | case $opt in 28 | n) newline="n";; 29 | esac 30 | done 31 | shift $((OPTIND-1)) 32 | # color output variables 33 | if which tput > /dev/null 2>&1 && [[ $(tput -T$TERM colors) -ge 8 ]]; then 34 | local _c_n="\033[0m" 35 | local _c_b="\033[1m" 36 | local _c_u="\033[4m" 37 | local _c_s="\033[7m" 38 | local _c_black="\033[30m" 39 | local _c_red="\033[31m" 40 | local _c_green="\033[32m" 41 | local _c_yellow="\033[33m" 42 | local _c_cyan="\033[34m" 43 | local _c_purple="\033[35m" 44 | local _c_blue="\033[36m" 45 | local _c_white="\033[37m" 46 | local _c_bgblack="\033[40m" 47 | local _c_bgred="\033[41m" 48 | local _c_bggreen="\033[42m" 49 | local _c_bgyellow="\033[43m" 50 | local _c_bgcyan="\033[44m" 51 | local _c_bgpurple="\033[45m" 52 | local _c_bgblue="\033[46m" 53 | local _c_bgwhite="\033[47m" 54 | local _c_reset="\033[0m" 55 | fi 56 | local template_str="echo -e${newline} \"$(echo -en "$@" \ 57 | | sed -E 's/%([busn])_/${_c_\1}%/g' \ 58 | | sed -E 's/%(bg)?(b|u|s|n|black|red|green|yellow|cyan|purple|blue|white|reset)%/${_c_\1\2}/g')$_c_reset\"" 59 | 60 | eval "$template_str" 61 | } 62 | 63 | # Shared util to return full path for single file, including symlink resolution 64 | # @param 1: (Required) single file path to resolve 65 | _resolvedpath () { 66 | local p resolved 67 | if [[ "${1}" == \~*/* || "${1}" == \~ || "${1}" == \~/* || "${1}" == \~/ ]]; then 68 | p="${HOME}${1:1:${#1}}" 69 | else 70 | p="${1}" 71 | fi 72 | resolved=$(/usr/bin/env python -c 'import os;print os.path.realpath("'"$p"'");') 73 | echo $resolved 74 | } 75 | 76 | # Shared util to return absolute path for single file 77 | # @param 1: (Required) single file path to resolve 78 | _fullpath () { 79 | echo "$(cd "$(dirname "$1")"; pwd)/$(basename "$1")" 80 | } 81 | 82 | # Shared util function to escape a string for a regular expression 83 | # Backslashes $ ^ . * ? () {} [] 84 | # @param 1: (Required) string to escape 85 | _regex_escape () 86 | { 87 | echo -n "$@" | sed -E 's/([$^*?().{}]|\[|\])/\\\1/g' 88 | } 89 | --------------------------------------------------------------------------------