# Jeff Jonas' .bashrc of useful things
#
# There are several motivations for these shell things
# 1) I use them every day since most of my work is from the shell command prompt,
#    not via GUI
# 2) To demonstrate why the shell is still the foundation for Unix/Linux adminstration,
#    with some essential "tricks" such as handling filenames containing whitespaces
#    or characters the shell normally expands
# 3) I was asked some of these questions during interviews.
#    I followed up with how to do it as efficiently as possible.
# 4) There are really times when you CANNOT reboot the system,
#    boot to a live CD/DVD/flash-drive or change INIT states.
#    So knowing how to use shell builtins to their max
#    is essential for regaining control of the system.

# For example, when you cannot run "ls" due to lack of process slots,
# then use "echo *" "echo */*" "echo .*" "echo ?" since the shell's builtin
# "GLOB" (global expansion) does all the filename expansion.
# See "Pathname Expansion" of the shell man page for how filenames are normally resolved
# and how it is controlled by variables such as nocaseglob, nullglob, failglob, dotglob

# If "ps" is not available, use "echo /proc/*" to find process IDs,
# "mycat /proc/*/cmdline" to see the command lines,
# "mygrep sh /proc/*/cmdline" to find processes running sh, bash or anything with "sh" in the name
# "mygrep 500 /proc/*/loginuid" to find all processes from user 500

#	*****	*****	*****	*****	*****	*****	*****	*****	*****	*****	*****	*****

# Recursively set all file modes, fix file ownership.
# The "{}" handles files with blanks and shell-meaningful characters

alias all444='find . -type f -exec chmod 0444 "{}" \;'
alias all777='find . -type d -exec chmod 0777 "{}" \;'
alias alljeffj='sudo chown -R jeffj:jeffj .'


# Generate secure hash of all files starting in current directory for a CD or DVD file verification
# typically used as follows:
# 1) cd into the root directory
# 2) allmd5 > /tmp/list
# 3) mv /tmp/list all_checksums
# that way
# - all pathnames are relative, not absolute
# - the resulting file is NOT included, because you cannot checksum a file still being created!
# sha256 is recommended over MD5.
# The real limitation is not the system creating the hash list
# but the target systems for they need the matching hash utility to verify the medium.

alias allmd5='find . -type f -exec md5sum "{}" \;'
alias allsha256='find . -type f -exec sha256 "{}" \;'
alias allsha512='find . -type f -exec sha512sum "{}" \;'


# DVD mastering & burning
# typical use
#	cd directory-of-interest # be IN the directory so all pathnames start ./ (relative to current working directory)
#	allsha512 > /tmp/all_sha_sums  # checksum all files to verify the medium, even years later
#	mv /tmp/all_sha_sums .         # move the checksum file into . so it's included on the disk
#	sha512sum -c sha_sums |grep -v OK$  # verify the checksums (best done from the burned media)
#	dvd1 /tmp/image .  # create CD/DVD image in /tmp (or wherever there's enough free space)
#	dvd2 /tmp/image    # burn the image

alias dvd1='genisoimage -r -J -o'
alias dvd2='wodim -v dev=/dev/sr0'
alias dvdinfo='dvd+rw-mediainfo /dev/sr0'


# delete all empty directories
# The "-depth" is needed to delete empty directories before visiting the parent directories
# so nested empty directories are all deleted.

alias zapdirs='find . -depth -type d -exec rmdir {} \;'


# pathadd: append $1 to $PATH if not already there
# bash and ksh have =~ for regular expression testing
# (ksh93 has =~ but OpenBSD's pdksh does not)
# The colons are to force matching the entire path, not just substrings of it
pathadd()
{
[[ ":$PATH:" =~ ":$1:" ]] || PATH=$PATH${PATH:+:}$1  # append if NOT already in PATH, suppress first ':'
}

# addpath: prepend $1 to $PATH if not already there
# bash and ksh have =~ for regular expression testing
addpath()
{
[[ ":$PATH:" =~ ":$1:" ]] || PATH=$1${PATH:+:}$PATH  # prepend if NOT already in PATH, suppress first ':'
}


# Here's a slightly more portable version from Linux /etc/bashrc
# but it runs several commands in subshells
# whereas pathadd() and addpath() are all bash builtins

pathmunge ()
{
    if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then
      if [ "$2" = "after" ] ; then
          PATH=$PATH:$1
      else
          PATH=$1:$PATH
      fi
    fi
}

# add these directories to $PATH if not already there
addpath $HOME/bin
pathadd /sbin



# Shell tools using shell builtins only:
# useful when the full environment is not available, or you cannot fork(2)

# cat multiple files, using shell builtins only
# works in bash and ksh, might work in sh
#
# the "read -r" inhibits "\" processing of escape characters
#
# the 'test -n' (for "is the string length > 0 ?")
# is invoked only upon read failing for end-of-file
# to handle files without a newline
# such as /proc/*/cmdline which is null terminated.

unset mycat

mycat()
{
local IFS=''  # temporarily inhibit input tokenizing to retain all whitespace
local file    # keep all variables temporary, within the function
local line

for file in $*
do
        echo "	==> $file <=="  # format from head(1)
	{
	while read -r line || test -n "$line"
	do
		echo $line
	done
	} < $file
done
}


# grep multiple files, using shell builtins only
# This is BASH ONLY due to =~ regexp test

unset mygrep

mygrep()
{
local IFS=''  # temporarily inhibit input tokenizing to retain all whitespace
local pattern
local filename
local line

pattern=$1  # first arg is regex to search for, all other args are filenames
shift
for filename in $*
do
	echo "	==> grep for '$pattern' in file '$filename' <=="
	{
	while read -r line || test -n "$line"
	do
		[[ $line =~ $pattern ]] && echo $line
	done
	} < $filename
done
}


# track what processes sourced .bashrc
dots=$dots:$$_bashrc
dots=${dots#:} # suppress the leading :
