├── 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 |
--------------------------------------------------------------------------------