Created
October 30, 2010 05:14
-
-
Save echristopherson/654982 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Some bash utility functions (and associated aliases) that can be convenient to | |
# have. Tested with 3.2.48(1)-release on Mac OS X 10.6. | |
# Add pathname to start or end of a path; take out existing copies first. | |
# Unfortunately, this doesn't handle weird cases like pathnames with : in | |
# them. A little experimentation shows that bash doesn't recognize those | |
# anyway. | |
add_to_path() { | |
local append_to | |
case "$1" in | |
--end|-e) | |
append_to='end' | |
shift | |
;; | |
--start|-s) | |
append_to='start' | |
shift | |
;; | |
esac | |
local path_in | |
local dir_to_add | |
# Apparently, you can't pass empty positional parameters to a bash | |
# function; this hack works around that. | |
if [ "$#" = '1' ]; then | |
path_in='' | |
dir_to_add="$1" | |
else | |
path_in="$1" | |
dir_to_add="$2" | |
fi | |
# If path_in is empty, return path to add. | |
if [ "$path_in" = '' ]; then | |
echo "$dir_to_add" | |
# Nothing left to do; no point in falling through. | |
return 0 | |
fi | |
# String matching and replacing is a bashism. | |
# If dir is only entry in path_in, keep it as is. | |
if [ "$path_in" = "$dir_to_add" ]; then | |
# Nothing left to do; no point in falling through. | |
echo "$path_in" | |
return 0 | |
fi | |
IFS=: read -r -a paths <<< "$path_in" | |
num_elements="${#paths[@]}" | |
for index in "${!paths[@]}"; do | |
# If dir is already in path, remove it | |
if [ "$dir_to_add" = "${paths[$index]}" ]; then | |
unset paths["$index"] | |
fi | |
done | |
case "$append_to" in | |
end) | |
paths+=("$dir_to_add") | |
;; | |
start|*) | |
paths=("$dir_to_add" "${paths[@]}") | |
;; | |
esac | |
IFS=':' | |
echo "${paths[*]}" | |
return 0 | |
} | |
# Shortcut for adding to start of $PATH | |
add_to_PATH_start() { | |
PATH="$(add_to_path --start "$PATH" "$1")" | |
} | |
# Shortcut for adding to end of $PATH | |
add_to_PATH_end() { | |
PATH="$(add_to_path --end "$PATH" "$1")" | |
} | |
# List one component of path per line, from top to bottom. | |
show_path() { | |
local path="$1" | |
path="${path//:/ | |
}" | |
echo "$path" | |
return 0 | |
} | |
alias showpath='show_path "$PATH"' | |
# Find and list (with ls) either directories or executables in given directory | |
# (or current directory). Easily extensible to other sorts of searches. | |
# This function is intended to be invoked by aliases. To invoke it directly, | |
# you must pass either "--dir" or "--exe" as its first parameter. | |
# | |
# The following parameters should be whatever you would pass to ls; the | |
# function currently accepts all options of Mac OS X's (and thus BSD's) ls that | |
# I could find in the man page. Some might behave idiosyncraticly, however. | |
# | |
# In addition, there is a switch -Z which causes all the found | |
# files/directories to be listed with ls together, which allows the listing to | |
# be sorted and formatted consistently. However, that output only appears after | |
# all the files/directories have been found, so you don't get instant | |
# gratification with a recursive search of a deep directory hierarchy. | |
# | |
# Thanks to nDuff on Freenode #bash for the help. | |
findls() { | |
local find_pattern | |
case "$1" in | |
--dir*) | |
find_pattern=(-type d) | |
;; | |
--exe*) | |
find_pattern=(-type f -perm +uog+x) | |
;; | |
*) | |
echo "You must pass either --dir or --exe to findls." 1>&2 | |
return 1 | |
;; | |
esac | |
shift | |
OPTIND=1 | |
# TODO: Parse these options from output of alias command | |
local lsopts=(-G -F) | |
local should_recurse=0 | |
local should_list_dot_files=0 | |
local should_list_dot_and_dotdot=0 | |
local should_descend=1 | |
local should_collect=0 | |
while getopts :@1AaBbCcdeFfGgHhikLlmnOoPpqRrSsTtUuvWwxZ arg; do | |
#echo "Found -$arg" | |
case "$arg" in | |
R) | |
should_recurse=1 | |
;; | |
a) | |
should_list_dot_files=1 | |
;; | |
A) | |
should_list_dot_files=1 | |
should_list_dot_and_dotdot=1 | |
;; | |
d) | |
# TODO: Implement | |
should_descend=0 | |
;; | |
h) | |
# TODO: help text | |
return 0 | |
;; | |
@|1|B|b|C|c|d|e|F|f|G|g|H|i|k|L|l|m|n|O|o|P|p|q|r|S|s|T|t|U|u|v|W|w|x) | |
lsopts+=(-$arg) | |
;; | |
# Option that lets us "collect" output and use ls to display it all at | |
# once at the end. This means it will be formatted consistently, and we | |
# can use ls's sort options. Unfortunately, it forces us to wait until | |
# all of find's output is collected before displaying it, so it doesn't | |
# work well recursively on deep directory hierarchies. | |
Z) | |
should_collect=1 | |
;; | |
esac | |
# TODO: Allow double-dash ls options (we'll have to go beyond getopts | |
# for that.) | |
done | |
shift $((OPTIND - 1)) | |
if [ "$should_list_dot_files" != '1' ]; then | |
find_pattern+=(-a ! -name '.*') | |
fi | |
if [ "$should_list_dot_and_dotdot" != '1' ]; then | |
find_pattern+=(-a ! -name '.' -a ! -name '..') | |
fi | |
if [ "$should_recurse" != '1' ]; then | |
find_pattern+=(-maxdepth 1) | |
fi | |
#echo "find_pattern[@]: ${find_pattern[@]}" | |
# Construct array of supplied dir arguments | |
local dirs=() | |
if [ "$1" = '' ]; then | |
dirs+=('.') | |
else | |
dirs=("$@") | |
fi | |
# TODO: Allow arguments to refer to files/directories *within* a | |
# directory, like ls does. This would have something to do with the -d | |
# option. | |
# The actual work | |
local num_dirs="${#dirs[@]}" | |
for dir in "${dirs[@]}"; do | |
if [ "$num_dirs" -gt '1' ]; then | |
echo "$dir:" | |
fi | |
#echo "Entering directory '$dir'" | |
pushd . > /dev/null | |
cd "$dir" | |
if [ "$should_collect" = '1' ]; then | |
local found_filenames=() | |
while IFS= read -r -d $'\0' filename; do | |
# Strip off initial './' | |
filename="$(echo "${filename/#.\//}")" | |
found_filenames+=("$filename") | |
done < <(find . "${find_pattern[@]}" -print0) | |
# the above < <() is file redirection + process substitution | |
ls -d "${lsopts[@]}" "${found_filenames[@]}" | |
else | |
# TODO: Strip off initial "./" | |
find . "${find_pattern[@]}" -exec ls -d "${lsopts[@]}" {} + | |
#strip_initial_dotslash($output) | |
fi | |
popd > /dev/null | |
if [ "$num_dirs" -gt '1' ]; then | |
echo | |
fi | |
done | |
} | |
# List subdirectories | |
alias lsdirs='findls --dir' | |
# List executables | |
alias lsexes='findls --exe' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment