3 # This defines some general, useful functions.
5 #hmmm: starting to get a bit beefy in here. perhaps there is a good way to refactor the functions into more specific folders, if they aren't really totally general purpose?
9 # test whether we've been here before or not.
11 type function_sentinel &>/dev/null
13 # there was no error, so we can skip the inits.
14 if [ ! -z "$DEBUG_FEISTY_MEOW" ]; then
15 echo "skipping function definitions, because already defined."
22 if [ -z "$skip_all" ]; then
24 if [ ! -z "$DEBUG_FEISTY_MEOW" ]; then
25 echo "feisty meow function definitions beginning now..."
28 # a handy little method that can be used for date strings. it was getting
29 # really tiresome how many different ways the script did the date formatting.
30 function date_stringer() {
32 if [ -z "$sep" ]; then sep='_'; fi
33 date +"%Y$sep%m$sep%d$sep%H%M$sep%S" | tr -d '/\n/'
36 # a slightly different but also handy time and date function. this is
37 # intended for prefixing on log lines, so that each line has the time it
38 # occurred as the first element.
39 function timestamper() {
40 date +"[%Y-%m-%d %H:%M:%S] " | tr -d '/\n/'
43 # a wrapper for the which command that finds items on the path. some OSes
44 # do not provide which, so we want to not be spewing errors when that
48 local to_find="$1"; shift
49 local WHICHER="$(/usr/bin/which which 2>/dev/null)"
50 #>&2 echo "got whicher as: $WHICHER"
52 # there is no which command here. we produce nothing due to this.
56 local sporkenz # must be defined local here, before call, or we don't get exit value?!
57 sporkenz=$($WHICHER "$to_find" 2>/dev/null)
58 #>&2 echo "broken with this line, but here is exit val: $?"
60 #>&2 echo "got whicher as: $WHICHER"
65 # makes a directory of the name specified and then tries to change the
66 # current directory to that directory.
68 if [ ! -d "$1" ]; then mkdir -p "$1"; fi
72 # returns true if the variable is an array.
74 [[ "$(declare -p $1)" =~ "declare -a" ]]
77 # returns true if the name provided is a defined alias.
83 # makes the status of pipe number N (passed as first parameter) into the
84 # main return value (i.e., the value for $?). this is super handy to avoid
85 # repeating the awkward looking code below in multiple places.
86 # the numbering starts at zero, for the first item at the head of the pipe.
87 function promote_pipe_return()
89 ( exit ${PIPESTATUS[$1]} )
94 function fm_username()
96 # see if we can get the user name from the login name. oddly this sometimes doesn't work.
97 local custom_user="$(logname 2>/dev/null)"
98 if [ -z "$custom_user" ]; then
99 # try the normal unix user variable.
102 if [ -z "$custom_user" ]; then
103 # try the windows user variable.
104 custom_user="$USERNAME"
111 # displays the value of a variable in bash friendly format.
116 local varname="$1"; shift
117 if [ -z "$varname" ]; then
121 if is_alias "$varname"; then
122 #echo found $varname is alias
123 local tmpfile="$(mktemp $TMP/aliasout.XXXXXX)"
124 alias $varname | sed -e 's/.*=//' >$tmpfile
125 echo "alias $varname=$(cat $tmpfile)"
127 elif [ -z "${!varname}" ]; then
128 echo "$varname undefined"
130 if is_array "$varname"; then
131 #echo found $varname is array var
133 eval temparray="(\${$varname[@]})"
134 echo "$varname=(${temparray[@]})"
135 #hmmm: would be nice to print above with elements enclosed in quotes, so that we can properly
136 # see ones that have spaces in them.
138 #echo found $varname is simple
139 echo "$varname=${!varname}"
148 # when passed a list of things, this will return the unique items from that list as an echo.
151 # do the uniquification: split the space separated items into separate lines, then
152 # sort the list, then run the uniq tool on the list. results will be packed back onto
153 # one line when invoked like: local fredlist="$(uniquify a b c e d a e f a e d b)"
154 echo $* | tr ' ' '\n' | sort | uniq
157 # sets the variable in parameter 1 to the value in parameter 2, but only if
158 # that variable was undefined.
159 function set_var_if_undefined()
161 local var_name="$1"; shift
162 local var_value="$1"; shift
163 if [ -z "${!var_name}" ]; then
164 eval export $var_name="$var_value"
170 function success_sound()
172 if [ ! -z "$CLAM_FINISH_SOUND" ]; then
173 bash $FEISTY_MEOW_SCRIPTS/multimedia/sound_play.sh "$CLAM_FINISH_SOUND"
177 function error_sound()
179 if [ ! -z "$CLAM_ERROR_SOUND" ]; then
180 bash $FEISTY_MEOW_SCRIPTS/multimedia/sound_play.sh "$CLAM_ERROR_SOUND"
186 # echoes the maximum number of columns that the terminal supports. usually
187 # anything you print to the terminal with length less than (but not equal to)
188 # maxcols will never wrap.
189 function get_maxcols()
191 # calculate the number of columsn in the terminal.
192 local cols=$(stty size | awk '{print $2}')
198 # checks the result of the last command that was run, and if that failed,
199 # then this complains and exits from bash. the function parameters are
200 # used as the message to print as a complaint.
201 function exit_on_error()
203 if [ $? -ne 0 ]; then
204 echo -e "\n\nan important action failed and this script will stop:\n\n$*\n\n*** Exiting script..."
210 # like exit_on_error, but will keep going after complaining.
211 function continue_on_error()
213 if [ $? -ne 0 ]; then
214 echo -e "\n\na problem occurred, but we can continue:\n\n$*\n\n=> Continuing script..."
221 # accepts any number of arguments and outputs them to the feisty meow event log.
222 function log_feisty_meow_event()
224 echo -e "$(timestamper)-- ${USER}@$(hostname): $*" >> "$FEISTY_MEOW_EVENT_LOG"
229 # wraps secure shell with some parameters we like, most importantly to enable X forwarding.
233 save_terminal_title # remember the current terminal title.
234 /usr/bin/ssh -C "${args[@]}"
235 #hmmm: removed -Y flag because considered dangerous to trust remote hosts to not abuse our X session.
236 restore_terminal_title
239 # this version of ssh preserves the use of the -Y flag for when X forwarding is needed.
243 save_terminal_title # remember the current terminal title.
244 /usr/bin/ssh -Y "${args[@]}"
245 restore_terminal_title
250 # locates a process given a search pattern to match in the process list.
252 # + the -u flag specifies a user name, e.g. "-u joe", which causes only
253 # the processes of that user "joe" to be considered.
255 # + the -x flag specifies a pattern to exclude from the list, e.g. "-x pszap.sh"
256 # would ignore any processes that mention the phrase "pszap.sh".
259 # default user flag is for all users.
260 local excluder="ScrengeflebbitsAPhraseWeNeverExpecttomatchanythingYO298238"
261 # for our default, pick an exclusion string we would never match.
264 while [ $found_flag -eq 1 ]; do
265 # reset our sentinel now that we're safely in our loop.
268 # save the first argument, since we're going to shift the args.
270 if [ "$arg1" == "-u" ]; then
271 # handle the user flag.
273 #echo "found a -u parm and user=$2"
274 found_flag=1 # signal that we found one.
275 # skip these two arguments, since we've consumed them.
278 elif [ "$arg1" == "-x" ]; then
279 # handle the exclusion flag.
281 #echo "found a -x parm and excluder=$excluder"
282 found_flag=1 # signal that we found one.
283 # skip these two arguments, since we've consumed them.
289 # now that we've yanked any flags out, we can pull the rest of the
290 # arguments in as patterns to seek in the process list.
291 local -a patterns=("${@}")
293 #echo patterns list is: "${patterns[@]}"
296 local PID_DUMP="$(mktemp "$TMP/zz_pidlist.XXXXXX")"
299 if [ "$OS" == "Windows_NT" ]; then
300 # gets cygwin's (god awful) ps to show windoze processes also.
301 local EXTRA_DOZER_FLAGS="-W"
302 # pattern to use for peeling off the process numbers.
303 # local pid_finder_cmd="awk -- '{ print \$4; }'"
306 # flags which clean up the process listing output on unixes.
307 # apparently cygwin doesn't count as a type of unix, because their
308 # crummy specialized ps command doesn't support normal ps flags.
309 local EXTRA_UNIX_FLAGS="-o pid,args"
310 # pattern to use for peeling off the process numbers.
311 # local pid_finder_cmd="sed -n -e \\'s/^[[:space:]]*\([0-9][0-9]*\).*$/\\\\1/p\\'"
312 #echo pidfinder: $pid_finder_cmd
313 # local pid_finder_cmd="awk -- '{ print \$1; }'"
317 /bin/ps $EXTRA_DOZER_FLAGS $EXTRA_UNIX_FLAGS $user_flag | tail -n +2 >$PID_DUMP
319 #echo got all this stuff in the pid dump file:
323 # search for the pattern the user wants to find, and just pluck the process
324 # ids out of the results.
326 for i in "${patterns[@]}"; do
327 #echo "pattern curr is '$i'"
328 PIDS_SOUGHT+=($(cat $PID_DUMP \
330 | grep -v "$excluder" \
331 | awk -- "{ print \$${field_number}; }" ))
334 #echo pids sought list became:
335 #echo "${PIDS_SOUGHT[@]}"
338 if [ ${#PIDS_SOUGHT[*]} -ne 0 ]; then
339 local PIDS_SOUGHT2=$(printf -- '%s\n' ${PIDS_SOUGHT[@]} | sort | uniq)
341 PIDS_SOUGHT=${PIDS_SOUGHT2[*]}
342 echo ${PIDS_SOUGHT[*]}
347 # finds all processes matching the pattern specified and shows their full
348 # process listing (whereas psfind just lists process ids).
351 echo "psa finds processes by pattern, but there was no pattern on the command line."
354 local -a patterns=("${@}")
355 p=$(psfind "${patterns[@]}")
361 if [ "${patterns[0]}" == "-u" ]; then
362 # void the two elements with that user flag so we don't use them as patterns.
363 unset patterns[0] patterns[1]=
367 echo "Processes matching ${patterns[@]}..."
369 if [ -n "$IS_DARWIN" ]; then
372 # only print the header the first time.
373 if [ -z "$fuzil_sentinel" ]; then
376 ps $i -w -u | sed -e '1d'
381 # cases besides mac os x's darwin.
382 if [ "$OS" == "Windows_NT" ]; then
383 # special case for windows.
386 #hmmm: currently not working right for windows cygwin. we're getting proper
387 # winpids out of the list now, but not able to use them in ps?
388 # should i be keeping the weirdo pid that we were getting in column 1 and
389 # use that, except when talking to taskkill?
390 # need further research.
391 ps -W -p $curr | tail -n +2
394 # normal OSes can handle a nice simple query.
402 #hmmm: holy crowbars, this is an old one. do we ever still have any need of it?
403 # an unfortunately similarly named function to the above 'ps' as in process
404 # methods, but this 'ps' stands for postscript. this takes a postscript file
405 # and converts it into pcl3 printer language and then ships it to the printer.
406 # this mostly makes sense for an environment where one's default printer is
407 # pcl. if the input postscript causes ghostscript to bomb out, there has been
408 # some good success running ps2ps on the input file and using the cleaned
409 # postscript file for printing.
410 function ps2pcl2lpr() {
412 gs -sDEVICE=pcl3 -sOutputFile=- -sPAPERSIZE=letter "$i" | lpr -l
418 #hmmm: ugly absolute path here.
420 restore_terminal_title
423 # switches from a /X/path form to an X:/ form. this also processes cygwin paths.
424 function unix_to_dos_path() {
425 # we usually remove dos slashes in favor of forward slashes.
427 if [[ ! "$OS" =~ ^[Ww][iI][nN] ]]; then
428 # fake this value for non-windows (non-cygwin) platforms.
431 # for cygwin, we must replace the /home/X path with an absolute one, since cygwin
432 # insists on the /home form instead of /c/cygwin/home being possible. this is
433 # super frustrating and nightmarish.
434 DOSSYHOME="$(cygpath -am "$HOME")"
437 # if [ ! -z "$SERIOUS_SLASH_TREATMENT" ]; then
438 # # unless this flag is set, in which case we force dos slashes.
439 # echo "$1" | sed -e "s?^$HOME?$DOSSYHOME?g" | sed -e 's/\\/\//g' | sed -e 's/\/cygdrive//' | sed -e 's/\/\([a-zA-Z]\)\/\(.*\)/\1:\/\2/' | sed -e 's/\//\\/g'
441 echo "$1" | sed -e "s?^$HOME?$DOSSYHOME?g" | sed -e 's/\\/\//g' | sed -e 's/\/cygdrive//' | sed -e 's/\/\([a-zA-Z]\)\/\(.*\)/\1:\/\2/'
445 # # switches from an X:/ form to a /cygdrive/X/path form. this is only useful
446 # # for the cygwin environment currently.
447 # function dos_to_unix_path() {
448 # # we always remove dos slashes in favor of forward slashes.
449 ##old: echo "$1" | sed -e 's/\\/\//g' | sed -e 's/\([a-zA-Z]\):\/\(.*\)/\/\1\/\2/'
450 # echo "$1" | sed -e 's/\\/\//g' | sed -e 's/\([a-zA-Z]\):\/\(.*\)/\/cygdrive\/\1\/\2/'
453 # returns a successful value (0) if this system is debian or ubuntu.
454 function debian_like() {
455 # decide if we think this is debian or ubuntu or a variant.
458 ! -z "$(grep -i debian /etc/issue)" -o \
459 ! -z "$(grep -i ubuntu /etc/issue)" -o \
460 ! -z "$(grep -i 'Pop._OS' /etc/issue)" \
461 ]; then echo 1; else echo 0; fi)
462 if [ $DEBIAN_LIKE -eq 1 ]; then
463 # success; this is debianish.
466 # this seems like some other OS.
471 # this function wraps the normal sudo by ensuring we replace the terminal
472 # label before we launch what they're passing to sudo. we also preserve
473 # specific variables that enable the main user's ssh credentials to still
474 # be relied on for ssh forwarding, even if the '-i' flag is passed to cause
475 # a fresh shell (which normally doesn't get the launching user's environment
480 # hoist our X authorization info in case environment is passed along;
481 # this can allow root to use our display to show X.org windows.
482 if [ -z "$IMPORTED_XAUTH" -a ! -z "$DISPLAY" ]; then
483 export IMPORTED_XAUTH="$(xauth list $DISPLAY | head -n 1 | awk '{print $3}')"
484 local REMOVE_IMP_XAUTH=true
487 # launch sudo with just the variables we want to reach the other side.
489 varmods+="OLD_HOME=$HOME "
490 if [ ! -z "$IMPORTED_XAUTH" ]; then varmods+="IMPORTED_XAUTH=$IMPORTED_XAUTH "; fi
491 if [ ! -z "$SSH_AUTH_SOCK" ]; then varmods+="SSH_AUTH_SOCK=$SSH_AUTH_SOCK "; fi
492 /usr/bin/sudo $varmods "$@"
495 # take the xauth info away again if it wasn't set already.
496 if [ ! -z "$REMOVE_IMP_XAUTH" ]; then
499 restore_terminal_title
503 # trashes the .#blah files that cvs and subversion leave behind when finding conflicts.
504 # this kind of assumes you've already checked them for any salient facts.
505 function clean_cvs_junk() {
507 find $i -follow -type f -iname ".#*" -exec perl $FEISTY_MEOW_SCRIPTS/files/safedel.pl {} ";"
511 # overlay for nechung binary so that we can complain less grossly about it when it's missing.
513 local wheres_nechung=$(whichable nechung)
514 if [ -z "$wheres_nechung" ]; then
515 echo "The nechung oracle program cannot be found. You may want to consider"
516 echo "rebuilding the feisty meow applications with this command:"
517 echo "bash $FEISTY_MEOW_SCRIPTS/generator/produce_feisty_meow.sh"
524 # recreates all the generated files that the feisty meow scripts use.
525 function regenerate() {
526 # do the bootstrapping process again.
528 echo "$(timestamper)regenerating feisty meow script environment."
529 bash $FEISTY_MEOW_SCRIPTS/core/reconfigure_feisty_meow.sh
531 # force a full reload by turning off sentinel variables and methods.
532 unset -v CORE_VARIABLES_LOADED FEISTY_MEOW_LOADING_DOCK USER_CUSTOMIZATIONS_LOADED \
534 unalias CORE_ALIASES_LOADED &>/dev/null
535 unset -f function_sentinel
537 # reuse the original path if we can.
538 if [ ! -z "$FEISTY_MEOW_ORIGINAL_PATH" ]; then
539 export PATH="$FEISTY_MEOW_ORIGINAL_PATH"
542 # reload feisty meow environment in current shell.
543 log_feisty_meow_event "reloading the feisty meow scripts for $USER in current shell."
544 source "$FEISTY_MEOW_SCRIPTS/core/launch_feisty_meow.sh"
545 # run nechung oracle to give user a new fortune.
547 restore_terminal_title
550 # merges a set of custom scripts into the feisty meow environment. can be
551 # passed a name to use as the custom scripts source folder (found on path
552 # $FEISTY_MEOW_SCRIPTS/customize/{name}), or it will try to guess the name
553 # by using the login name.
554 function recustomize()
556 local custom_user="$1"; shift
557 if [ -z "$custom_user" ]; then
558 # default to login name if there was no name provided.
559 custom_user="$(fm_username)"
560 # we do intend to use the login name here to get the login name and to ignore
561 # if the user has sudo root access; we don't want to provide a custom
564 # chop off any email address style formatting to leave just the name.
565 custom_user="$(echo "$custom_user" | cut -f1 -d'@')"
569 if [ ! -d "$FEISTY_MEOW_SCRIPTS/customize/$custom_user" ]; then
570 echo -e "the customization folder for '$custom_user' is missing:
572 $FEISTY_MEOW_SCRIPTS/customize/$custom_user
574 we will skip recustomization, but these other customizations are available:
576 # a little tr and sed magic to fix the carriage returns into commas.
577 local line="$(find $FEISTY_MEOW_SCRIPTS/customize -mindepth 1 -maxdepth 1 -type d -exec basename {} ';' | tr '\n' '&' | sed 's/&/, /g' | sed -e 's/, $//')"
578 # make the line feeds and carriage returns manageable with tr.
579 # convert the ampersand, our weird replacement for EOL, with a comma + space in sed.
580 # last touch with sed removes the last comma.
585 # recreate the feisty meow loading dock.
586 regenerate >/dev/null
588 # jump into the loading dock and make our custom link.
589 pushd "$FEISTY_MEOW_LOADING_DOCK" &>/dev/null
590 if [ -h custom ]; then
591 # there's an existing link, so remove it.
594 # make sure we cleaned up the area before we re-link.
595 if [ -h custom -o -d custom -o -f custom ]; then
597 Due to an over-abundance of caution, we are not going to remove an unexpected
598 'custom' object found in the file system. This object is located in the
599 feisty meow loading dock here: $(pwd)
600 And here is a description of the rogue 'custom' object:
604 If you are pretty sure that this is just a remnant of an older approach in
605 feisty meow, where we copied the custom directory rather than linking it
606 (and it most likely is just such a bit of cruft of that nature), then please
607 remove that old remnant 'custom' item, for example by saying:
608 /bin/rm -rf \"custom\" ; popd
609 Sorry for the interruption, but we want to make sure this removal wasn't
610 automatic if there is even a small amount of doubt about the issue."
614 # create the custom folder as a link to the customizations.
615 ln -s "$FEISTY_MEOW_SCRIPTS/customize/$custom_user" custom
619 # now take into account all the customizations by regenerating the feisty meow environment.
622 restore_terminal_title
625 # generates a random password where the first parameter is the number of characters
626 # in the password (default 20) and the second parameter specifies whether to use
627 # special characters (1) or not (0).
628 # found function at http://legroom.net/2010/05/06/bash-random-password-generator
629 function random_password()
631 [ "$2" == "0" ] && CHAR="[:alnum:]" || CHAR="[:graph:]"
632 cat /dev/urandom | tr -cd "$CHAR" | head -c ${1:-32}
636 function add_cygwin_drive_mounts() {
637 for i in c d e f g h q z ; do
638 #hmmm: improve this by not adding the link if already there, or if the drive is not valid.
639 ln -s /cygdrive/$i $i
643 ############################
645 # takes a file to modify, and then it will replace any occurrences of the
646 # pattern provided as the second parameter with the text in the third
648 function replace_pattern_in_file()
650 local file="$1"; shift
651 local pattern="$1"; shift
652 local replacement="$1"; shift
653 if [ -z "$file" -o -z "$pattern" -o -z "$replacement" ]; then
654 echo "replace_pattern_in_file: needs a filename, a pattern to replace, and the"
655 echo "text to replace that pattern with."
658 sed -i -e "s%$pattern%$replacement%g" "$file"
661 # similar to replace_pattern_in_file, but also will add the new value
662 # when the old one did not already exist in the file.
663 function replace_if_exists_or_add()
665 local file="$1"; shift
666 local phrase="$1"; shift
667 local replacement="$1"; shift
668 if [ -z "$file" -o ! -f "$file" -o -z "$phrase" -o -z "$replacement" ]; then
669 echo "replace_if_exists_or_add: needs a filename, a phrase to replace, and the"
670 echo "text to replace that phrase with."
673 grep "$phrase" "$file" >/dev/null
674 # replace if the phrase is there, otherwise add it.
675 if [ $? -eq 0 ]; then
676 replace_pattern_in_file "$file" "$phrase" "$replacement"
678 # this had better be the complete line.
679 echo "$replacement" >>"$file"
683 ############################
685 # finds a variable (first parameter) in a particular property file
686 # (second parameter). the expected format for the file is:
688 function seek_variable()
690 local find_var="$1"; shift
691 local file="$1"; shift
692 if [ -z "$find_var" -o -z "$file" -o ! -f "$file" ]; then
693 echo -e "seek_variable: needs two parameters, firstly a variable name, and\nsecondly a file where the variable's value will be sought." 1>&2
698 if [ ${#line} -eq 0 ]; then continue; fi
699 # split the line into the variable name and value.
700 IFS='=' read -a assignment <<< "$line"
701 local var="${assignment[0]}"
702 local value="${assignment[1]}"
703 if [ "${value:0:1}" == '"' ]; then
704 # assume the entry was in quotes and remove them.
705 value="${value:1:$((${#value} - 2))}"
707 if [ "$find_var" == "$var" ]; then
713 # finds a variable (first parameter) in a particular XML format file
714 # (second parameter). the expected format for the file is:
715 # ... name="varX" value="valueX" ...
716 function seek_variable_in_xml()
718 local find_var="$1"; shift
719 local file="$1"; shift
720 if [ -z "$find_var" -o -z "$file" -o ! -f "$file" ]; then
721 echo "seek_variable_in_xml: needs two parameters, firstly a variable name, and"
722 echo "secondly an XML file where the variable's value will be sought."
727 if [ ${#line} -eq 0 ]; then continue; fi
728 # process the line to make it more conventional looking.
729 line="$(echo "$line" | sed -e 's/.*name="\([^"]*\)" value="\([^"]*\)"/\1=\2/')"
730 # split the line into the variable name and value.
731 IFS='=' read -a assignment <<< "$line"
732 local var="${assignment[0]}"
733 local value="${assignment[1]}"
734 if [ "${value:0:1}" == '"' ]; then
735 # assume the entry was in quotes and remove them.
736 value="${value:1:$((${#value} - 2))}"
738 if [ "$find_var" == "$var" ]; then
744 ############################
746 # goes to a particular directory passed as parameter 1, and then removes all
747 # the parameters after that from that directory.
748 function push_whack_pop()
750 local dir="$1"; shift
751 pushd "$dir" &>/dev/null
752 if [ $? -ne 0 ]; then echo failed to enter dir--quitting.; fi
753 rm -rf $* &>/dev/null
754 if [ $? -ne 0 ]; then echo received a failure code when removing.; fi
760 # new breed of definer functions goes here. still in progress.
762 # defines an alias and remembers that this is a new or modified definition.
763 # if the feisty meow codebase is unloaded, then so are all the aliases that
765 function define_yeti_alias()
767 # if alias exists already, save old value for restore,
768 # otherwise save null value for restore,
769 # have to handle unaliasing if there was no prior value of one
771 # add alias name to a list of feisty defined aliases.
773 #hmmm: first implem, just do the alias and get that working...
782 #hmmm: this points to an extended functions file being needed; not all of these are core.
784 # displays documentation in "md" formatted files.
787 local file="$1"; shift
788 pandoc "$file" | lynx -stdin
793 # just shows a separator line for an 80 column console, or uses the first
794 # parameter as the number of columns to expect.
798 if [ -z "$count" ]; then
799 count=$(($COLUMNS - 1))
803 for ((i=0; i < $count; i++)); do
809 # alias for separator.
817 # count the number of sub-directories in a directory and echo the result.
818 function count_directories()
820 local subbydir="$1"; shift
821 numdirs="$(find "$subbydir" -mindepth 1 -maxdepth 1 -type d | wc -l)"
825 # takes a string and capitalizes just the first character. any capital letters in the remainder of
826 # the string are made lower case. the processed string is returned by an echo.
827 function capitalize_first_char()
829 local to_dromedary="$1"; shift
830 to_dromedary="$(tr '[:lower:]' '[:upper:]' <<< ${to_dromedary:0:1})$(tr '[:upper:]' '[:lower:]' <<< ${to_dromedary:1})"
834 # given a source path and a target path, this will make a symbolic link from
835 # the source to the destination, but only if the source actually exists.
836 function make_safe_link()
838 local src="$1"; shift
839 local target="$1"; shift
841 if [ -d "$src" ]; then
842 ln -s "$src" "$target"
843 exit_on_error "Creating symlink from '$src' to '$target'"
845 echo "Created symlink from '$src' to '$target'."
848 # pretty prints the json files provided as parameters.
849 function clean_json()
851 if [ -z "$*" ]; then return; fi
854 local file="$1"; shift
855 if [ -z "$file" ]; then break; fi
856 if [ ! -f "$file" ]; then "echo File '$file' does not exist."; continue; fi
857 temp_out="$TMP/$file.view"
858 cat "$file" | python -m json.tool > "$temp_out"
859 show_list+=($temp_out)
860 continue_on_error "pretty printing '$file'"
862 filedump "${show_list[@]}"
868 # only print our special headers or text fields.
872 grep -i "\"text\":\|^=.*" |
873 sed -e "s/\\\\r/$CR/g" -e "s/\\\\n/\\$LF/g"
878 # echoes the machine's hostname. can be used like so:
879 # local my_host=$(get_hostname)
880 function get_hostname()
882 # there used to be more variation in how to do this, but adopting mingw
883 # and cygwin tools really helped out.
884 local this_host=unknown
885 if [ "$OS" == "Windows_NT" ]; then
886 this_host=$(hostname)
887 elif [ ! -z "$(echo $MACHTYPE | grep apple)" ]; then
888 this_host=$(hostname)
889 elif [ ! -z "$(echo $MACHTYPE | grep suse)" ]; then
890 this_host=$(hostname --long)
891 elif [ -x "$(whichable hostname)" ]; then
892 this_host=$(hostname)
897 # makes sure that the provided "folder" is a directory and is writable.
898 function test_writeable()
900 local folder="$1"; shift
901 if [ ! -d "$folder" -o ! -w "$folder" ]; then return 1; fi
907 # given a filename and a string to seek and a number of lines, then this
908 # function will remove the first occurrence of a line in the file that
909 # matches the string, and it will also axe the next N lines as specified.
910 function create_chomped_copy_of_file()
912 local filename="$1"; shift
913 local seeker="$1"; shift
914 local numlines=$1; shift
916 #echo into create_chomped_copy...
917 #var filename seeker numlines
919 # make a backup first, oy.
920 \cp -f "$filename" "/tmp/$(basename ${filename}).bkup-${RANDOM}"
921 exit_on_error "backing up file: $filename"
923 # make a temp file to write to before we move file into place in bind.
924 local new_version="/tmp/$(basename ${filename}).bkup-${RANDOM}"
925 \rm -f "$new_version"
926 exit_on_error "cleaning out new version of file from: $new_version"
932 # don't bother looking at the lines if we're already in skip mode.
933 if [[ $skip_count == 0 ]]; then
934 # find the string they're seeking.
935 if [[ ! "$line" =~ .*${seeker}.* ]]; then
937 echo "$line" >> "$new_version"
939 # a match! start skipping. we will delete this line and the next N lines.
941 #echo first skip count is now $skip_count
945 # we're already skipping. let's keep going until we hit the limit.
947 #echo ongoing skip count is now $skip_count
948 if (( $skip_count > $numlines )); then
949 echo "Done skipping, and back to writing output file."
955 #echo file we created looks like this:
958 if [ ! -z "$found_any" ]; then
959 # put the file back into place under the original name.
960 \mv "$new_version" "$filename"
961 exit_on_error "moving the new version into place in: $filename"
963 # cannot always be considered an error, but we can at least gripe.
964 echo "Did not find any matches for seeker '$seeker' in file: $filename"
970 # space 'em all: fixes naming for all of the files of the appropriate types
971 # in the directories specified. we skip any file with a dot in front, to
972 # respect their hidden nature. currently the set of files we'll rename is
973 # very boutique; it's in this function, and just happens to be the types of
974 # files we work with a lot.
975 function spacemall() {
976 local -a dirs=("${@}")
977 if [ ${#dirs[@]} -eq 0 ]; then
981 local charnfile="$(mktemp $TMP/zz_charn.XXXXXX)"
982 #hmmm: any way to do the below more nicely or reusably?
983 #hmmm: yes! a variable with a list of files that are considered TEXT_FILE_EXTENSIONS or something like that.
984 #hmmm: yes continued! also a variable for BINARY_FILE_EXTENSIONS to avoid those, where we need to in other scripts.
985 #hmmm: wait, we actually have a mix here, since this is a renaming function and not a searching function; get it straight!
986 #hmmm: would the composition of those two types of extensions cover all the files i want to rename? they have to be "important".
987 find "${dirs[@]}" -follow -maxdepth 1 -mindepth 1 -type f -and -not -iname ".[a-zA-Z0-9]*" | \
989 "csv\|doc\|docx\|eml\|html\|ics\|jpeg\|jpg\|m4a\|mov\|mp3\|odp\|ods\|odt\|pdf\|png\|ppt\|pptx\|rtf\|txt\|vsd\|vsdx\|wav\|webp\|xls\|xlsx\|xml\|zip" | \
990 sed -e 's/^/"/' | sed -e 's/$/"/' | \
991 xargs bash "$FEISTY_MEOW_SCRIPTS/files/spacem.sh"
992 # drop the temp file now that we're done.
998 # tty relevant functions...
1000 # keep_awake: sends a message to the screen from the background.
1001 function keep_awake()
1003 # just starts the keep_awake process in the background.
1004 bash $FEISTY_MEOW_SCRIPTS/tty/keep_awake_process.sh &
1005 # this should leave the job running as %1 or a higher number if there
1006 # are pre-existing background jobs.
1011 # site avenger functions...
1015 THISDIR="$FEISTY_MEOW_SCRIPTS/site_avenger"
1016 source "$FEISTY_MEOW_SCRIPTS/site_avenger/shared_site_mgr.sh"
1022 # you have hit the borderline functional zone...
1024 #hmmm: not really doing anything yet; ubuntu seems to have changed from pulseaudio in 17.04?
1025 # restarts the sound driver.
1026 function fix_sound_driver() {
1027 # stop bash complaining about blank function body.
1030 # sudo service alsasound restart
1031 #elif pulse something
1032 # sudo pulseaudio -k
1033 # sudo pulseaudio -D
1035 # something else...?
1040 # ...and here's the end of the borderline functional zone.
1044 # NOTE: no more function definitions are allowed after this point.
1046 function function_sentinel()
1051 if [ ! -z "$DEBUG_FEISTY_MEOW" ]; then echo "feisty meow function definitions done."; fi
1055 # test code for set_var_if_undefined.
1057 if [ $run_test != 0 ]; then
1058 echo running tests on set_var_if_undefined.
1060 set_var_if_undefined flagrant forknordle
1061 exit_on_error "testing if defined variable would be whacked"
1062 if [ $flagrant != petunia ]; then
1063 echo set_var_if_undefined failed to leave the test variable alone
1066 unset bobblehead_stomper
1067 set_var_if_undefined bobblehead_stomper endurance
1068 if [ $bobblehead_stomper != endurance ]; then
1069 echo set_var_if_undefined failed to set a variable that was not defined yet