├── LICENSE ├── README.md ├── ch01 └── interactive ├── ch03 ├── func_choice.1 ├── func_choice.2 ├── func_choice.3 ├── func_choose └── select_dir ├── ch05 ├── check_arg_count ├── check_unset_parms ├── chmod_all.1 ├── chmod_all.2 ├── embedded_documentation ├── suffixer └── use_up_option ├── ch06 ├── checkfile ├── checkstr ├── dashes ├── dbinit.1 ├── dbinit.2 ├── func_calc ├── rpncalc ├── strvsnum └── trackmatch ├── ch07 ├── asar.awk └── hist.awk ├── ch10 ├── ad ├── func_max.1 ├── func_max.2 └── hard_to_kill ├── ch11 └── default_date ├── ch12 ├── cdscript ├── dash ├── load_mp3 ├── mkalbum └── oodiff ├── ch13 ├── dbiniter ├── getopts_custom ├── getopts_example ├── onebyone ├── parseViaArray ├── parseViaFunc ├── parseViaRead └── pluralize ├── ch14 ├── MakeTemp ├── chkpath.1 ├── chkpath.2 ├── clean_temp ├── finding_tools ├── make_temp ├── security_template └── validate_using_case ├── ch15 ├── command_substitution ├── email_sample ├── email_sample_css ├── finding_ipas ├── fpmath ├── func_split └── using_phases ├── ch16 ├── add_to_bash_profile ├── add_to_bashrc ├── bash_logout ├── bash_profile ├── bashrc ├── builtin_tty.c ├── colors ├── func_cd ├── func_kill ├── func_mcd ├── func_pathmunge ├── func_signals ├── func_trunc_PWD ├── func_tweak_path ├── inputrc ├── prompts └── run_screen ├── ch17 ├── archive_meta-data ├── ff-sessions ├── func_commify ├── func_shift_by ├── is_process_running └── perl_sub_commify ├── ch19 └── buggy ├── lib ├── func_calc ├── func_cd ├── func_choice ├── func_choice.1 ├── func_choice.2 ├── func_choice.3 ├── func_choose ├── func_commify ├── func_kill ├── func_max.1 ├── func_max.2 ├── func_mcd ├── func_pathmunge ├── func_shift_by ├── func_signals ├── func_split ├── func_trunc_PWD └── func_tweak_path └── settings ├── README ├── add_to_bash_profile ├── add_to_bashrc ├── bash_logout ├── bash_profile ├── bashrc ├── inputrc └── run_screen /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 JP Vossen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _bash Cookbook_ examples 2 | 3 | 4 | 5 | 6 | Welcome to the examples from the _bash Cookbook_ (second edition), by Carl Albing and JP Vossen. 7 | 8 | * 9 | * 10 | * By the same authors, _bash Idioms_ (2022-04) 11 | * 12 | * 13 | * 14 | * Style Guide 15 | * Other bash resources 16 | * 17 | 18 | 19 | ## About the Book 20 | 21 | For system administrators, programmers, and end users, there are occasions when a simple (or not so simple) shell script can save you time and effort, or facilitate consistency and repeatability for a variety of common tasks. This cookbook provides more than 300 practical recipes for using bash, the popular Unix shell that enables you to harness and customize the power of any Unix or Linux system. 22 | 23 | Ideal for new and experienced users alike--including proficient Windows users and sysadmins--this updated second edition helps you solve a wide range of problems. You'll learn ways to handle input/output, file manipulation, program execution, administrative asks, and many other challenges. Each recipe includes one or more scripting examples and a discussion of why the solution works. 24 | 25 | You'll find recipes for problems including: 26 | 27 | * Standard output and input, executing commands 28 | * Shell variables, shell logic, and arithmetic 29 | * Intermediate shell tools and advanced scripting 30 | * Searching for files with `find`, `locate`, and `slocate` 31 | * Working with dates and times 32 | * Creating shell script for various end-user tasks 33 | * Working with tasks that require parsing 34 | * Writing secure shell scripts 35 | * Configuring and customizing `bash` 36 | 37 | 38 | ## Example Files 39 | 40 | Each sub-directory contains the important, long, or difficult-to-type 41 | examples from the relevant chapter. 42 | 43 | `./settings`, however, is special. It contains a collection of sample 44 | configuration files, see `./settings/README` and chapter 16 for details. 45 | -------------------------------------------------------------------------------- /ch01/interactive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: interactive 3 | 4 | case "$-" in 5 | *i*) # Code for interactive shell here 6 | ;; 7 | *) # Code for noninteractive shell here 8 | ;; 9 | esac 10 | -------------------------------------------------------------------------------- /ch03/func_choice.1: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choice.1 2 | 3 | # Let the user make a choice about something and return a standardized 4 | # answer. How the default is handled and what happens next is up to 5 | # the if/then after the choice in main. 6 | # Called like: choice 7 | # e.g. choice "Do you want to play a game?" 8 | # Returns: global variable CHOICE 9 | function choice { 10 | 11 | CHOICE='' 12 | local prompt="$*" 13 | local answer 14 | 15 | read -p "$prompt" answer 16 | case "$answer" in 17 | [yY1] ) CHOICE='y';; 18 | [nN0] ) CHOICE='n';; 19 | * ) CHOICE="$answer";; 20 | esac 21 | } # end of function choice 22 | -------------------------------------------------------------------------------- /ch03/func_choice.2: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choice.2 2 | CHOICE='' 3 | until [ "$CHOICE" = "y" ]; do 4 | printf "%b" "This package's date is $THISPACKAGE\n" >&2 5 | choice "Is that correct? [Y/,]: " 6 | if [ -z "$CHOICE" ]; then 7 | CHOICE='y' 8 | elif [ "$CHOICE" != "y" ]; then 9 | printf "%b" "Overriding $THISPACKAGE with $CHOICE\n" 10 | THISPACKAGE=$CHOICE 11 | fi 12 | done 13 | 14 | # Build the package here 15 | -------------------------------------------------------------------------------- /ch03/func_choice.3: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choice.3 2 | 3 | choice "Enter your favorite color, if you have one: " 4 | if [ -n "$CHOICE" ]; then 5 | printf "%b" "You chose: $CHOICE\n" 6 | else 7 | printf "%b" "You do not have a favorite color.\n" 8 | fi 9 | -------------------------------------------------------------------------------- /ch03/func_choose: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choose 2 | 3 | # Let the user make a choice about something and execute code based on 4 | # the answer 5 | # Called like: choose 6 | # e.g. choose "y" \ 7 | # "Do you want to play a game?" \ 8 | # /usr/games/GlobalThermonuclearWar \ 9 | # 'printf "%b" "See you later Professor Falkin.\n"' >&2 10 | # Returns: nothing 11 | function choose { 12 | 13 | local default="$1" 14 | local prompt="$2" 15 | local choice_yes="$3" 16 | local choice_no="$4" 17 | local answer 18 | 19 | read -p "$prompt" answer 20 | [ -z "$answer" ] && answer="$default" 21 | 22 | case "$answer" in 23 | [yY1] ) eval "$choice_yes" 24 | # error check 25 | ;; 26 | [nN0] ) eval "$choice_no" 27 | # error check 28 | ;; 29 | * ) printf "%b" "Unexpected answer '$answer'!" >&2 ;; 30 | esac 31 | } # end of function choose 32 | -------------------------------------------------------------------------------- /ch03/select_dir: -------------------------------------------------------------------------------- 1 | # cookbook filename: select_dir 2 | 3 | directorylist="Finished $(for i in /*;do [ -d "$i" ] && echo $i; done)" 4 | 5 | PS3='Directory to process? ' # Set a useful select prompt 6 | until [ "$directory" == "Finished" ]; do 7 | 8 | printf "%b" "\a\n\nSelect a directory to process:\n" >&2 9 | select directory in $directorylist; do 10 | 11 | # User types a number which is stored in $REPLY, but select 12 | # returns the value of the entry 13 | if [ "$directory" == "Finished" ]; then 14 | echo "Finished processing directories." 15 | break 16 | elif [ -n "$directory" ]; then 17 | echo "You chose number $REPLY, processing $directory..." 18 | # Do something here 19 | break 20 | else 21 | echo "Invalid selection!" 22 | fi # end of handle user's selection 23 | 24 | done # end of select a directory 25 | done # end of until dir == finished 26 | -------------------------------------------------------------------------------- /ch05/check_arg_count: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: check_arg_count 3 | # 4 | # Check for the correct # of arguments: 5 | # Use this syntax or use: if [ $# -lt 3 ] 6 | if (( $# < 3 )) 7 | then 8 | printf "%b" "Error. Not enough arguments.\n" >&2 9 | printf "%b" "usage: myscript file1 op file2\n" >&2 10 | exit 1 11 | elif (( $# > 3 )) 12 | then 13 | printf "%b" "Error. Too many arguments.\n" >&2 14 | printf "%b" "usage: myscript file1 op file2\n" >&2 15 | exit 2 16 | else 17 | printf "%b" "Argument count correct. Proceeding...\n" 18 | fi 19 | -------------------------------------------------------------------------------- /ch05/check_unset_parms: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: check_unset_parms 3 | # 4 | USAGE="usage: myscript scratchdir sourcefile conversion" 5 | FILEDIR=${1:?"Error. You must supply a scratch directory."} 6 | FILESRC=${2:?"Error. You must supply a source file."} 7 | CVTTYPE=${3:?"Error. ${USAGE}"} 8 | -------------------------------------------------------------------------------- /ch05/chmod_all.1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: chmod_all.1 3 | # 4 | # change permissions on a bunch of files 5 | # 6 | for FN in $* 7 | do 8 | echo changing $FN 9 | chmod 0750 $FN 10 | done 11 | -------------------------------------------------------------------------------- /ch05/chmod_all.2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: chmod_all.2 3 | # 4 | # change permissions on a bunch of files 5 | # with better quoting in case of filenames with spaces 6 | # 7 | for FN in "$@" 8 | do 9 | chmod 0750 "$FN" 10 | done 11 | -------------------------------------------------------------------------------- /ch05/embedded_documentation: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: embedded_documentation 3 | 4 | echo 'Shell script code goes here' 5 | 6 | # Use a : NOOP and here document to embed documentation, 7 | : <<'END_OF_DOCS' 8 | 9 | Embedded documentation such as Perl's Plain Old Documentation (POD), 10 | or even plain text here. 11 | 12 | Any accurate documentation is better than none at all. 13 | 14 | Sample documentation in Perl's Plain Old Documentation (POD) format adapted from 15 | CODE/ch07/Ch07.001_Best_Ex7.1 and 7.2 in the Perl Best Practices example tarball 16 | "PBP_code.tar.gz". 17 | 18 | =head1 NAME 19 | 20 | MY~PROGRAM--One-line description here 21 | 22 | =head1 SYNOPSIS 23 | 24 | MY~PROGRAM [OPTIONS] 25 | 26 | =head1 OPTIONS 27 | 28 | -h = This usage. 29 | -v = Be verbose. 30 | -V = Show version, copyright, and license information. 31 | 32 | 33 | =head1 DESCRIPTION 34 | 35 | A full description of the application and its features. 36 | May include numerous subsections (i.e., =head2, =head3, etc.) 37 | 38 | [...] 39 | 40 | 41 | =head1 LICENSE AND COPYRIGHT 42 | 43 | =cut 44 | 45 | END_OF_DOCS 46 | -------------------------------------------------------------------------------- /ch05/suffixer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: suffixer 3 | # 4 | # rename files that end in .bad to be .bash 5 | 6 | for FN in *.bad 7 | do 8 | mv "${FN}" "${FN%bad}bash" 9 | done 10 | -------------------------------------------------------------------------------- /ch05/use_up_option: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: use_up_option 3 | # 4 | # use and consume an option 5 | # 6 | # parse the optional argument 7 | VERBOSE=0 8 | if [[ $1 = -v ]] 9 | then 10 | VERBOSE=1 11 | shift 12 | fi 13 | # 14 | # the real work is here 15 | # 16 | for FN in "$@" 17 | do 18 | if (( VERBOSE == 1 )) 19 | then 20 | echo changing $FN 21 | fi 22 | chmod 0750 "$FN" 23 | done 24 | -------------------------------------------------------------------------------- /ch06/checkfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: checkfile 3 | # 4 | DIRPLACE=/tmp 5 | INFILE=/home/yucca/amazing.data 6 | OUTFILE=/home/yucca/more.results 7 | 8 | if [ -d "$DIRPLACE" ] 9 | then 10 | cd $DIRPLACE 11 | if [ -e "$INFILE" ] 12 | then 13 | if [ -w "$OUTFILE" ] 14 | then 15 | doscience < "$INFILE" >> "$OUTFILE" 16 | else 17 | echo "cannot write to $OUTFILE" 18 | fi 19 | else 20 | echo "cannot read from $INFILE" 21 | fi 22 | else 23 | echo "cannot cd into $DIRPLACE" 24 | fi 25 | -------------------------------------------------------------------------------- /ch06/checkstr: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: checkstr 3 | # 4 | # if statement 5 | # test a string to see if it has any length 6 | # 7 | # use the command-line argument 8 | VAR="$1" 9 | # 10 | # if [ "$VAR" ] will usually work but is bad form, using -n is more clear 11 | if [ -n "$VAR" ] 12 | then 13 | echo has text 14 | else 15 | echo zero length 16 | fi 17 | # 18 | if [ -z "$VAR" ] 19 | then 20 | echo zero length 21 | else 22 | echo has text 23 | fi 24 | -------------------------------------------------------------------------------- /ch06/dashes: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: dashes 3 | # 4 | # dashes - print a line of dashes 5 | # 6 | # options: # how many (default 72) 7 | # -c X use char X instead of dashes 8 | # 9 | 10 | LEN=72 11 | CHAR='-' 12 | while (( $# > 0 )) 13 | do 14 | case $1 in 15 | [0-9]*) LEN=$1 16 | ;; 17 | -c) shift; 18 | CHAR=${1:--} 19 | ;; 20 | *) printf 'usage: %s [-c X] [#]\n' ${0##*/} >&2 21 | exit 2 22 | ;; 23 | esac 24 | shift 25 | done 26 | # 27 | # more... 28 | -------------------------------------------------------------------------------- /ch06/dbinit.1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: dbinit.1 3 | # 4 | DBLIST=$(sh ./listdb | tail -n +2) 5 | select DB in $DBLIST 6 | do 7 | echo Initializing database: $DB 8 | mysql -u user -p $DB echo The answer is: $(( $* )) 6 | # Floating point 7 | awk "BEGIN {print \"The answer is: \" $* }"; 8 | } # end of calc 9 | -------------------------------------------------------------------------------- /ch06/rpncalc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: rpncalc 3 | # 4 | # simple RPN command-line (integer) calculator 5 | # 6 | # takes the arguments and computes with them 7 | # of the form a b op 8 | # allow the use of x instead of * 9 | # 10 | # error check our argument counts: 11 | if [ \( $# -lt 3 \) -o \( $(($# % 2)) -eq 0 \) ] 12 | then 13 | echo "usage: calc number number op [ number op ] ..." 14 | echo "use x or '*' for multiplication" 15 | exit 1 16 | fi 17 | 18 | ANS=$(($1 ${3//x/*} $2)) 19 | shift 3 20 | while [ $# -gt 0 ] 21 | do 22 | ANS=$((ANS ${2//x/*} $1)) 23 | shift 2 24 | done 25 | echo $ANS 26 | -------------------------------------------------------------------------------- /ch06/strvsnum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: strvsnum 3 | # 4 | # the old string vs. numeric comparison dilemma 5 | # 6 | VAR1=" 05 " 7 | VAR2="5" 8 | 9 | printf "%s" "do they -eq as equal? " 10 | if [ "$VAR1" -eq "$VAR2" ] 11 | then 12 | echo YES 13 | else 14 | echo NO 15 | fi 16 | 17 | printf "%s" "do they = as equal? " 18 | if [ "$VAR1" = "$VAR2" ] 19 | then 20 | echo YES 21 | else 22 | echo NO 23 | fi 24 | -------------------------------------------------------------------------------- /ch06/trackmatch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: trackmatch 3 | # 4 | for CDTRACK in * 5 | do 6 | if [[ "$CDTRACK" =~ "([[:alpha:][:blank:]]*)- ([[:digit:]]*) - (.*)$" ]] 7 | then 8 | echo Track ${BASH_REMATCH[2]} is ${BASH_REMATCH[3]} 9 | mv "$CDTRACK" "Track${BASH_REMATCH[2]}" 10 | fi 11 | done 12 | -------------------------------------------------------------------------------- /ch07/asar.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # cookbook filename: asar.awk 3 | # Associative arrays in Awk 4 | # Usage: ls -lR /usr/local | asar.awk 5 | 6 | NF > 7 { 7 | user[$3]++ 8 | } 9 | END { 10 | for (i in user) { 11 | printf "%s owns %d files\n", i, user[i] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ch07/hist.awk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/awk -f 2 | # cookbook filename: hist.awk 3 | # Histograms in Awk 4 | # Usage: ls -lR /usr/local | hist.awk 5 | 6 | function max(arr, big) 7 | { 8 | big = 0; 9 | for (i in user) 10 | { 11 | if (user[i] > big) { big=user[i];} 12 | } 13 | return big 14 | } 15 | 16 | NF > 7 { 17 | user[$3]++ 18 | } 19 | END { 20 | # for scaling 21 | maxm = max(user); 22 | for (i in user) { 23 | #printf "%s owns %d files\n", i, user[i] 24 | scaled = 60 * user[i] / maxm ; 25 | printf "%-10.10s [%8d]:", i, user[i] 26 | for (i=0; i 6 | [ -n "$BOOK_ASC" ] || { 7 | echo "FATAL: must export \$BOOK_ASC to the location of '...bcb2/head/asciidoc/'!" 8 | exit 1 9 | } 10 | \cd "$BOOK_ASC" || { 11 | echo "FATAL: can't cd to '$BOOK_ASC'!" 12 | exit 2 13 | } 14 | 15 | SELF="$0" # For clarity in recursion # <2> 16 | 17 | action="$1" # For code readability # <3> 18 | shift # Remove that argument from the list # <4> 19 | 20 | # If `xsel` is executable and we have no more arguments... # <5> 21 | [ -x /usr/bin/xsel -a $# -lt 1 ] && { 22 | # Read/write the clipboard 23 | text=$(xsel -b) 24 | function Output { 25 | echo -en "$*" | xsel -bi 26 | } 27 | } || { # Otherwise... 28 | # Read/write STDIN/STDOUT 29 | text=$* 30 | function Output { 31 | echo -en "$*" 32 | } 33 | } 34 | 35 | case "$action" in 36 | 37 | ####################################################################### 38 | # Content/Markup 39 | 40 | rec|recipe ) # Create the tags for a new recipe # <6> 41 | id="$($SELF id $text)" # Create an "ID" # <7> 42 | Output "$(cat <<- EoF # <8> 43 | [[$id]] 44 | === $text 45 | 46 | [[problem-$id]] 47 | ==== Problem 48 | 49 | [[solution-$id]] 50 | ==== Solution 51 | 52 | [[discussion-$id]] 53 | ==== Discussion 54 | 55 | [[see_also-$id]] 56 | ==== See Also 57 | * \`man \` 58 | * item1 59 | * <> 60 | * URL[text] 61 | EoF 62 | )" 63 | ;; 64 | 65 | table ) # Create the tags for a new table # <9> 66 | Output "$(cat <<- EoF 67 | .A Table 68 | [options="header"] 69 | |======= 70 | |head|h|h 71 | |cell|c|c 72 | |cell|c|c 73 | |======= 74 | EoF 75 | )" 76 | ;; 77 | 78 | # ... 79 | ### Headers 80 | h1 ) # Inside chapter head 1 (really Asciidoc h3) # <10> 81 | Output "=== $text" 82 | ;; 83 | h2 ) # Inside chapter head 2 (really Asciidoc h4) 84 | Output "==== $text" 85 | ;; 86 | h3 ) # Inside chapter head 3 (really Asciidoc h5) 87 | Output "===== $text" 88 | ;; 89 | 90 | ### Lists 91 | bul|bullet ) # Bullet list (.. = level 2, + = multiline element) 92 | Output ". $text" 93 | ;; 94 | nul|number|order* ) # Num./ordered list (## = level 2, + = multiline element) 95 | Output "# $text" 96 | ;; 97 | term ) # Terms 98 | Output "term_here::\n $text" # <11> 99 | ;; 100 | # ... 101 | 102 | cleanup ) ## Clean up all the xHTML/XML/PDF cruft 103 | rm -fv {ch??,app?}.{pdf,xml,html} book.xml docbook-xsl.css # <12> 104 | ;; 105 | 106 | * ) # <13> 107 | \cd - > /dev/null # UGLY cheat to revert the 'cd' above... 108 | # See also: http://stackoverflow.com/questions/59895/ 109 | # can-a-bash-script-tell-what-directory-its-stored-in 110 | ( echo "Usage:" 111 | egrep '\)[[:space:]]+# ' $SELF 112 | echo '' 113 | egrep '\)[[:space:]]+## ' $SELF ) | more # <14> 114 | ;; 115 | 116 | esac 117 | -------------------------------------------------------------------------------- /ch10/func_max.1: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_max.1 2 | 3 | # define the function: 4 | function max () 5 | { 6 | local HIDN 7 | if [ $1 -gt $2 ] 8 | then 9 | BIGR=$1 10 | else 11 | BIGR=$2 12 | fi 13 | HIDN=5 14 | } 15 | -------------------------------------------------------------------------------- /ch10/func_max.2: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_max.2 2 | 3 | # define the function: 4 | function max () 5 | { 6 | if [ $1 -gt $2 ] 7 | then 8 | echo $1 9 | else 10 | echo $2 11 | fi 12 | } 13 | -------------------------------------------------------------------------------- /ch10/hard_to_kill: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: hard_to_kill 3 | 4 | function trapped { 5 | if [ "$1" = "USR1" ]; then 6 | echo "Got me with a $1 trap!" 7 | exit 8 | else 9 | echo "Received $1 trap--neener, neener" 10 | fi 11 | } 12 | 13 | trap "trapped ABRT" ABRT 14 | trap "trapped EXIT" EXIT 15 | trap "trapped HUP" HUP 16 | trap "trapped INT" INT 17 | trap "trapped KILL" KILL # This won't actually work 18 | trap "trapped QUIT" QUIT 19 | trap "trapped TERM" TERM 20 | trap "trapped USR1" USR1 # This one is special 21 | 22 | # Just hang out and do nothing, without introducing "third-party" 23 | # trap behavior, such as if we used 'sleep' 24 | while (( 1 )); do 25 | : # : is a NOOP 26 | done 27 | -------------------------------------------------------------------------------- /ch11/default_date: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: default_date 3 | 4 | # Use noon time to prevent a script running around midnight and a clock a 5 | # few seconds off from causing off by one day errors. 6 | START_DATE=$(date -d 'last week Monday 12:00:00' '+%Y-%m-%d') 7 | 8 | while [ 1 ]; do 9 | printf "%b" "The starting date is $START_DATE, is that correct? (Y/new date)" 10 | read answer 11 | 12 | # Anything other than ENTER, "Y", or "y" is validated as a new date 13 | # Could use "[Yy]*" to allow the user to spell out "yes"... 14 | # Validate the new date format as: CCYY-MM-DD 15 | case "$answer" in 16 | [Yy]) break 17 | ;; 18 | [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]) 19 | printf "%b" "Overriding $START_DATE with $answer\n" 20 | START_DATE="$answer" 21 | ;; 22 | 23 | *) printf "%b" "Invalid date, please try again...\n" 24 | ;; 25 | esac 26 | done 27 | 28 | END_DATE=$(date -d "$START_DATE +7 days" '+%Y-%m-%d') 29 | 30 | echo "START_DATE: $START_DATE" 31 | echo "END_DATE: $END_DATE" 32 | -------------------------------------------------------------------------------- /ch12/cdscript: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: cdscript 3 | # cdscript - prep and burn a CD from a dir. 4 | # 5 | # usage: cdscript dir [ cddev ] 6 | # 7 | if (( $# < 1 || $# > 2 )) 8 | then 9 | echo 'usage: cdscript dir [ cddev ]' 10 | exit 2 11 | fi 12 | 13 | # set the defaults 14 | SRCDIR=$1 15 | # your device might be "ATAPI:0,0,0" or other digits 16 | CDDEV=${2:-"ATAPI:0,0,0"} 17 | ISOIMAGE=/tmp/cd$$.iso # <1> 18 | 19 | echo "building ISO image..." 20 | # 21 | # make the ISO fs image 22 | # 23 | mkisofs -A "$(cat ~/.cdAnnotation)" \ 24 | -p "$(hostname)" -V "${SRCDIR##*/}" \ 25 | -r -o "$ISOIMAGE" $SRCDIR 26 | STATUS=$? # <2> 27 | if (( STATUS != 0 )) 28 | then 29 | echo "Error. ISO image failed." 30 | echo "Investigate then remove $ISOIMAGE" 31 | exit $STATUS 32 | fi 33 | 34 | echo "ISO image built; burning to cd..." 35 | # 36 | # burn the CD 37 | # 38 | SPD=8 39 | OPTS="-eject -v fs=64M driveropts=burnproof" 40 | cdrecord $OPTS -speed=$SPD dev=${CDDEV} $ISOIMAGE 41 | STATUS=$? # <3> 42 | if (( STATUS != 0 )) 43 | then 44 | echo "Error. CD Burn failed." 45 | echo "Investigate then remove $ISOIMAGE" 46 | exit $STATUS 47 | fi 48 | 49 | rm -f $ISOIMAGE 50 | echo "Done." 51 | -------------------------------------------------------------------------------- /ch12/dash: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: dash 3 | # dash - print a line of dashes 4 | # options: # how many (default 72) 5 | # -c X use char X instead of dashes 6 | # 7 | function usagexit ( ) 8 | { 9 | printf "usage: %s [-c X] [#]\n" ${0##*/} # <1> 10 | exit 2 11 | } >&2 12 | 13 | LEN=72 # <2> 14 | CHAR='-' 15 | while (( $# > 0 )) # <3> 16 | do 17 | case $1 in 18 | [0-9]*) LEN=$1;; # <4> 19 | -c) shift # <5> 20 | CHAR=$1;; 21 | *) usagexit;; # <6> 22 | esac 23 | shift 24 | done 25 | 26 | if (( LEN > 4096 )) # <7> 27 | then 28 | echo "too large" >&2 29 | exit 3 30 | fi 31 | 32 | # build the string to the exact length 33 | DASHES="" 34 | for ((i=0; i 39 | let COUNT=0 40 | export FZ 41 | export FREE 42 | FREESPACE # <2> 43 | find . -name '*.mp3' -print | \ # <3> 44 | ( while read PATHNM # <4> 45 | do 46 | FILESIZE "$PATHNM" 47 | if ((FZ <= FREE)) 48 | then 49 | echo loading $PATHNM 50 | cp "$PATHNM" /media/mp3 51 | if (( $? == 0 )) 52 | then 53 | let SUM+=FZ 54 | let COUNT++ 55 | REDUCE $FZ 56 | else 57 | echo "bad copy of $PATHNM to /media/mp3" 58 | rm -f /media/mp3/"${PATHNM##*/}" 59 | # recompute because we don't know how far it got 60 | FREESPACE 61 | fi 62 | # any reason to go on? 63 | if (( FREE <= 0 )) 64 | then 65 | break 66 | fi 67 | else 68 | echo skipping $PATHNM 69 | fi 70 | done 71 | printf "loaded %d songs (%d blocks)" $COUNT $SUM 72 | printf " onto /media/mp3 (%d blocks free)\n" $FREE 73 | ) 74 | # end of script 75 | -------------------------------------------------------------------------------- /ch12/mkalbum: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash # <1> 2 | # cookbook filename: mkalbum 3 | # mkalbum - make an HTML "album" of a pile of photo files. 4 | # ver. 0.2 5 | # 6 | # An album is a directory of HTML pages. 7 | # It will be created in the current directory. 8 | # 9 | # An album page is the HTML to display one photo, with 10 | # a title that is the filename of the photo, along with 11 | # hyperlinks to the first, previous, next, and last photos. 12 | # 13 | # ERROUT 14 | ERROUT() # <2> 15 | { 16 | printf "%b" "$@" 17 | } >&2 # <3> 18 | 19 | # 20 | # USAGE 21 | USAGE() # <4> 22 | { 23 | ERROUT "usage: %s \n" ${0##*/} # <5> 24 | } 25 | 26 | # EMIT(thisph, startph, prevph, nextph, lastph) 27 | EMIT() # <6> 28 | { 29 | THISPH="../$1" 30 | STRTPH="${2%.*}.html" 31 | PREVPH="${3%.*}.html" 32 | NEXTPH="${4%.*}.html" 33 | LASTPH="${5%.*}.html" 34 | if [ -z "$3" ] 35 | then 36 | PREVLINE=' Prev ' 37 | else 38 | PREVLINE=' Prev ' 39 | fi 40 | if [ -z "$4" ] 41 | then 42 | NEXTLINE=' Next ' 43 | else 44 | NEXTLINE=' Next ' 45 | fi 46 | cat < 47 | 48 | $THISPH 49 | 50 |

$THISPH

51 | 52 | 53 | 54 | $PREVLINE 55 | $NEXTLINE 56 | 57 | 58 |
First Last
59 | $THISPH 62 | 63 | 64 | EOF 65 | } 66 | 67 | if (( $# != 1 )) 68 | then 69 | USAGE 70 | exit -1 71 | fi 72 | ALBUM="$1" 73 | if [ -d "${ALBUM}" ] 74 | then 75 | ERROUT "Directory [%s] already exists.\n" ${ALBUM} 76 | USAGE 77 | exit -2 78 | else 79 | mkdir "$ALBUM" 80 | fi 81 | cd "$ALBUM" 82 | 83 | PREV="" 84 | FIRST="" 85 | LAST="last" 86 | 87 | while read PHOTO 88 | do 89 | # prime the pump 90 | if [ -z "${CURRENT}" ] 91 | then 92 | CURRENT="$PHOTO" 93 | FIRST="$PHOTO" 94 | continue 95 | fi 96 | 97 | PHILE=${CURRENT##*/} # remove any leading path 98 | EMIT "$CURRENT" "$FIRST" "$PREV" "$PHOTO" "$LAST" > "${PHILE%.*}.html" 99 | 100 | # set up for next iteration 101 | PREV="$CURRENT" 102 | CURRENT="$PHOTO" 103 | 104 | done 105 | 106 | PHILE=${CURRENT##*/} # remove any leading pathname 107 | EMIT "$CURRENT" "$FIRST" "$PREV" "" "$LAST" > "${PHILE%.*}.html" 108 | 109 | # make the symlink for "last" 110 | ln -s "${PHILE%.*}.html" ./last.html # <8> 111 | 112 | # make a link for index.html 113 | ln -s "${FIRST%.*}.html" ./index.html 114 | -------------------------------------------------------------------------------- /ch12/oodiff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: oodiff 3 | # oodiff -- diff the CONTENTS of two OpenOffice/LibreOffice files 4 | # works only on .odt files 5 | # 6 | function usagexit () 7 | { 8 | echo "usage: ${0##*/} file1 file2" 9 | echo "where both files must be .odt files" 10 | exit $1 11 | } >&2 # <1> 12 | 13 | # assure two readable arg filenames which end in .odt 14 | if (( $# != 2 )) 15 | then 16 | usagexit 1 17 | fi 18 | if [[ $1 != *.odt || $2 != *.odt ]] 19 | then 20 | usagexit 2 21 | fi 22 | if [[ ! -r $1 || ! -r $2 ]] 23 | then 24 | usagexit 3 25 | fi 26 | 27 | BAS1=$(basename "$1" .odt) 28 | BAS2=$(basename "$2" .odt) 29 | 30 | # unzip them someplace private 31 | PRIV1="/tmp/${BAS1}.$$_1" 32 | PRIV2="/tmp/${BAS2}.$$_2" 33 | 34 | # make absolute 35 | HERE=$PWD 36 | if [[ ${1:0:1} == '/' ]] # <2> 37 | then 38 | FULL1="${1}" 39 | else 40 | FULL1="${HERE}/${1}" 41 | fi 42 | 43 | # make absolute 44 | if [[ ${2:0:1} == '/' ]] 45 | then 46 | FULL2="${2}" 47 | else 48 | FULL2="${HERE}/${2}" 49 | fi 50 | 51 | # mkdir scratch areas and check for failure 52 | # N.B. must have whitespace around the { and } and 53 | # must have the trailing ; in the {} lists 54 | mkdir "$PRIV1" || { echo "Unable to mkdir '$PRIV1'" ; exit 4; } 55 | mkdir "$PRIV2" || { echo "Unable to mkdir '$PRIV2'" ; exit 5; } 56 | 57 | cd "$PRIV1" 58 | unzip -q "$FULL1" 59 | sed -e 's/>/>\ # <3> 60 | /g' -e 's/ contentwnl.xml 62 | 63 | cd "$PRIV2" 64 | unzip -q "$FULL2" 65 | sed -e 's/>/>\ 66 | /g' -e 's/ contentwnl.xml 68 | 69 | cd "$HERE" 70 | 71 | diff "${PRIV1}/contentwnl.xml" "${PRIV2}/contentwnl.xml" 72 | 73 | rm -rf "$PRIV1" "$PRIV2" 74 | -------------------------------------------------------------------------------- /ch13/dbiniter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: dbiniter 3 | # 4 | # initialize databases from a standard file 5 | # creating databases as needed 6 | 7 | DBLIST=$(mysql -e "SHOW DATABASES;" | tail -n +2) 8 | select DB in $DBLIST "new..." 9 | do 10 | if [[ $DB == "new..." ]] 11 | then 12 | printf "%b" "name for new db: " 13 | read DB rest 14 | echo creating new database $DB 15 | mysql -e "CREATE DATABASE IF NOT EXISTS $DB;" 16 | fi 17 | 18 | if [ -n "$DB" ] 19 | then 20 | echo Initializing database: $DB 21 | mysql $DB < ourInit.sql 22 | fi 23 | done 24 | -------------------------------------------------------------------------------- /ch13/getopts_custom: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: getopts_custom 3 | # 4 | # using getopts - with custom error messages 5 | # 6 | aflag= 7 | bflag= 8 | # since we don't want getopts to generate error 9 | # messages, but want this script to issue its 10 | # own messages, we will put, in the option list, a 11 | # leading ':' to silence getopts. 12 | while getopts :ab: FOUND 13 | do 14 | case $FOUND in 15 | a) aflag=1 16 | ;; 17 | b) bflag=1 18 | bval="$OPTARG" 19 | ;; 20 | \:) printf "argument missing from -%s option\n" $OPTARG 21 | printf "Usage: %s: [-a] [-b value] args\n" ${0##*/} 22 | exit 2 23 | ;; 24 | \?) printf "unknown option: -%s\n" $OPTARG 25 | printf "Usage: %s: [-a] [-b value] args\n" ${0##*/} 26 | exit 2 27 | ;; 28 | esac >&2 29 | done 30 | shift $(($OPTIND - 1)) 31 | 32 | if [ "$aflag" ] 33 | then 34 | printf "Option -a specified\n" 35 | fi 36 | if [ "$bflag" ] 37 | then 38 | printf 'Option -b "%s" specified\n' "$bval" 39 | fi 40 | printf "Remaining arguments are: %s\n" "$*" 41 | -------------------------------------------------------------------------------- /ch13/getopts_example: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: getopts_example 3 | # 4 | # using getopts 5 | # 6 | aflag= 7 | bflag= 8 | while getopts 'ab:' OPTION 9 | do 10 | case $OPTION in 11 | a) aflag=1 12 | ;; 13 | b) bflag=1 14 | bval="$OPTARG" 15 | ;; 16 | ?) printf "Usage: %s: [-a] [-b value] args\n" ${0##*/} >&2 17 | exit 2 18 | ;; 19 | esac 20 | done 21 | shift $(($OPTIND - 1)) 22 | 23 | if [ "$aflag" ] 24 | then 25 | printf "Option -a specified\n" 26 | fi 27 | if [ "$bflag" ] 28 | then 29 | printf 'Option -b "%s" specified\n' "$bval" 30 | fi 31 | printf "Remaining arguments are: %s\n" "$*" 32 | -------------------------------------------------------------------------------- /ch13/onebyone: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: onebyone 3 | # 4 | # parsing input one character at a time 5 | 6 | while read ALINE 7 | do 8 | for ((i=0; i < ${#ALINE}; i++)) 9 | do 10 | ACHAR=${ALINE:i:1} 11 | # do something here, e.g. echo $ACHAR 12 | echo $ACHAR 13 | done 14 | done 15 | -------------------------------------------------------------------------------- /ch13/parseViaArray: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: parseViaArray 3 | # 4 | # find the file size 5 | # use an array to parse the ls -l output into words 6 | 7 | LSL=$(ls -ld $1) 8 | 9 | declare -a MYRA 10 | MYRA=($LSL) 11 | 12 | echo the file $1 is ${MYRA[4]} bytes. 13 | -------------------------------------------------------------------------------- /ch13/parseViaFunc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: parseViaFunc 3 | # 4 | # parse ls -l via function call 5 | # an example of output from ls -l follows: 6 | # -rw-r--r-- 1 albing users 126 Jun 10 22:50 fnsize 7 | 8 | function lsparts () 9 | { 10 | PERMS=$1 11 | LCOUNT=$2 12 | OWNER=$3 13 | GROUP=$4 14 | SIZE=$5 15 | CRMONTH=$6 16 | CRDAY=$7 17 | CRTIME=$8 18 | FILE=$9 19 | } 20 | 21 | lsparts $(ls -l "$1") 22 | 23 | echo $FILE has $LCOUNT 'link(s)' and is $SIZE bytes long. 24 | -------------------------------------------------------------------------------- /ch13/parseViaRead: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: parseViaRead 3 | # 4 | # parse ls -l with a read statement 5 | # an example of output from ls -l follows: 6 | # -rw-r--r-- 1 albing users 126 2006-10-10 22:50 fnsize 7 | 8 | ls -l "$1" | { read PERMS LCOUNT OWNER GROUP SIZE CRDATE CRTIME FILE ; 9 | echo $FILE has $LCOUNT 'link(s)' and is $SIZE bytes long. ; 10 | } 11 | -------------------------------------------------------------------------------- /ch13/pluralize: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: pluralize 3 | # 4 | # A function to make words plural by adding an s 5 | # when the value ($2) is != 1 or -1. 6 | # It only adds an 's'; it is not very smart. 7 | # 8 | function plural () 9 | { 10 | if [ $2 -eq 1 -o $2 -eq -1 ] 11 | then 12 | echo ${1} 13 | else 14 | echo ${1}s 15 | fi 16 | } 17 | 18 | while read num name 19 | do 20 | echo $num $(plural "$name" $num) 21 | done 22 | -------------------------------------------------------------------------------- /ch14/MakeTemp: -------------------------------------------------------------------------------- 1 | # cookbook filename: MakeTemp 2 | # Function to incorporate or source into another script 3 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | # Try to create a secure temp file name or directory 5 | # Called like: $temp_file=$(MakeTemp [path/to/name-prefix]) 6 | # Returns the name of secure temp file name or directory in $TEMP_NAME 7 | # For example: 8 | 9 | # $temp_dir=$(MakeTemp dir /tmp/$PROGRAM.foo) 10 | # $temp_file=$(MakeTemp file /tmp/$PROGRAM.foo) 11 | # 12 | function MakeTemp { 13 | 14 | # Make sure $TMP is set to something 15 | [ -n "$TMP" ] || TMP='/tmp' 16 | 17 | local type_name=$1 18 | local prefix=${2:-$TMP/temp} # Unless prefix is defined, use $TMP + temp 19 | local temp_type='' 20 | local sanity_check='' 21 | 22 | case $type_name in 23 | file ) 24 | temp_type='' 25 | ur_cmd='touch' 26 | # -f Regular file -r Readable 27 | # -w Writable -O Owned by me 28 | sanity_check='test -f $TEMP_NAME -a -r $TEMP_NAME \ 29 | -a -w $TEMP_NAME -a -O $TEMP_NAME' 30 | ;; 31 | dir|directory ) 32 | temp_type='-d' 33 | ur_cmd='mkdir -p -m0700' 34 | # -d Directory -r Readable 35 | # -w Writable -x Searchable -O Owned by me 36 | sanity_check='test -d $TEMP_NAME -a -r $TEMP_NAME \ 37 | -a -w $TEMP_NAME -a -x $TEMP_NAME -a -O $TEMP_NAME' 38 | ;; 39 | * ) Error "\nBad type in $PROGRAM:MakeTemp! Needs file|dir." 1 ;; 40 | esac 41 | 42 | # First try mktemp 43 | TEMP_NAME=$(mktemp $temp_type ${prefix}.XXXXXXXXX) 44 | 45 | # If that fails try urandom, if that fails give up 46 | if [ -z "$TEMP_NAME" ]; then 47 | TEMP_NAME="${prefix}.$(cat /dev/urandom | od -x | tr -d ' ' | head -1)" 48 | $ur_cmd $TEMP_NAME 49 | fi 50 | 51 | # Make sure the file or directory was actually created, or DIE 52 | if ! eval $sanity_check; then 53 | Error \ 54 | "\aFATAL ERROR: can't make temp $type_name with '$0:MakeTemp$*'!\n" 2 55 | else 56 | echo "$TEMP_NAME" 57 | fi 58 | 59 | } # end of function MakeTemp 60 | -------------------------------------------------------------------------------- /ch14/chkpath.1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: chkpath.1 3 | # Check your $PATH for world-writable or missing directories 4 | 5 | exit_code=0 6 | 7 | for dir in ${PATH//:/ }; do 8 | [ -L "$dir" ] && printf "%b" "symlink, " 9 | if [ ! -d "$dir" ]; then 10 | printf "%b" "missing\t\t" 11 | (( exit_code++ )) 12 | elif [ -n "$(ls -lLd $dir | grep '^d.......w. ')" ]; then 13 | printf "%b" "world writable\t" 14 | (( exit_code++ )) 15 | else 16 | printf "%b" "ok\t\t" 17 | fi 18 | printf "%b" "$dir\n" 19 | done 20 | exit $exit_code 21 | -------------------------------------------------------------------------------- /ch14/chkpath.2: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: chkpath.2 3 | # Check your $PATH for world-writable or missing directories, with 'stat' 4 | 5 | exit_code=0 6 | 7 | for dir in ${PATH//:/ }; do 8 | [ -L "$dir" ] && printf "%b" "symlink, " 9 | if [ ! -d "$dir" ]; then 10 | printf "%b" "missing\t\t\t\t" 11 | (( exit_code++ )) 12 | else 13 | stat=$(ls -lHd $dir | awk '{print $1, $3, $4}') 14 | if [ -n "$(echo $stat | grep '^d.......w. ')" ]; then 15 | printf "%b" "world writable\t$stat " 16 | (( exit_code++ )) 17 | else 18 | printf "%b" "ok\t\t$stat " 19 | fi 20 | fi 21 | printf "%b" "$dir\n" 22 | 23 | done 24 | exit $exit_code 25 | -------------------------------------------------------------------------------- /ch14/clean_temp: -------------------------------------------------------------------------------- 1 | # cookbook filename: clean_temp 2 | 3 | # Do our best to clean up temp files no matter what 4 | # Note $temp_dir must be set before this, and must not change! 5 | cleanup="rm -rf $temp_dir" 6 | trap "$cleanup" ABRT EXIT HUP INT QUIT 7 | -------------------------------------------------------------------------------- /ch14/finding_tools: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: finding_tools 3 | 4 | # Export may or may not also be needed, depending on what you are doing 5 | 6 | # These are fairly safe bets 7 | _cp='/bin/cp' 8 | _mv='/bin/mv' 9 | _rm='/bin/rm' 10 | 11 | # These are a little trickier 12 | case $(/bin/uname) in 13 | 'Linux') 14 | _cut='/bin/cut' 15 | _nice='/bin/nice' 16 | # [...] 17 | ;; 18 | 'SunOS') 19 | _cut='/usr/bin/cut' 20 | _nice='/usr/bin/nice' 21 | # [...] 22 | ;; 23 | # [...] 24 | esac 25 | -------------------------------------------------------------------------------- /ch14/make_temp: -------------------------------------------------------------------------------- 1 | # cookbook filename: make_temp 2 | 3 | # Make sure $TMP is set to something 4 | [ -n "$TMP" ] || TMP='/tmp' 5 | 6 | # Make a "good enough" random temp directory 7 | until [ -n "$temp_dir" -a ! -d "$temp_dir" ]; do 8 | temp_dir="/$TMP/meaningful_prefix.${RANDOM}${RANDOM}${RANDOM}" 9 | done 10 | mkdir -p -m 0700 $temp_dir \ 11 | || { echo "FATAL: Failed to create temp dir '$temp_dir': $?"; exit 100; } 12 | 13 | # Make a "good enough" random temp file in the temp dir 14 | temp_file="$temp_dir/meaningful_prefix.${RANDOM}${RANDOM}${RANDOM}" 15 | touch $temp_file && chmod 0600 $temp_file \ 16 | || { echo "FATAL: Failed to create temp file '$temp_file': $?"; exit 101; } 17 | -------------------------------------------------------------------------------- /ch14/security_template: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: security_template 3 | 4 | # Set a sane/secure path 5 | PATH='/usr/local/bin:/bin:/usr/bin' 6 | # It's almost certainly already marked for export, but make sure 7 | \export PATH 8 | 9 | # Clear all aliases. Important: leading \ inhibits alias expansion. 10 | \unalias -a 11 | 12 | # Clear the command path hash 13 | hash -r 14 | 15 | # Set the hard limit to 0 to turn off core dumps 16 | ulimit -H -c 0 -- 17 | 18 | # Set a sane/secure IFS (note this is bash & ksh93 syntax only--not portable!) 19 | IFS=$' \t\n' 20 | 21 | # Set a sane/secure umask variable and use it 22 | # Note this does not affect files already redirected on the command line 23 | # 022 results in 0755 perms, 077 results in 0700 perms, etc. 24 | UMASK=022 25 | umask $UMASK 26 | 27 | until [ -n "$temp_dir" -a ! -d "$temp_dir" ]; do 28 | temp_dir="/tmp/meaningful_prefix.${RANDOM}${RANDOM}${RANDOM}" 29 | done 30 | mkdir -p -m 0700 $temp_dir \ 31 | || (echo "FATAL: Failed to create temp dir '$temp_dir': $?"; exit 100) 32 | 33 | # Do our best to clean up temp files no matter what 34 | # Note $temp_dir must be set before this, and must not change! 35 | cleanup="rm -rf $temp_dir" 36 | trap "$cleanup" ABRT EXIT HUP INT QUIT 37 | -------------------------------------------------------------------------------- /ch14/validate_using_case: -------------------------------------------------------------------------------- 1 | # cookbook filename: validate_using_case 2 | 3 | case $raw_input in 4 | *.company.com ) # Probably a local hostname 5 | ;; 6 | *.jpg ) # Probably a JPEG file 7 | ;; 8 | *.[jJ][pP][gG] ) # Probably a JPEG file, case-insensitive 9 | ;; 10 | foo | bar ) # Entered 'foo' or 'bar 11 | ;; 12 | [0-9][0-9][0-9] ) # A 3-digit number 13 | ;; 14 | [a-z][a-z][a-z][a-z] ) # A 4-lowercase-char word 15 | ;; 16 | * ) # None of the above 17 | ;; 18 | esac 19 | -------------------------------------------------------------------------------- /ch15/command_substitution: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: command_substitution 3 | 4 | REMOTE_HOST='host.example.com' # Required 5 | REMOTE_FILE='/etc/passwd' # Required 6 | SSH_USER='user@' # Optional, set to '' to not use 7 | #SSH_ID='-i ~/.ssh/foo.id' # Optional, set to '' to not use 8 | SSH_ID='' 9 | 10 | result=$( 11 | ssh $SSH_ID $SSH_USER$REMOTE_HOST \ 12 | "[ -r $REMOTE_FILE ] && echo 1 || echo 0" 13 | ) || { echo "SSH command failed!" >&2; exit 1; } 14 | 15 | if [ $result = 1 ]; then 16 | echo "$REMOTE_FILE present on $REMOTE_HOST" 17 | else 18 | echo "$REMOTE_FILE not present on $REMOTE_HOST" 19 | fi 20 | -------------------------------------------------------------------------------- /ch15/email_sample: -------------------------------------------------------------------------------- 1 | # cookbook filename: email_sample 2 | 3 | # Define some mail settings. Use a case statement with uname or hostname 4 | # to tweak settings as required for your environment. 5 | case $HOSTNAME in 6 | *.company.com ) MAILER='mail' ;; # Linux and BSD 7 | host1.* ) MAILER='mailx' ;; # Solaris, BSD, and some Linuxes 8 | host2.* ) MAILER='mailto' ;; # Handy, if installed 9 | esac 10 | RECIPIENTS='recipient1@example.com recipient2@example.com' 11 | SUBJECT="Data from $0" 12 | 13 | [...] 14 | # Create the body as a file or variable using echo, printf, or a here-document 15 | # Create or modify $SUBJECT and/or $RECIPIENTS as needed 16 | [...] 17 | 18 | ( echo $email_body ; uuencode $attachment $(basename $attachment) ) \ 19 | | $MAILER -s "$SUBJECT" "$RECIPIENTS" 20 | -------------------------------------------------------------------------------- /ch15/email_sample_css: -------------------------------------------------------------------------------- 1 | # cookbook filename: email_sample_css 2 | # From Chapter 8 of Classic Shell Scripting 3 | 4 | for MAIL in /bin/mailx /usr/bin/mailx /usr/sbin/mailx /usr/ucb/mailx /bin/mail \ 5 | /usr/bin/mail; do 6 | [ -x $MAIL ] && break 7 | done 8 | [ -x $MAIL ] || { echo 'Cannot find a mailer!' >&2; exit 1; } 9 | -------------------------------------------------------------------------------- /ch15/finding_ipas: -------------------------------------------------------------------------------- 1 | # cookbook filename: finding_ipas 2 | 3 | # IPv4 Using awk, cut, and head 4 | $ /sbin/ifconfig -a | awk '/(cast)/ { print $2 }' | cut -d':' -f2 | head -1 5 | 6 | # IPv4 Using Perl, just for fun 7 | $ /sbin/ifconfig -a | perl -ne 'if ( m/^\s*inet (?:addr:)?([\d.]+).*?cast/ ) 8 | > { print qq($1\n); exit 0; }' 9 | 10 | # IPv6 Using awk, cut, and head 11 | $ /sbin/ifconfig -a | egrep 'inet6 addr: |address: ' | cut -d':' -f2- \ 12 | | cut -d'/' -f1 | head -1 | tr -d ' ' 13 | 14 | # IPv6 Using Perl, just for fun 15 | $ /sbin/ifconfig -a | perl -ne 'if 16 | > ( m/^\s*(?:inet6)? \s*addr(?:ess)?: ([0-9A-Fa-f:]+)/ ) { print qq($1\n); 17 | > exit 0; }' 18 | -------------------------------------------------------------------------------- /ch15/fpmath: -------------------------------------------------------------------------------- 1 | # cookbook filename: fpmath 2 | # using coproc for floating-point math 3 | 4 | # initialize the coprocess 5 | # call this first 6 | # before attempting any calls to fpmath 7 | function fpinit () 8 | { 9 | coproc /usr/bin/bc 10 | 11 | bcin=${COPROC[1]} 12 | bcout=${COPROC[0]} 13 | echo "scale=5" >& ${bcin} 14 | } 15 | 16 | # compute with floating-point numbers 17 | # by sending the args to bc 18 | # then reading its response 19 | function fpmath() 20 | { 21 | echo "$@" >& ${bcin} 22 | if read -t 0.25 -u ${bcout} responz 23 | then 24 | echo "$responz" 25 | fi 26 | } 27 | 28 | ############################ 29 | # main 30 | 31 | fpinit 32 | 33 | while read aline 34 | do 35 | answer=$(fpmath "$aline") 36 | if [[ -n $answer ]] 37 | then 38 | echo $answer 39 | fi 40 | done 41 | -------------------------------------------------------------------------------- /ch15/func_split: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_split 2 | 3 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | # Output fixed-size pieces of input ONLY if the limit is exceeded 5 | # Called like: Split 6 | # e.g. Split $output ${output}_ --lines 100 7 | # See split(1) and wc(1) for option details 8 | function Split { 9 | local file=$1 10 | local prefix=$2 11 | local limit_type=$3 12 | local limit_size=$4 13 | local wc_option 14 | 15 | # Sanity checks 16 | if [ -z "$file" ]; then 17 | printf "%b" "Split: requires a file name!\n" 18 | return 1 19 | fi 20 | if [ -z "$prefix" ]; then 21 | printf "%b" "Split: requires an output file prefix!\n" 22 | return 1 23 | fi 24 | if [ -z "$limit_type" ]; then 25 | printf "%b" \ 26 | "Split: requires a limit option (e.g. --lines), see 'man split'!\n" 27 | return 1 28 | fi 29 | if [ -z "$limit_size" ]; then 30 | printf "%b" "Split: requires a limit size (e.g. 100), see 'man split'!\n" 31 | return 1 32 | fi 33 | 34 | # Convert split options to wc options. Sigh. 35 | # Not all options supported by all wc/splits on all systems 36 | case $limit_type in 37 | -b|--bytes) wc_option='-c';; 38 | -C|--line-bytes) wc_option='-L';; 39 | -l|--lines) wc_option='-l';; 40 | esac 41 | 42 | # If whatever limit is exceeded 43 | if [ "$(wc $wc_option $file | awk '{print $1}')" -gt $limit_size ]; then 44 | # Actually do something 45 | split --verbose $limit_type $limit_size $file $prefix 46 | fi 47 | } # end of function Split 48 | -------------------------------------------------------------------------------- /ch15/using_phases: -------------------------------------------------------------------------------- 1 | # cookbook filename: using_phases 2 | 3 | # Main loop 4 | until [ "$phase" = "Finished." ]; do 5 | 6 | case $phase in 7 | 8 | phase0 ) 9 | ThisPhase=0 10 | NextPhase="$(( $ThisPhase + 1 ))" 11 | echo '############################################' 12 | echo "Phase$ThisPhase = Initialization of FooBarBaz build" 13 | # Things that should only be initialized at the beginning of a 14 | # new build cycle go here 15 | # ... 16 | echo "Phase${ThisPhase}=Ending" 17 | phase="phase$NextPhase" 18 | ;; 19 | 20 | 21 | # ... 22 | 23 | 24 | phase20 ) 25 | ThisPhase=20 26 | NextPhase="$(( $ThisPhase + 1 ))" 27 | echo '############################################' 28 | echo "Phase$ThisPhase = Main processing for FooBarBaz build" 29 | 30 | 31 | # ... 32 | 33 | 34 | choice "[P$ThisPhase] Do we need to stop and fix anything? [y/N]: " 35 | if [ "$choice" = "y" ]; then 36 | echo "Re-run '$MYNAME phase${ThisPhase}' after handling this." 37 | exit $ThisPhase 38 | fi 39 | 40 | echo "Phase${ThisPhase}=Ending" 41 | phase="phase$NextPhase" 42 | ;; 43 | 44 | 45 | # ... 46 | 47 | 48 | * ) 49 | echo "What the heck?!? We should never get HERE! Gonna croak!" 50 | echo "Try $0 -h" 51 | exit 99 52 | phase="Finished." 53 | ;; 54 | esac 55 | printf "%b" "\a" # Ring the bell 56 | done 57 | -------------------------------------------------------------------------------- /ch16/add_to_bash_profile: -------------------------------------------------------------------------------- 1 | # cookbook filename: add_to_bash_profile 2 | # Add this code to your ~/.bash_profile 3 | 4 | # If we're running in bash, search for then source our settings 5 | # You can also just hardcode $SETTINGS, but this is more flexible 6 | if [ -n "$BASH_VERSION" ]; then 7 | for path in /opt/bin /etc ~ ; do 8 | # Use the first one found 9 | if [ -d "$path/settings" -a -r "$path/settings" -a -x "$path/settings" ] 10 | then 11 | export SETTINGS="$path/settings" 12 | fi 13 | done 14 | source "$SETTINGS/bash_profile" 15 | #source "$SETTINGS/bashrc" # If necessary 16 | fi 17 | -------------------------------------------------------------------------------- /ch16/add_to_bashrc: -------------------------------------------------------------------------------- 1 | # cookbook filename: add_to_bashrc 2 | # Add this code to your ~/.bashrc 3 | 4 | # If we're running in bash, and it isn't already set, 5 | # search for then source our settings 6 | # You can also just hard code $SETTINGS, but this is more flexible 7 | if [ -n "$BASH_VERSION" ]; then 8 | if [ -z "$SETTINGS" ]; then 9 | for path in /opt/bin /etc ~ ; do 10 | # Use the first one found 11 | if [ -d "$path/settings" -a -r "$path/settings" -a -x "$path/settings" ] 12 | then 13 | export SETTINGS="$path/settings" 14 | fi 15 | done 16 | fi 17 | source "$SETTINGS/bashrc" 18 | fi 19 | -------------------------------------------------------------------------------- /ch16/bash_logout: -------------------------------------------------------------------------------- 1 | # cookbook filename: bash_logout 2 | 3 | # settings/bash_logout: execute on shell logout 4 | 5 | # Clear the screen on logout to prevent information leaks, if not already 6 | # set as an exit trap elsewhere 7 | [ -n "$PS1" ] && clear 8 | -------------------------------------------------------------------------------- /ch16/bash_profile: -------------------------------------------------------------------------------- 1 | # cookbook filename: bash_profile 2 | 3 | # settings/bash_profile: Login shell environment settings 4 | # To reread (and implement changes to this file) use: 5 | # source $SETTINGS/bash_profile 6 | 7 | # Only if interactive bash with a terminal! 8 | [ -t 1 -a -n "$BASH_VERSION" ] || return 9 | 10 | # Failsafe. This should be set when we're called, but if not, the 11 | # "not found" error messages should be pretty clear. 12 | # Use leading ':' to prevent this from being run as a program after 13 | # it is expanded. 14 | : ${SETTINGS:='SETTINGS_variable_not_set'} 15 | 16 | # DEBUGGING only--will break scp, rsync 17 | # echo "Sourcing $SETTINGS/bash_profile..." 18 | # export PS4='+xtrace $LINENO: ' 19 | # set -x 20 | 21 | # Debugging/logging--will not break scp, rsync 22 | #case "$-" in 23 | # *i*) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Interactive" \ 24 | # "$SETTINGS/bash_profile ssh=$SSH_CONNECTION" >> ~/rc.log ;; 25 | # * ) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Noninteractive" \ 26 | # "$SETTINGS/bash_profile ssh=$SSH_CONNECTION" >> ~/rc.log ;; 27 | #esac 28 | 29 | # Use the keychain (http://www.funtoo.org/Keychain/) shell script 30 | # to manage ssh-agent, if it's available. If it's not, you should look 31 | # into adding it. 32 | for path in $SETTINGS ${PATH//:/ }; do 33 | if [ -x "$path/keychain" ]; then 34 | # Load default id_rsa and/or id_dsa keys, add others here as needed 35 | # See also --clear --ignore-missing --noask --quiet --time-out 36 | $path/keychain ~/.ssh/id_?sa ~/.ssh/${USER}_?sa 37 | break 38 | fi 39 | done 40 | 41 | 42 | # Apply interactive subshell customizations to login shells too. 43 | # The system profile file in /etc probably already does this. 44 | # If not, it's probably better to do it manually in wherever you: 45 | # source "$SETTINGS/bash_profile" 46 | # But just in case... 47 | # for file in /etc/bash.bashrc /etc/bashrc ~/.bashrc; do 48 | # [ -r "$file" ] && source $file && break # Use the first one found 49 | #done 50 | 51 | 52 | # Do site- or host-specific things here 53 | case $HOSTNAME in 54 | *.company.com ) # source $SETTINGS/company.com 55 | ;; 56 | host1.* ) # host1 stuff 57 | ;; 58 | host2.company.com ) # source .bashrc.host2 59 | ;; 60 | drake.* ) # echo DRAKE in bash_profile.jp! 61 | ;; 62 | esac 63 | 64 | 65 | # Do this last because we basically fork off from here. If we exit screen 66 | # we return to a fully configured session. The screen session gets configured 67 | # as well, and if we never leave it, well, this session isn't that bloated. 68 | 69 | # Only run if we are interactive and not already running screen 70 | # AND '~/.use_screen' exists. 71 | if [ "$PS1" -a $TERM != "screen" -a "$USING_SCREEN" != "YES" -a -f ~/.use_screen ]; \ 72 | then 73 | # We'd rather use 'type -P' here, but that was added in bash-2.05b and we 74 | # use systems we don't control with versions older than that. We can't 75 | # easily use 'which' since on some systems that produces output whether 76 | # the file is found or not. 77 | for path in ${PATH//:/ }; do 78 | if [ -x "$path/screen" ]; then 79 | # If screen(1) exists and is executable, run our wrapper 80 | [ -x "$SETTINGS/run_screen" ] && $SETTINGS/run_screen 81 | fi 82 | done 83 | fi 84 | -------------------------------------------------------------------------------- /ch16/bashrc: -------------------------------------------------------------------------------- 1 | # cookbook filename: bashrc 2 | 3 | # settings/bash_profile: subshell environment settings 4 | # To reread (and implement changes to this file) use: 5 | # source $SETTINGS/bashrc 6 | 7 | # Only if interactive bash with a terminal! 8 | [ -t 1 -a -n "$BASH_VERSION" ] || return 9 | 10 | # Failsafe. This should be set when we're called, but if not, the 11 | # "not found" error messages should be pretty clear. 12 | # Use leading ':' to prevent this from being run as a program after 13 | # it is expanded. 14 | : ${SETTINGS:='SETTINGS_variable_not_set'} 15 | 16 | # DEBUGGING only--will break scp, rsync 17 | # echo "Sourcing $SETTINGS/bash_profile..." 18 | # export PS4='+xtrace $LINENO: ' 19 | # set -x 20 | 21 | # Debugging/logging--will not break scp, rsync 22 | # case "$-" in 23 | # *i*) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Interactive" \ 24 | # "$SETTINGS/bashrc ssh=$SSH_CONNECTION" >> ~/rc.log ;; 25 | # * ) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Noninteractive" \ 26 | # "$SETTINGS/bashrc ssh=$SSH_CONNECTION" >> ~/rc.log ;; 27 | #esac 28 | 29 | # In theory this is also sourced from /etc/bashrc (/etc/bash.bashrc) 30 | # or ~/.bashrc to apply all these settings to login shells too. In practice 31 | # if these settings only work sometimes (like in subshells), verify that. 32 | 33 | # Source keychain file (if it exists) for SSH and GPG agents 34 | [ -r "$HOME/.keychain/${HOSTNAME}-sh" ] \ 35 | && source "$HOME/.keychain/${HOSTNAME}-sh" 36 | [ -r "$HOME/.keychain/${HOSTNAME}-sh-gpg" ] \ 37 | && source "$HOME/.keychain/${HOSTNAME}-sh-gpg" 38 | 39 | # Set some more useful prompts 40 | # Interactive command-line prompt 41 | # ONLY set one of these if we really are interactive, since lots of people 42 | # (even us sometimes) test to see if a shell is interactive using 43 | # something like: if [ "$PS1" ]; then 44 | case "$-" in 45 | *i*) 46 | #export PS1='\n[\u@\h t:\l l:$SHLVL h:\! j:\j v:\V]\n$PWD\$ ' 47 | #export PS1='\n[\u@\h:T\l:L$SHLVL:C\!:\D{%Y-%m-%d_%H:%M:%S_%Z}]\n$PWD\$ ' 48 | export PS1='\n[\u@\h:T\l:L$SHLVL:C\!:J\j:\D{%Y-%m-%d_%H:%M:%S_%Z}]\n$PWD\$ ' 49 | #export PS2='> ' # Secondary (i.e. continued) prompt 50 | 51 | #export PS3='Please make a choice: ' # Select prompt 52 | #export PS4='+xtrace $LINENO: ' # xtrace (debug) prompt 53 | export PS4='+xtrace $BASH_SOURCE::$FUNCNAME-$LINENO: ' # xtrace prompt 54 | 55 | # If this is an xterm set the title to user@host:dir 56 | case "$TERM" in 57 | xterm*|rxvt*) 58 | PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}:$PWD\007"' 59 | ;; 60 | esac 61 | ;; 62 | esac 63 | 64 | # Make sure custom inputrc is handled, if we can find it; note different 65 | # names. Also note different order, since for this one we probably want 66 | # our custom settings to override the system file, if present. 67 | for file in $SETTINGS/inputrc ~/.inputrc /etc/inputrc; do 68 | [ -r "$file" ] && export INPUTRC="$file" && break # Use first found 69 | done 70 | 71 | # No core files by default 72 | # See also /etc/security/limits.conf on many Linux systems. 73 | ulimit -S -c 0 > /dev/null 2>&1 74 | 75 | # Set various aspects of the bash history 76 | export HISTSIZE=5000 # Num. of commands in history stack in memory 77 | export HISTFILESIZE=5000 # Num. of commands in history file 78 | #export HISTCONTROL=ignoreboth # bash < 3, omit dups & lines starting with spaces 79 | export HISTCONTROL='erasedups:ignoredups:ignorespace' 80 | export HISTIGNORE='&:[ ]*' # bash >= 3, omit dups & lines starting with spaces 81 | #export HISTTIMEFORMAT='%Y-%m-%d_%H:%M:%S_%Z=' # bash >= 3, timestamp hist file 82 | shopt -s histappend # Append rather than overwrite history on exit 83 | shopt -q -s cdspell # Auto-fix minor typos in interactive use of 'cd' 84 | shopt -q -s checkwinsize # Update the values of LINES and COLUMNS 85 | shopt -q -s cmdhist # Make multiline commands 1 line in history 86 | set -o notify # (or set -b) # Immediate notif. of background job termination. 87 | set -o ignoreeof # Don't let Ctrl-D exit the shell 88 | 89 | # Other bash settings 90 | PATH="$PATH:/opt/bin" 91 | export MANWIDTH=80 # Manpage width, use < 80 if COLUMNS=80 & less -N 92 | export LC_COLLATE='C' # Set traditional C sort order (e.g. UC first) 93 | export HOSTFILE='/etc/hosts' # Use /etc/hosts for hostname completion 94 | export CDPATH='.:~/:..:../..' # Similar to $PATH, but for use by 'cd' 95 | # Note that the '.' in $CDPATH is needed so that cd will work under POSIX mode 96 | # but this will also cause cd to echo the new directory to STDOUT! 97 | # And see also "cdspell" above! 98 | 99 | # Import bash completion settings, if they exist in the default location 100 | # and if not already imported (e.g. "$BASH_COMPLETION_COMPAT_DIR" NOT set). 101 | # This can take a second or two on a slow system, so you may not always 102 | # want to do it, even if it does exist (which it doesn't by default on many 103 | # systems, e.g. Red Hat). 104 | if [ -z "$BASH_COMPLETION_COMPAT_DIR" ] && ! shopt -oq posix; then 105 | if [ -f /usr/share/bash-completion/bash_completion ]; then 106 | . /usr/share/bash-completion/bash_completion 107 | elif [ -f /etc/bash_completion ]; then 108 | . /etc/bash_completion 109 | fi 110 | fi 111 | 112 | # Use a lesspipe filter, if we can find it. This sets the $LESSOPEN variable. 113 | # Globally replace the $PATH ':' delimiter with space for use in a list. 114 | for path in $SETTINGS /opt/bin ~/ ${PATH//:/ }; do 115 | # Use first one found of 'lesspipe.sh' (preferred) or 'lesspipe' (Debian) 116 | [ -x "$path/lesspipe.sh" ] && eval $("$path/lesspipe.sh") && break 117 | [ -x "$path/lesspipe" ] && eval $("$path/lesspipe") && break 118 | done 119 | 120 | # Set other less & editor prefs (overkill) 121 | export LESS="--LONG-PROMPT --LINE-NUMBERS --ignore-case --QUIET --no-init" 122 | export VISUAL='vi' # Set a default that should always work 123 | # We'd rather use 'type -P' here, but that was added in bash-2.05b and we use 124 | # systems we don't control with versions older than that. We can't easily 125 | # use 'which' since that produces output whether the file is found or not. 126 | #for path in ${PATH//:/ }; do 127 | # # Overwrite VISUAL if we can find nano 128 | # [ -x "$path/nano" ] \ 129 | # && export VISUAL='nano --smooth --const --nowrap --suspend' && break 130 | #done 131 | # See above notes re: nano for why we're using this for loop 132 | for path in ${PATH//:/ }; do 133 | # Alias vi to vim in binary mode if we can 134 | [ -x "$path/vim" ] && alias vi='vim -b' && break 135 | done 136 | export EDITOR="$VISUAL" # Yet Another Possibility 137 | export SVN_EDITOR="$VISUAL" # Subversion 138 | alias edit=$VISUAL # Provide a command to use on all systems 139 | 140 | # Set ls options and aliases. 141 | # Note all the colorizing may or may not work depending on your terminal 142 | # emulation and settings, esp. ANSI color. But it shouldn't hurt to have. 143 | # See above notes re: nano for why we're using this for loop. 144 | for path in ${PATH//:/ }; do 145 | [ -r "$path/dircolors" ] && eval "$(dircolors)" \ 146 | && LS_OPTIONS='--color=auto' && break 147 | done 148 | export LS_OPTIONS="$LS_OPTIONS -F -h" 149 | # Using dircolors may cause csh scripts to fail with an 150 | # "Unknown colorls variable 'do'." error. The culprit is the ":do=01;35:" 151 | # part in the LS_COLORS environment variable. For a possible solution see 152 | # http://forums.macosxhints.com/showthread.php?t=7287 153 | # eval "$(dircolors)" 154 | alias ls="ls $LS_OPTIONS" 155 | alias ll="ls $LS_OPTIONS -l" 156 | alias ll.="ls $LS_OPTIONS -ld" # Usage: ll. ~/.* 157 | alias la="ls $LS_OPTIONS -la" 158 | alias lrt="ls $LS_OPTIONS -alrt" 159 | 160 | # Useful aliases 161 | # Moved to a function: alias bot='cd $(dirname $(find . | tail -1))' 162 | #alias clip='xsel -b' # pipe stuff into right "X" clipboard 163 | alias gc='xsel -b' # "GetClip" get stuff from right "X" clipboard 164 | alias pc='xsel -bi' # "PutClip" put stuff to right "X" clipboard 165 | alias clr='cd ~/ && clear' # Clear and return $HOME 166 | alias cls='clear' # DOS-ish for clear 167 | alias cal='cal -M' # Start calendars on Monday 168 | alias copy='cp' # DOS-ish for cp 169 | #alias cp='cp -i' # Annoying Red Hat default from /root/.bashrc 170 | alias cvsst='cvs -qn update' # Hack to get concise CVS status (like svn st) 171 | alias del='rm' # DOS-ish for rm 172 | alias df='df --print-type --exclude-type=tmpfs --exclude-type=devtmpfs' 173 | alias diff='diff -u' # Make unified diffs the default 174 | alias jdiff="\diff --side-by-side --ignore-case --ignore-blank-lines\ 175 | --ignore-all-space --suppress-common-lines" # Useful GNU diff command 176 | alias dir='ls' # DOS-ish for ls 177 | alias hu='history -n && history -a' # Read new hist. lines; append current lines 178 | alias hr='hu' # "History update" backward compat to 'hr' 179 | alias inxi='inxi -c19' # (Ubuntu) system information script 180 | alias ipconfig='ifconfig' # Windows-ish for ifconfig 181 | alias lesss='less -S' # Don't wrap lines 182 | alias locate='locate -i' # Case-insensitive locate 183 | alias man='LANG=C man' # Display manpages properly 184 | alias md='mkdir' # DOS-ish for mkdir 185 | alias move='mv' # DOS-ish for mv 186 | #alias mv='mv -i' # Annoying Red Hat default from /root/.bashrc 187 | alias ntsysv='rcconf' # Debian rcconf is pretty close to Red Hat ntsysv 188 | #alias open='gnome-open' # Open files & URLs using GNOME handlers; see run below 189 | alias pathping='mtr' # mtr - a network diagnostic tool 190 | alias ping='ping -c4' # Only 4 pings by default 191 | alias r='fc -s' # Recall and execute 'command' starting with... 192 | alias rd='rmdir' # DOS-ish for rmdir 193 | # Tweaked from http://bit.ly/2fc4e8Z 194 | alias randomwords="shuf -n102 /usr/share/dict/words \ 195 | | perl -ne 'print qq(\u\$_);' | column" 196 | alias ren='mv' # DOS-ish for mv/rename 197 | #alias rm='rm -i' # Annoying Red Hat default from /root/.bashrc 198 | alias reloadbind='rndc -k /etc/bind/rndc.key freeze \ 199 | && rndc -k /etc/bind/rndc.key reload && rndc -k /etc/bind/rndc.key thaw' 200 | # Reload dynamic BIND zones after editing db.* files 201 | alias svndiff='meld' # Cool GUI diff, similar to TortoiseMerge 202 | alias svnpropfix='svn propset svn:keywords "id url date"' 203 | alias svnkey='svn propset svn:keywords "id url"' 204 | alias svneol='svn propset svn:eol-style' # One of 'native', 'LF', 'CR', 'CRLF' 205 | alias svnexe='svn propset svn:executable on' 206 | alias top10='sort | uniq -c | sort -rn | head' 207 | alias tracert='traceroute' # DOS-ish for traceroute 208 | alias vzip='unzip -lvM' # View contents of ZIP file 209 | alias wgetdir="wget --no-verbose --recursive --no-parent --no-directories \ 210 | --level=1" # Grab a whole directory using wget 211 | alias wgetsdir="wget --no-verbose --recursive --timestamping --no-parent \ 212 | --no-host-directories --reject 'index.*'" # Grab a dir and subdirs 213 | alias zonex='host -l' # Extract (dump) DNS zone 214 | 215 | # Date/time 216 | alias iso8601="date '+%Y-%m-%dT%H:%M:%S%z'" # ISO 8601 time 217 | alias now="date '+%F %T %Z(%z)'" # More readable ISO 8601 local 218 | alias utc="date --utc '+%F %T %Z(%z)'" # More readable ISO 8601 UTC 219 | 220 | # Neat stuff from http://xmodulo.com/useful-bash-aliases-functions.html 221 | alias meminfo='free -m -l -t' # See how much memory you have left 222 | alias whatpid='ps auwx | grep' # Get PID and process info 223 | alias port='netstat -tulanp' # Show which apps are connecting to the network 224 | 225 | # If the script exists and is executable, create an alias to get 226 | # web server headers 227 | for path in ${PATH//:/ }; do 228 | [ -x "$path/lwp-request" ] && alias httpdinfo='lwp-request -eUd' && break 229 | done 230 | 231 | 232 | # Useful functions 233 | 234 | # Use 'gnome-open' to "run" things 235 | function run { 236 | [ -r "$*" ] && { 237 | gnome-open "$*" >& /dev/null 238 | } || { 239 | echo "'$*' not found or not readable!" 240 | } 241 | } 242 | 243 | 244 | # Python version of 'perl -c' 245 | function python-c { 246 | python -m py_compile "$1" && rm -f "${1}c" 247 | } 248 | 249 | 250 | # cd to the bottom of a narrow but deep dir tree 251 | function bot { 252 | local dir=${1:-.} 253 | #\cd $(dirname $(find $dir | tail -1)) 254 | \cd $(find . -name CVS -prune -o -type d -print | tail -1) 255 | } 256 | 257 | 258 | # mkdir newdir then cd into it 259 | # usage: mcd () 260 | function mcd { 261 | local newdir='_mcd_command_failed_' 262 | if [ -d "$1" ]; then # Dir exists, mention that... 263 | echo "$1 exists..." 264 | newdir="$1" 265 | else 266 | if [ -n "$2" ]; then # We've specified a mode 267 | command mkdir -p -m $1 "$2" && newdir="$2" 268 | else # Plain old mkdir 269 | command mkdir -p "$1" && newdir="$1" 270 | fi 271 | fi 272 | builtin cd "$newdir" # No matter what, cd into it 273 | } # end of mcd 274 | 275 | 276 | # Trivial command-line calculator 277 | function calc { 278 | # INTEGER ONLY! --> echo The answer is: $(( $* )) 279 | # Floating point 280 | awk "BEGIN {print \"$* = \" $* }"; 281 | #awk "BEGIN {printf \"$* = %f\", $* }"; 282 | } # end of calc 283 | function addup { 284 | awk '{sum += $1} END {print sum}' 285 | } 286 | 287 | 288 | # Allow use of 'cd ...' to cd up 2 levels, 'cd ....' up 3, etc. (like 4NT/4DOS) 289 | # Usage: cd ..., etc. 290 | function cd { 291 | 292 | local option= length= count= cdpath= i= # Local scope and start clean 293 | 294 | # If we have a -L or -P symlink option, save then remove it 295 | if [ "$1" = "-P" -o "$1" = "-L" ]; then 296 | option="$1" 297 | shift 298 | fi 299 | 300 | # Are we using the special syntax? Make sure $1 isn't empty, then 301 | # match the first 3 characters of $1 to see if they are '...', then 302 | # make sure there isn't a slash by trying a substitution; if it fails, 303 | # there's no slash. 304 | if [ -n "$1" -a "${1:0:3}" = '...' -a "$1" = "${1%/*}" ]; then 305 | # We are using special syntax 306 | length=${#1} # Assume that $1 has nothing but dots and count them 307 | count=2 # 'cd ..' still means up one level, so ignore first two 308 | 309 | # While we haven't run out of dots, keep cd'ing up 1 level 310 | for ((i=$count;i<=$length;i++)); do 311 | cdpath="${cdpath}../" # Build the cd path 312 | done 313 | 314 | # Actually do the cd 315 | builtin cd $option "$cdpath" 316 | elif [ -n "$1" ]; then 317 | # We are NOT using special syntax; just plain old cd by itself 318 | builtin cd $option "$*" 319 | else 320 | # We are NOT using special syntax; plain old cd by itself to home dir 321 | builtin cd $option 322 | fi 323 | } # end of cd 324 | 325 | 326 | # Do site- or host-specific things here 327 | case $HOSTNAME in 328 | *.company.com ) # source $SETTINGS/company.com 329 | ;; 330 | host1.* ) # host1 stuff 331 | ;; 332 | host2.company.com ) # source .bashrc.host2 333 | ;; 334 | drake.* ) # echo DRAKE in bashrc.jp! 335 | export TAPE=/dev/tape 336 | ;; 337 | esac 338 | -------------------------------------------------------------------------------- /ch16/builtin_tty.c: -------------------------------------------------------------------------------- 1 | # cookbook filename: builtin_tty.c 2 | 3 | #include "config.h" 4 | #include 5 | #include "builtins.h" 6 | #include "shell.h" 7 | #include "bashgetopt.h" 8 | 9 | 10 | extern char *ttyname ( ); 11 | 12 | tty_builtin (list) 13 | WORD_LIST *list; 14 | { 15 | int opt, sflag; 16 | char *t; 17 | 18 | reset_internal_getopt ( ); 19 | sflag = 0; 20 | while ((opt = internal_getopt (list, "s")) != -1) 21 | { 22 | switch (opt) 23 | { 24 | case 's': 25 | sflag = 1; 26 | break; 27 | default: 28 | builtin_usage ( ); 29 | return (EX_USAGE); 30 | } 31 | } 32 | list = loptend; 33 | 34 | t = ttyname (0); 35 | if (sflag == 0) 36 | puts (t ? t : "not a tty"); 37 | return (t ? EXECUTION_SUCCESS : EXECUTION_FAILURE); 38 | } 39 | 40 | char *tty_doc[] = { 41 | "tty writes the name of the terminal that is opened for standard", 42 | "input to standard output. If the `-s' option is supplied, nothing", 43 | "is written; the exit status determines whether or not the standard", 44 | "input is connected to a tty.", 45 | (char *)NULL 46 | }; 47 | 48 | struct builtin tty_struct = { 49 | "tty", 50 | tty_builtin, 51 | BUILTIN_ENABLED, 52 | tty_doc, 53 | "tty [-s]", 54 | 0 55 | }; 56 | -------------------------------------------------------------------------------- /ch16/colors: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: colors 3 | # 4 | # Daniel Crisman's ANSI color chart script from 5 | # The Bash Prompt HOWTO: 6.1. Colours 6 | # http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html. 7 | # 8 | # This file echoes a bunch of color codes to the 9 | # terminal to demonstrate what's available. Each 10 | # line is the color code of one foreground color, 11 | # out of 17 (default + 16 escapes), followed by a 12 | # test use of that color on all nine background 13 | # colors (default + 8 escapes). 14 | # 15 | 16 | T='gYw' # The test text 17 | 18 | echo -e "\n 40m 41m 42m 43m\ 19 | 44m 45m 46m 47m"; 20 | 21 | for FGs in ' m' ' 1m' ' 30m' '1;30m' ' 31m' '1;31m' ' 32m' \ 22 | '1;32m' ' 33m' '1;33m' ' 34m' '1;34m' ' 35m' '1;35m' \ 23 | ' 36m' '1;36m' ' 37m' '1;37m'; do 24 | FG=${FGs// /} 25 | echo -en " $FGs \033[$FG $T " 26 | for BG in 40m 41m 42m 43m 44m 45m 46m 47m; do 27 | echo -en "$EINS \033[$FG\033[$BG $T \033[0m"; 28 | done 29 | echo; 30 | done 31 | echo 32 | -------------------------------------------------------------------------------- /ch16/func_cd: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_cd 2 | 3 | # Allow use of 'cd ...' to cd up 2 levels, 'cd ....' up 3, etc. (like 4NT/4DOS) 4 | # Usage: cd ..., etc. 5 | function cd { 6 | 7 | local option= length= count= cdpath= i= # Local scope and start clean 8 | 9 | # If we have a -L or -P symlink option, save then remove it 10 | if [ "$1" = "-P" -o "$1" = "-L" ]; then 11 | option="$1" 12 | shift 13 | fi 14 | 15 | # Are we using the special syntax? Make sure $1 isn't empty, then 16 | # match the first 3 characters of $1 to see if they are '...', then 17 | # make sure there isn't a slash by trying a substitution; if it fails, 18 | # there's no slash. 19 | if [ -n "$1" -a "${1:0:3}" = '...' -a "$1" = "${1%/*}" ]; then 20 | # We are using special syntax 21 | length=${#1} # Assume that $1 has nothing but dots and count them 22 | count=2 # 'cd ..' still means up one level, so ignore first two 23 | 24 | # While we haven't run out of dots, keep cd'ing up 1 level 25 | for ((i=$count;i<=$length;i++)); do 26 | cdpath="${cdpath}../" # Build the cd path 27 | done 28 | 29 | # Actually do the cd 30 | builtin cd $option "$cdpath" 31 | elif [ -n "$1" ]; then 32 | # We are NOT using special syntax; just plain old cd by itself 33 | builtin cd $option "$*" 34 | else 35 | # We are NOT using special syntax; plain old cd by itself to home dir 36 | builtin cd $option 37 | fi 38 | } # end of cd 39 | -------------------------------------------------------------------------------- /ch16/func_kill: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_kill 2 | 3 | _kill() { 4 | local cur 5 | local sign 6 | 7 | COMPREPLY=( ) 8 | cur=${COMP_WORDS[COMP_CWORD]} 9 | 10 | if (($COMP_CWORD == 2)) && [[ ${COMP_WORDS[1]} == -n ]]; then 11 | # return list of available signals 12 | _signals 13 | elif (($COMP_CWORD == 1 )) && [[ "$cur" == -* ]]; then 14 | # return list of available signals 15 | sign="-" 16 | _signals 17 | else 18 | # return list of available PIDs 19 | COMPREPLY=( $( compgen -W '$( command ps axo pid | sed 1d )' $cur ) ) 20 | fi 21 | } 22 | -------------------------------------------------------------------------------- /ch16/func_mcd: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_mcd 2 | 3 | # mkdir newdir then cd into it 4 | # usage: mcd () 5 | function mcd { 6 | local newdir='_mcd_command_failed_' 7 | if [ -d "$1" ]; then # Dir exists, mention that... 8 | echo "$1 exists..." 9 | newdir="$1" 10 | else 11 | if [ -n "$2" ]; then # We've specified a mode 12 | command mkdir -p -m $1 "$2" && newdir="$2" 13 | else # Plain old mkdir 14 | command mkdir -p "$1" && newdir="$1" 15 | fi 16 | fi 17 | builtin cd "$newdir" # No matter what, cd into it 18 | } # end of mcd 19 | -------------------------------------------------------------------------------- /ch16/func_pathmunge: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_pathmunge 2 | 3 | # Adapted from Red Hat Linux 4 | 5 | function pathmunge { 6 | if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then 7 | if [ "$2" = "after" ] ; then 8 | PATH="$PATH:$1" 9 | else 10 | PATH="$1:$PATH" 11 | fi 12 | fi 13 | } 14 | -------------------------------------------------------------------------------- /ch16/func_signals: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_signals 2 | 3 | _signals() { 4 | local i 5 | 6 | COMPREPLY=( $( compgen -A signal SIG${cur#-} )) 7 | 8 | for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do 9 | COMPREPLY[i]=$sign${COMPREPLY[i]#SIG} 10 | done 11 | } 12 | -------------------------------------------------------------------------------- /ch16/func_trunc_PWD: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_trunc_PWD 2 | 3 | function trunc_PWD { 4 | # $PWD truncation code adapted from The Bash Prompt HOWTO: 5 | # 11.10. Controlling the Size and Appearance of $PWD 6 | # http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html 7 | 8 | # How many characters of the $PWD should be kept 9 | local pwdmaxlen=30 10 | # Indicator that there has been directory truncation: 11 | local trunc_symbol='...' 12 | # Temp variable for PWD 13 | local myPWD=$PWD 14 | 15 | # Replace any leading part of $PWD that matches $HOME with '~' 16 | # OPTIONAL, comment out if you want the full path! 17 | myPWD=${PWD/$HOME/~} 18 | 19 | if [ ${#myPWD} -gt $pwdmaxlen ]; then 20 | local pwdoffset=$(( ${#myPWD} - $pwdmaxlen )) 21 | echo "${trunc_symbol}${myPWD:$pwdoffset:$pwdmaxlen}" 22 | else 23 | echo "$myPWD" 24 | fi 25 | } 26 | -------------------------------------------------------------------------------- /ch16/func_tweak_path: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_tweak_path 2 | 3 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | # Add a directory to the beginning or end of your path as long as it's not 5 | # already present. Does not take into account symbolic links! 6 | # Returns: 1 or sets the new $PATH 7 | # Called like: add_to_path (pre|post) 8 | function add_to_path { 9 | local location=$1 10 | local directory=$2 11 | 12 | # Make sure we have something to work with 13 | if [ -z "$location" -o -z "$directory" ]; then 14 | echo "$0:$FUNCNAME: requires a location and a directory to add" >&2 15 | echo "Usage: add_to_path (pre|post) " >&2 16 | echo "e.g. add_to_path pre /bin" >&2 17 | return 1 18 | fi 19 | 20 | # Make sure the directory is not relative 21 | if [ $(echo $directory | grep '^/') ]; then 22 | : echo "$0:$FUNCNAME: '$directory' is absolute" >&2 23 | else 24 | echo "$0:$FUNCNAME: can't add relative directory '$directory' to \$PATH" >&2 25 | return 1 26 | fi 27 | 28 | # Make sure the directory to add actually exists 29 | if [ -d "$directory" ]; then 30 | : echo "$0:$FUNCNAME: directory exists" >&2 31 | else 32 | echo "$0:$FUNCNAME: '$directory' does not exist--aborting" >&2 33 | return 1 34 | fi 35 | 36 | # Make sure it's not already in the $PATH 37 | if $(contains "$PATH" "$directory"); then 38 | : echo "$0:$FUNCNAME: adding directory to \$PATH" >&2 39 | else 40 | echo "$0:$FUNCNAME: '$directory' already in \$PATH--aborting" >&2 41 | fi 42 | 43 | # Figure out what to do 44 | case $location in 45 | pre* ) PATH="$directory:$PATH" ;; 46 | post* ) PATH="$PATH:$directory" ;; 47 | * ) PATH="$PATH:$directory" ;; 48 | esac 49 | 50 | # Clean up the new path, then set it 51 | PATH=$(clean_path $PATH) 52 | 53 | } # end of function add_to_path 54 | 55 | 56 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 57 | # Remove a directory from your path, if present. 58 | # Returns: sets the new $PATH 59 | # Called like: rm_from_path 60 | function rm_from_path { 61 | local directory=$1 62 | 63 | # Remove all instances of $directory from $PATH 64 | PATH=${PATH//$directory/} 65 | 66 | # Clean up the new path, then set it 67 | PATH=$(clean_path $PATH) 68 | 69 | } # end of function rm_from_path 70 | 71 | 72 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 73 | # Remove leading/trailing or duplicate ':', remove duplicate entries 74 | # Returns: echoes the "cleaned up" path 75 | # Called like: cleaned_path=$(clean_path $PATH) 76 | function clean_path { 77 | local path=$1 78 | local newpath 79 | local directory 80 | 81 | # Make sure we have something to work with 82 | [ -z "$path" ] && return 1 83 | 84 | # Remove duplicate directories, if any 85 | for directory in ${path//:/ }; do 86 | contains "$newpath" "$directory" && newpath="${newpath}:${directory}" 87 | done 88 | 89 | # Remove any leading ':' separators 90 | # Remove any trailing ':' separators 91 | # Remove any duplicate ':' separators 92 | newpath=$(echo $newpath | sed 's/^:*//; s/:*$//; s/::/:/g') 93 | 94 | # Return the new path 95 | echo $newpath 96 | 97 | } # end of function clean_path 98 | 99 | 100 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 101 | # Determine if the path contains a given directory 102 | # Return 1 if target is contained within pattern, 0 otherwise 103 | # Called like: contains $PATH $dir 104 | function contains { 105 | local pattern=":$1:" 106 | local target=$2 107 | 108 | # This will be a case-sensitive comparison unless nocasematch is set 109 | case $pattern in 110 | *:$target:* ) return 1 ;; 111 | * ) return 0 ;; 112 | esac 113 | } # end of function contains 114 | -------------------------------------------------------------------------------- /ch16/inputrc: -------------------------------------------------------------------------------- 1 | # cookbook filename: inputrc 2 | # settings/inputrc: # readline settings 3 | # To reread (and implement changes to this file) use: 4 | # bind -f $SETTINGS/inputrc 5 | 6 | # First, include any system-wide bindings and variable 7 | # assignments from /etc/inputrc 8 | # (fails silently if file doesn't exist) 9 | $include /etc/inputrc 10 | 11 | $if Bash 12 | # Ignore case when doing completion 13 | set completion-ignore-case on 14 | # Completed dir names have a slash appended 15 | set mark-directories on 16 | # Completed names which are symlinks to dirs have a slash appended 17 | set mark-symlinked-directories on 18 | # List ls -F for completion 19 | set visible-stats on 20 | # Cycle through ambiguous completions instead of list 21 | "\C-i": menu-complete 22 | # Set bell to audible 23 | set bell-style audible 24 | # List possible completions instead of ringing bell 25 | set show-all-if-ambiguous on 26 | 27 | # From the readline documentation at 28 | # https://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC12 29 | # Macros that are convenient for shell interaction 30 | # edit the path 31 | "\C-xp": "PATH=${PATH}\e\C-e\C-a\ef\C-f" 32 | # prepare to type a quoted word -- insert open and close double quotes 33 | # and move to just after the open quote 34 | "\C-x\"": "\"\"\C-b" 35 | # insert a backslash (testing backslash escapes in sequences and macros) 36 | "\C-x\\": "\\" 37 | # Quote the current or previous word 38 | "\C-xq": "\eb\"\ef\"" 39 | # Add a binding to refresh the line, which is unbound 40 | "\C-xr": redraw-current-line 41 | # Edit variable on current line. 42 | #"\M-\C-v": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y=" 43 | "\C-xe": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y=" 44 | $endif 45 | 46 | # some defaults / modifications for the emacs mode 47 | $if mode=emacs 48 | 49 | # allow the use of the Home/End keys 50 | "\e[1~": beginning-of-line 51 | "\e[4~": end-of-line 52 | 53 | # allow the use of the Delete/Insert keys 54 | "\e[3~": delete-char 55 | "\e[2~": quoted-insert 56 | 57 | # mappings for "page up" and "page down" to step to beginning/end of the history 58 | # "\e[5~": beginning-of-history 59 | # "\e[6~": end-of-history 60 | 61 | # alternate mappings for "page up" and "page down" to search the history 62 | # "\e[5~": history-search-backward 63 | # "\e[6~": history-search-forward 64 | 65 | # MUCH nicer up-arrow search behavior! 66 | "\e[A": history-search-backward ## up-arrow 67 | "\e[B": history-search-forward ## down-arrow 68 | 69 | # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving 70 | ### These were/are broken, and /etc/inputrc has better anyway 71 | # "\e[5C": forward-word 72 | # "\e[5D": backward-word 73 | # "\e\e[C": forward-word 74 | # "\e\e[D": backward-word 75 | 76 | # for non RH/Debian xterm, can't hurt for RH/Debian xterm 77 | "\eOH": beginning-of-line 78 | "\eOF": end-of-line 79 | 80 | # for FreeBSD console 81 | "\e[H": beginning-of-line 82 | "\e[F": end-of-line 83 | 84 | $endif 85 | -------------------------------------------------------------------------------- /ch16/prompts: -------------------------------------------------------------------------------- 1 | # cookbook filename: prompts 2 | 3 | # Username @ short hostname, the date and time, and the current working 4 | # directory (CWD): 5 | export PS1='[\u@\h \d \A] \w \$ ' 6 | 7 | # Username @ long hostname, the date and time in ISO 8601 format, and the 8 | # basename of the current working directory (\W): 9 | export PS1='[\u@\H \D{%Y-%m-%d %H:%M:%S%z}] \W \$ ' 10 | 11 | # Username @ short hostname, bash version, and the current working 12 | # directory (\w): 13 | export PS1='[\u@\h \V \w] \$ ' 14 | 15 | # Newline, username @ short hostname, base PTY, shell level, history number, 16 | # newline, and full working directory name ($PWD): 17 | export PS1='\n[\u@\h \l:$SHLVL:\!]\n$PWD\$ ' 18 | 19 | # Username @ short hostname, the exit status of the last command, and the 20 | # current working directory: 21 | export PS1='[\u@\h $? \w \$ ' 22 | 23 | # Newline, username @ short hostname, and the number of jobs 24 | # in the background: 25 | export PS1='\n[\u@\h jobs:\j]\n$PWD\$ ' 26 | 27 | # Newline, username @ short hostname, terminal, shell, level, history, jobs, 28 | # version and full working directory name: 29 | export PS1='\n[\u@\h t:\l l:$SHLVL h:\! j:\j v:\V]\n$PWD\$ ' 30 | 31 | # Newline, username @ short hostname, T for terminal, L for shell level, C 32 | # command number, and the date and time in ISO 8601 format: 33 | export PS1='\n[\u@\h:T\l:L$SHLVL:C\!:\D{%Y-%m-%d_%H:%M:%S_%Z}]\n$PWD\$ ' 34 | 35 | # Username @ short hostname, and the current working directory in light 36 | # blue: 37 | export PS1='\[\033[1;34m\][\u@\h:\w]\$\[\033[0m\] ' 38 | 39 | # Username @ short hostname, and the current working directory in both the 40 | # xterm title bar and the prompt itself: 41 | export PS1='\[\033]0;\u@\h:\w\007\][\u@\h:\w]\$ ' 42 | 43 | # Both color and xterm updates: 44 | export PS1='\[\033]0;\u@\h:\w\007\]\[\033[1;34m\][\u@\h:\w]\$\[\033[0m\] ' 45 | -------------------------------------------------------------------------------- /ch16/run_screen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: run_screen 3 | # run_screen--Wrapper script intended to run from a "profile" file to run 4 | # screen at login time with a friendly menu. 5 | 6 | # Sanity check 7 | if [ "$TERM" == "screen" -o "$TERM" == "screen-256color" ]; then 8 | printf "%b" "According to \$TERM = '$TERM' we're *already* using" \ 9 | " screen.\nAborting...\n" 10 | exit 1 11 | elif [ "$USING_SCREEN" == "YES" ]; then 12 | printf "%b" "According to \$USING_SCREEN = '$USING_SCREEN' we're" 13 | " *already* using screen.\nAborting...\n" 14 | exit 1 15 | fi 16 | 17 | # The "$USING_SCREEN" variable is for the rare cases when screen does NOT set 18 | # $TERM=screen. This can happen when 'screen' is not in TERMCAP or friends, 19 | # as is the case on a Solaris 9 box we use but don't control. If we don't 20 | # have some way to tell when we're inside screen, this wrapper goes into an 21 | # ugly and confusing endless loop. 22 | 23 | # Seed list with Exit and New options and see what screens are already running. 24 | # The select list is whitespace-delimited, and we only want actual screen 25 | # sessions, so use perl to remove whitespace, filter for sessions, and show 26 | # only useful info from 'screen -ls' output. 27 | available_screens="Exit New $(screen -ls \ 28 | | perl -ne 's/\s+//g; print if s/^(\d+\..*?)(?:\(.*?\))?(\(.*?\))$/$1$2\n/;')" 29 | 30 | # Print a warning if using runtime feedback 31 | run_time_feedback=0 32 | [ "$run_time_feedback" == 1 ] && printf "%b" " 33 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 34 | 'screen' Notes: 35 | 36 | 1) Sessions marked 'unreachable' or 'dead' should be investigated and 37 | removed with the -wipe option if appropriate.\n\n" 38 | 39 | 40 | # Present a list of choices 41 | PS3='Choose a screen for this session: ' 42 | select selection in $available_screens; do 43 | if [ "$selection" == "Exit" ]; then 44 | break 45 | elif [ "$selection" == "New" ]; then 46 | export USING_SCREEN=YES 47 | exec screen -c $SETTINGS/screenrc -a \ 48 | -S $USER.$(date '+%Y-%m-%d_%H:%M:%S%z') 49 | break 50 | elif [ "$selection" ]; then 51 | # Pull out just the part we need using cut 52 | # We'd rather use a 'here string' [$(cut -d'(' -f1 <<< $selection)] 53 | # than this echo, but they are only in bash-2.05b+ 54 | screen_to_use="$(echo $selection | cut -d'(' -f1)" 55 | # Old: exec screen -dr $screen_to_use 56 | # Alt: exec screen -x $USER/$screen_to_use 57 | exec screen -r $USER/$screen_to_use 58 | break 59 | else 60 | printf "%b" "Invalid selection.\n" 61 | fi 62 | done 63 | -------------------------------------------------------------------------------- /ch17/archive_meta-data: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: archive_meta-data 3 | 4 | printf "%b" "Mode\tUser\tGroup\tBytes\tModified\tFileSpec\n" > archive_file 5 | find / \( -path /proc -o -path /mnt -o -path /tmp -o -path /var/tmp \ 6 | -o -path /var/cache -o -path /var/spool \) -prune \ 7 | -o -type d -printf 'd%m\t%u\t%g\t%s\t%t\t%p/\n' \ 8 | -o -type l -printf 'l%m\t%u\t%g\t%s\t%t\t%p -> %l\n' \ 9 | -o -printf '%m\t%u\t%g\t%s\t%t\t%p\n' >> archive_file 10 | -------------------------------------------------------------------------------- /ch17/ff-sessions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: ff-sessions 3 | # Save/Restore FF sessions 4 | 5 | # Run from cron like: 6 | # 45 03,15 * * * opt/bin/ff-sess.sh qsave # <1> 7 | 8 | FF_DIR="$HOME/.mozilla/firefox" 9 | date=$(date '+%u_%a_%H') # e.g.: 3_Wed_15 # <2> 10 | 11 | case "$1" in 12 | qsave ) # Quiet save 13 | cd $FF_DIR 14 | rm -f ff_sessions_$date.zip # <3> 15 | zip -9qr ff_sessions_$date.zip */session* # <4> 16 | ;; 17 | 18 | save ) # Noisy save (calls qsave) 19 | echo "SAVING '$FF_DIR/*/session*' data into '$date' file" # <5> 20 | $0 qsave # <6> 21 | ;; 22 | 23 | restore ) 24 | [ -z "$2" ] && { echo "Need a date to restore from!"; exit 1; } # <7> 25 | date="$2" # <8> 26 | echo "Restoring session data from '$date' file" 27 | cd $FF_DIR 28 | unzip -o ff_sessions_$date.zip # <9> 29 | ;; 30 | 31 | * ) # <10> 32 | echo 'Save/Restore FF sessions' 33 | echo "$0 save" 34 | echo "$0 restore " 35 | echo " e.g., $0 restore 3_Wed_15" 36 | ;; 37 | esac 38 | -------------------------------------------------------------------------------- /ch17/func_commify: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_commify 2 | 3 | function commify { 4 | typeset text=${1} 5 | 6 | typeset bdot=${text%%.*} 7 | typeset adot=${text#${bdot}} 8 | 9 | typeset i commified 10 | (( i = ${#bdot} - 1 )) 11 | 12 | while (( i>=3 )) && [[ ${bdot:i-3:1} == [0-9] ]]; do 13 | commified=",${bdot:i-2:3}${commified}" 14 | (( i -= 3 )) 15 | done 16 | echo "${bdot:0:i+1}${commified}${adot}" 17 | } 18 | -------------------------------------------------------------------------------- /ch17/func_shift_by: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_shift_by 2 | 3 | # Pop a given number of items from the top of a stack, 4 | # such that you can then perform an action on whatever is left. 5 | # Called like: shift_by <# to keep> 6 | # Returns: the remainder of the stack or list 7 | # 8 | # For example, list some objects, then keep only the top 10. 9 | # 10 | # It is CRITICAL that you pass the items in order with the objects to 11 | # be removed at the top (or front) of the list, since all this function 12 | # does is remove (pop) the number of entries you specify from the top 13 | # of the list. 14 | # 15 | # You should experiment with echo before using rm! 16 | # 17 | # For example: 18 | # rm -rf $(shift_by $MAX_BUILD_DIRS_TO_KEEP $(ls -rd backup.2006*)) 19 | # 20 | function shift_by { 21 | 22 | # If $1 is zero or greater than $#, the positional parameters are 23 | # not changed. In this case that is a BAD THING! 24 | if (( $1 == 0 || $1 > ( $# - 1 ) )); then 25 | echo '' 26 | else 27 | # Remove the given number of objects (plus 1) from the list. 28 | shift $(( $1 + 1 )) 29 | # Return whatever is left. 30 | echo "$*" 31 | fi 32 | } 33 | -------------------------------------------------------------------------------- /ch17/is_process_running: -------------------------------------------------------------------------------- 1 | # cookbook filename: is_process_running 2 | 3 | # Can you believe this?!? 4 | case `uname` in 5 | Linux|AIX) PS_ARGS='-ewwo pid,args' ;; 6 | SunOS) PS_ARGS='-eo pid,args' ;; 7 | *BSD) PS_ARGS='axwwo pid,args' ;; 8 | Darwin) PS_ARGS='Awwo pid,command' ;; 9 | esac 10 | 11 | if ps $PS_ARGS | grep -q 'bin/[s]shd'; then 12 | echo 'sshd is running' 13 | else 14 | echo 'sshd not running' 15 | fi 16 | -------------------------------------------------------------------------------- /ch17/perl_sub_commify: -------------------------------------------------------------------------------- 1 | # cookbook filename: perl_sub_commify 2 | 3 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | # Add comma thousands separator to numbers 5 | # Returns: input string, with any numbers commified 6 | # From Perl Cookbook2 2.16, pg 84 7 | sub commify { 8 | @_ == 1 or carp ('Sub usage: $withcomma = commify($somenumber);'); 9 | 10 | # From _Perl_Cookbook_1 2.17, pg 64, or _Perl_Cookbook_2 2.16, pg 84 11 | my $text = reverse $_[0]; 12 | $text =~ s/(\d\d\d)(?=\d)(?!\d*\.)/$1,/g; 13 | return scalar reverse $text; 14 | } 15 | -------------------------------------------------------------------------------- /ch19/buggy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: buggy 3 | # 4 | 5 | set -x 6 | 7 | result=$1 8 | 9 | [ $result = 1 ] \ 10 | && { echo "Result is 1; excellent." ; exit 0; } \ 11 | || { echo "Uh-oh, ummm, RUN AWAY! " ; exit 120; } 12 | -------------------------------------------------------------------------------- /lib/func_calc: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_calc 2 | 3 | # Trivial command-line calculator 4 | function calc { 5 | # INTEGER ONLY! --> echo The answer is: $(( $* )) 6 | # Floating point 7 | awk "BEGIN {print \"The answer is: \" $* }"; 8 | } # end of calc 9 | -------------------------------------------------------------------------------- /lib/func_cd: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_cd 2 | 3 | # Allow use of 'cd ...' to cd up 2 levels, 'cd ....' up 3, etc. (like 4NT/4DOS) 4 | # Usage: cd ..., etc. 5 | function cd { 6 | 7 | local option= length= count= cdpath= i= # Local scope and start clean 8 | 9 | # If we have a -L or -P symlink option, save then remove it 10 | if [ "$1" = "-P" -o "$1" = "-L" ]; then 11 | option="$1" 12 | shift 13 | fi 14 | 15 | # Are we using the special syntax? Make sure $1 isn't empty, then 16 | # match the first 3 characters of $1 to see if they are '...', then 17 | # make sure there isn't a slash by trying a substitution; if it fails, 18 | # there's no slash. 19 | if [ -n "$1" -a "${1:0:3}" = '...' -a "$1" = "${1%/*}" ]; then 20 | # We are using special syntax 21 | length=${#1} # Assume that $1 has nothing but dots and count them 22 | count=2 # 'cd ..' still means up one level, so ignore first two 23 | 24 | # While we haven't run out of dots, keep cd'ing up 1 level 25 | for ((i=$count;i<=$length;i++)); do 26 | cdpath="${cdpath}../" # Build the cd path 27 | done 28 | 29 | # Actually do the cd 30 | builtin cd $option "$cdpath" 31 | elif [ -n "$1" ]; then 32 | # We are NOT using special syntax; just plain old cd by itself 33 | builtin cd $option "$*" 34 | else 35 | # We are NOT using special syntax; plain old cd by itself to home dir 36 | builtin cd $option 37 | fi 38 | } # end of cd 39 | -------------------------------------------------------------------------------- /lib/func_choice: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choice 2 | 3 | function choice { 4 | # Let the user make a choice about something and return a standardized 5 | # answer. How the default is handled and what happens next is up to 6 | # the if/then after the choice in main 7 | 8 | local answer 9 | printf "%b" "\a" # Ring the bell 10 | read -p "$*" answer 11 | case "$answer" in 12 | [yY1] ) choice='y';; 13 | [nN0] ) choice='n';; 14 | * ) choice="$answer";; 15 | esac 16 | } # end of function choice 17 | -------------------------------------------------------------------------------- /lib/func_choice.1: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choice.1 2 | 3 | # Let the user make a choice about something and return a standardized 4 | # answer. How the default is handled and what happens next is up to 5 | # the if/then after the choice in main. 6 | # Called like: choice 7 | # e.g. choice "Do you want to play a game?" 8 | # Returns: global variable CHOICE 9 | function choice { 10 | 11 | CHOICE='' 12 | local prompt="$*" 13 | local answer 14 | 15 | read -p "$prompt" answer 16 | case "$answer" in 17 | [yY1] ) CHOICE='y';; 18 | [nN0] ) CHOICE='n';; 19 | * ) CHOICE="$answer";; 20 | esac 21 | } # end of function choice 22 | -------------------------------------------------------------------------------- /lib/func_choice.2: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choice.2 2 | CHOICE='' 3 | until [ "$CHOICE" = "y" ]; do 4 | printf "%b" "This package's date is $THISPACKAGE\n" >&2 5 | choice "Is that correct? [Y/,]: " 6 | if [ -z "$CHOICE" ]; then 7 | CHOICE='y' 8 | elif [ "$CHOICE" != "y" ]; then 9 | printf "%b" "Overriding $THISPACKAGE with $CHOICE\n" 10 | THISPACKAGE=$CHOICE 11 | fi 12 | done 13 | 14 | # Build the package here 15 | -------------------------------------------------------------------------------- /lib/func_choice.3: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choice.3 2 | 3 | choice "Enter your favorite color, if you have one: " 4 | if [ -n "$CHOICE" ]; then 5 | printf "%b" "You chose: $CHOICE\n" 6 | else 7 | printf "%b" "You do not have a favorite color.\n" 8 | fi 9 | -------------------------------------------------------------------------------- /lib/func_choose: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_choose 2 | 3 | # Let the user make a choice about something and execute code based on 4 | # the answer 5 | # Called like: choose 6 | # e.g. choose "y" \ 7 | # "Do you want to play a game?" \ 8 | # /usr/games/GlobalThermonuclearWar \ 9 | # 'printf "%b" "See you later Professor Falkin.\n"' >&2 10 | # Returns: nothing 11 | function choose { 12 | 13 | local default="$1" 14 | local prompt="$2" 15 | local choice_yes="$3" 16 | local choice_no="$4" 17 | local answer 18 | 19 | read -p "$prompt" answer 20 | [ -z "$answer" ] && answer="$default" 21 | 22 | case "$answer" in 23 | [yY1] ) eval "$choice_yes" 24 | # error check 25 | ;; 26 | [nN0] ) eval "$choice_no" 27 | # error check 28 | ;; 29 | * ) printf "%b" "Unexpected answer '$answer'!" >&2 ;; 30 | esac 31 | } # end of function choose 32 | -------------------------------------------------------------------------------- /lib/func_commify: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_commify 2 | 3 | function commify { 4 | typeset text=${1} 5 | 6 | typeset bdot=${text%%.*} 7 | typeset adot=${text#${bdot}} 8 | 9 | typeset i commified 10 | (( i = ${#bdot} - 1 )) 11 | 12 | while (( i>=3 )) && [[ ${bdot:i-3:1} == [0-9] ]]; do 13 | commified=",${bdot:i-2:3}${commified}" 14 | (( i -= 3 )) 15 | done 16 | echo "${bdot:0:i+1}${commified}${adot}" 17 | } 18 | -------------------------------------------------------------------------------- /lib/func_kill: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_kill 2 | 3 | _kill() { 4 | local cur 5 | local sign 6 | 7 | COMPREPLY=( ) 8 | cur=${COMP_WORDS[COMP_CWORD]} 9 | 10 | if (($COMP_CWORD == 2)) && [[ ${COMP_WORDS[1]} == -n ]]; then 11 | # return list of available signals 12 | _signals 13 | elif (($COMP_CWORD == 1 )) && [[ "$cur" == -* ]]; then 14 | # return list of available signals 15 | sign="-" 16 | _signals 17 | else 18 | # return list of available PIDs 19 | COMPREPLY=( $( compgen -W '$( command ps axo pid | sed 1d )' $cur ) ) 20 | fi 21 | } 22 | -------------------------------------------------------------------------------- /lib/func_max.1: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_max.1 2 | 3 | # define the function: 4 | function max () 5 | { 6 | local HIDN 7 | if [ $1 -gt $2 ] 8 | then 9 | BIGR=$1 10 | else 11 | BIGR=$2 12 | fi 13 | HIDN=5 14 | } 15 | -------------------------------------------------------------------------------- /lib/func_max.2: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_max.2 2 | 3 | # define the function: 4 | function max () 5 | { 6 | if [ $1 -gt $2 ] 7 | then 8 | echo $1 9 | else 10 | echo $2 11 | fi 12 | } 13 | -------------------------------------------------------------------------------- /lib/func_mcd: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_mcd 2 | 3 | # mkdir newdir then cd into it 4 | # usage: mcd () 5 | function mcd { 6 | local newdir='_mcd_command_failed_' 7 | if [ -d "$1" ]; then # Dir exists, mention that... 8 | echo "$1 exists..." 9 | newdir="$1" 10 | else 11 | if [ -n "$2" ]; then # We've specified a mode 12 | command mkdir -p -m $1 "$2" && newdir="$2" 13 | else # Plain old mkdir 14 | command mkdir -p "$1" && newdir="$1" 15 | fi 16 | fi 17 | builtin cd "$newdir" # No matter what, cd into it 18 | } # end of mcd 19 | -------------------------------------------------------------------------------- /lib/func_pathmunge: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_pathmunge 2 | 3 | # Adapted from Red Hat Linux 4 | 5 | function pathmunge { 6 | if ! echo $PATH | /bin/egrep -q "(^|:)$1($|:)" ; then 7 | if [ "$2" = "after" ] ; then 8 | PATH="$PATH:$1" 9 | else 10 | PATH="$1:$PATH" 11 | fi 12 | fi 13 | } 14 | -------------------------------------------------------------------------------- /lib/func_shift_by: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_shift_by 2 | 3 | # Pop a given number of items from the top of a stack, 4 | # such that you can then perform an action on whatever is left. 5 | # Called like: shift_by <# to keep> 6 | # Returns: the remainder of the stack or list 7 | # 8 | # For example, list some objects, then keep only the top 10. 9 | # 10 | # It is CRITICAL that you pass the items in order with the objects to 11 | # be removed at the top (or front) of the list, since all this function 12 | # does is remove (pop) the number of entries you specify from the top 13 | # of the list. 14 | # 15 | # You should experiment with echo before using rm! 16 | # 17 | # For example: 18 | # rm -rf $(shift_by $MAX_BUILD_DIRS_TO_KEEP $(ls -rd backup.2006*)) 19 | # 20 | function shift_by { 21 | 22 | # If $1 is zero or greater than $#, the positional parameters are 23 | # not changed. In this case that is a BAD THING! 24 | if (( $1 == 0 || $1 > ( $# - 1 ) )); then 25 | echo '' 26 | else 27 | # Remove the given number of objects (plus 1) from the list. 28 | shift $(( $1 + 1 )) 29 | # Return whatever is left. 30 | echo "$*" 31 | fi 32 | } 33 | -------------------------------------------------------------------------------- /lib/func_signals: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_signals 2 | 3 | _signals() { 4 | local i 5 | 6 | COMPREPLY=( $( compgen -A signal SIG${cur#-} )) 7 | 8 | for (( i=0; i < ${#COMPREPLY[@]}; i++ )); do 9 | COMPREPLY[i]=$sign${COMPREPLY[i]#SIG} 10 | done 11 | } 12 | -------------------------------------------------------------------------------- /lib/func_split: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_split 2 | 3 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | # Output fixed-size pieces of input ONLY if the limit is exceeded 5 | # Called like: Split 6 | # e.g. Split $output ${output}_ --lines 100 7 | # See split(1) and wc(1) for option details 8 | function Split { 9 | local file=$1 10 | local prefix=$2 11 | local limit_type=$3 12 | local limit_size=$4 13 | local wc_option 14 | 15 | # Sanity checks 16 | if [ -z "$file" ]; then 17 | printf "%b" "Split: requires a file name!\n" 18 | return 1 19 | fi 20 | if [ -z "$prefix" ]; then 21 | printf "%b" "Split: requires an output file prefix!\n" 22 | return 1 23 | fi 24 | if [ -z "$limit_type" ]; then 25 | printf "%b" \ 26 | "Split: requires a limit option (e.g. --lines), see 'man split'!\n" 27 | return 1 28 | fi 29 | if [ -z "$limit_size" ]; then 30 | printf "%b" "Split: requires a limit size (e.g. 100), see 'man split'!\n" 31 | return 1 32 | fi 33 | 34 | # Convert split options to wc options. Sigh. 35 | # Not all options supported by all wc/splits on all systems 36 | case $limit_type in 37 | -b|--bytes) wc_option='-c';; 38 | -C|--line-bytes) wc_option='-L';; 39 | -l|--lines) wc_option='-l';; 40 | esac 41 | 42 | # If whatever limit is exceeded 43 | if [ "$(wc $wc_option $file | awk '{print $1}')" -gt $limit_size ]; then 44 | # Actually do something 45 | split --verbose $limit_type $limit_size $file $prefix 46 | fi 47 | } # end of function Split 48 | -------------------------------------------------------------------------------- /lib/func_trunc_PWD: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_trunc_PWD 2 | 3 | function trunc_PWD { 4 | # $PWD truncation code adapted from The Bash Prompt HOWTO: 5 | # 11.10. Controlling the Size and Appearance of $PWD 6 | # http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x783.html 7 | 8 | # How many characters of the $PWD should be kept 9 | local pwdmaxlen=30 10 | # Indicator that there has been directory truncation: 11 | local trunc_symbol='...' 12 | # Temp variable for PWD 13 | local myPWD=$PWD 14 | 15 | # Replace any leading part of $PWD that matches $HOME with '~' 16 | # OPTIONAL, comment out if you want the full path! 17 | myPWD=${PWD/$HOME/~} 18 | 19 | if [ ${#myPWD} -gt $pwdmaxlen ]; then 20 | local pwdoffset=$(( ${#myPWD} - $pwdmaxlen )) 21 | echo "${trunc_symbol}${myPWD:$pwdoffset:$pwdmaxlen}" 22 | else 23 | echo "$myPWD" 24 | fi 25 | } 26 | -------------------------------------------------------------------------------- /lib/func_tweak_path: -------------------------------------------------------------------------------- 1 | # cookbook filename: func_tweak_path 2 | 3 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 | # Add a directory to the beginning or end of your path as long as it's not 5 | # already present. Does not take into account symbolic links! 6 | # Returns: 1 or sets the new $PATH 7 | # Called like: add_to_path (pre|post) 8 | function add_to_path { 9 | local location=$1 10 | local directory=$2 11 | 12 | # Make sure we have something to work with 13 | if [ -z "$location" -o -z "$directory" ]; then 14 | echo "$0:$FUNCNAME: requires a location and a directory to add" >&2 15 | echo "Usage: add_to_path (pre|post) " >&2 16 | echo "e.g. add_to_path pre /bin" >&2 17 | return 1 18 | fi 19 | 20 | # Make sure the directory is not relative 21 | if [ $(echo $directory | grep '^/') ]; then 22 | : echo "$0:$FUNCNAME: '$directory' is absolute" >&2 23 | else 24 | echo "$0:$FUNCNAME: can't add relative directory '$directory' to \$PATH" >&2 25 | return 1 26 | fi 27 | 28 | # Make sure the directory to add actually exists 29 | if [ -d "$directory" ]; then 30 | : echo "$0:$FUNCNAME: directory exists" >&2 31 | else 32 | echo "$0:$FUNCNAME: '$directory' does not exist--aborting" >&2 33 | return 1 34 | fi 35 | 36 | # Make sure it's not already in the $PATH 37 | if $(contains "$PATH" "$directory"); then 38 | : echo "$0:$FUNCNAME: adding directory to \$PATH" >&2 39 | else 40 | echo "$0:$FUNCNAME: '$directory' already in \$PATH--aborting" >&2 41 | fi 42 | 43 | # Figure out what to do 44 | case $location in 45 | pre* ) PATH="$directory:$PATH" ;; 46 | post* ) PATH="$PATH:$directory" ;; 47 | * ) PATH="$PATH:$directory" ;; 48 | esac 49 | 50 | # Clean up the new path, then set it 51 | PATH=$(clean_path $PATH) 52 | 53 | } # end of function add_to_path 54 | 55 | 56 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 57 | # Remove a directory from your path, if present. 58 | # Returns: sets the new $PATH 59 | # Called like: rm_from_path 60 | function rm_from_path { 61 | local directory=$1 62 | 63 | # Remove all instances of $directory from $PATH 64 | PATH=${PATH//$directory/} 65 | 66 | # Clean up the new path, then set it 67 | PATH=$(clean_path $PATH) 68 | 69 | } # end of function rm_from_path 70 | 71 | 72 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 73 | # Remove leading/trailing or duplicate ':', remove duplicate entries 74 | # Returns: echoes the "cleaned up" path 75 | # Called like: cleaned_path=$(clean_path $PATH) 76 | function clean_path { 77 | local path=$1 78 | local newpath 79 | local directory 80 | 81 | # Make sure we have something to work with 82 | [ -z "$path" ] && return 1 83 | 84 | # Remove duplicate directories, if any 85 | for directory in ${path//:/ }; do 86 | contains "$newpath" "$directory" && newpath="${newpath}:${directory}" 87 | done 88 | 89 | # Remove any leading ':' separators 90 | # Remove any trailing ':' separators 91 | # Remove any duplicate ':' separators 92 | newpath=$(echo $newpath | sed 's/^:*//; s/:*$//; s/::/:/g') 93 | 94 | # Return the new path 95 | echo $newpath 96 | 97 | } # end of function clean_path 98 | 99 | 100 | #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 101 | # Determine if the path contains a given directory 102 | # Return 1 if target is contained within pattern, 0 otherwise 103 | # Called like: contains $PATH $dir 104 | function contains { 105 | local pattern=":$1:" 106 | local target=$2 107 | 108 | # This will be a case-sensitive comparison unless nocasematch is set 109 | case $pattern in 110 | *:$target:* ) return 1 ;; 111 | * ) return 0 ;; 112 | esac 113 | } # end of function contains 114 | -------------------------------------------------------------------------------- /settings/README: -------------------------------------------------------------------------------- 1 | This directory contains sample configuration files. 2 | See chapter 16 for details. 3 | 4 | 5 | /etc/profile 6 | Global login environment file for Bourne and similar login shells. We 7 | recommend you leave this alone unless you are the system administrator 8 | and know what you are doing. 9 | 10 | /etc/bashrc (Red Hat) 11 | /etc/bash.bashrc (Debian) 12 | Global environment file for interactive bash sub-shells. We recommend 13 | you leave this alone unless you are the system administrator and know 14 | what you are doing. 15 | 16 | /etc/bash_completion 17 | If this exists, it's almost certainly the configuration file for Ian 18 | Macdonald's programmable completion library (see [Recipe #Improving 19 | Programmable Completion]). We recommend looking into it, it's 20 | pretty cool. 21 | 22 | /etc/inputrc 23 | Global GNU Readline configuration. We recommend tweaking this as desired 24 | for the entire system (if you are the administrator), or tweaking 25 | ~/.inputrc for just you ([Receipe #Getting Started with a Custom 26 | Configuration]). This is not executed or sourced but read in via 27 | Readline and $INPUTRC, and $include (or bind -f) and note that it may 28 | contain include statements to other Readline files. 29 | 30 | 31 | ~/.bashrc 32 | Personal environment file for interactive bash sub-shells. We recommend 33 | that you place your aliases, functions and fancy prompts here. 34 | 35 | ~/.bash_profile 36 | Personal profile for bash login shells. We recommend that you make 37 | sure this sources ~/.bashrc, then ignore it. 38 | 39 | ~/.bash_login 40 | Personal profile file for Bourne login shells; only used by bash if 41 | ~/.bash_profile is not present. We recommend you ignore this. 42 | 43 | ~/.profile 44 | Personal profile file for Bourne login shells; only used by bash if 45 | ~/.bash_profile and ~/.bash_login are not present. We recommend you 46 | ignore this unless you also use other shells that use it. 47 | 48 | ~/.bash_logout 49 | Executed when you logout. We recommend you place any cleanup routines, 50 | (e.g., [Recipe #Clear the Screen when you Log Out]) here. This is only 51 | executed on a clean logout (i.e., not if your session dies due to a 52 | dropped WAN link). 53 | 54 | ~/.inputrc 55 | Personal customizations for GNU Readline. We recommend tweaking this 56 | as desired ([Receipe #Getting Started with a Custom Configuration]). 57 | This is not executed or sourced but read in via Readline and $INPUTRC, 58 | and $include (or bind -f) and note that it may contain include 59 | statements to other Readline files. 60 | -------------------------------------------------------------------------------- /settings/add_to_bash_profile: -------------------------------------------------------------------------------- 1 | # cookbook filename: add_to_bash_profile 2 | # Add this code to your ~/.bash_profile 3 | 4 | # If we're running in bash, search for then source our settings 5 | # You can also just hardcode $SETTINGS, but this is more flexible 6 | if [ -n "$BASH_VERSION" ]; then 7 | for path in /opt/bin /etc ~ ; do 8 | # Use the first one found 9 | if [ -d "$path/settings" -a -r "$path/settings" -a -x "$path/settings" ] 10 | then 11 | export SETTINGS="$path/settings" 12 | fi 13 | done 14 | source "$SETTINGS/bash_profile" 15 | #source "$SETTINGS/bashrc" # If necessary 16 | fi 17 | -------------------------------------------------------------------------------- /settings/add_to_bashrc: -------------------------------------------------------------------------------- 1 | # cookbook filename: add_to_bashrc 2 | # Add this code to your ~/.bashrc 3 | 4 | # If we're running in bash, and it isn't already set, 5 | # search for then source our settings 6 | # You can also just hard code $SETTINGS, but this is more flexible 7 | if [ -n "$BASH_VERSION" ]; then 8 | if [ -z "$SETTINGS" ]; then 9 | for path in /opt/bin /etc ~ ; do 10 | # Use the first one found 11 | if [ -d "$path/settings" -a -r "$path/settings" -a -x "$path/settings" ] 12 | then 13 | export SETTINGS="$path/settings" 14 | fi 15 | done 16 | fi 17 | source "$SETTINGS/bashrc" 18 | fi 19 | -------------------------------------------------------------------------------- /settings/bash_logout: -------------------------------------------------------------------------------- 1 | # cookbook filename: bash_logout 2 | 3 | # settings/bash_logout: execute on shell logout 4 | 5 | # Clear the screen on logout to prevent information leaks, if not already 6 | # set as an exit trap elsewhere 7 | [ -n "$PS1" ] && clear 8 | -------------------------------------------------------------------------------- /settings/bash_profile: -------------------------------------------------------------------------------- 1 | # cookbook filename: bash_profile 2 | 3 | # settings/bash_profile: Login shell environment settings 4 | # To reread (and implement changes to this file) use: 5 | # source $SETTINGS/bash_profile 6 | 7 | # Only if interactive bash with a terminal! 8 | [ -t 1 -a -n "$BASH_VERSION" ] || return 9 | 10 | # Failsafe. This should be set when we're called, but if not, the 11 | # "not found" error messages should be pretty clear. 12 | # Use leading ':' to prevent this from being run as a program after 13 | # it is expanded. 14 | : ${SETTINGS:='SETTINGS_variable_not_set'} 15 | 16 | # DEBUGGING only--will break scp, rsync 17 | # echo "Sourcing $SETTINGS/bash_profile..." 18 | # export PS4='+xtrace $LINENO: ' 19 | # set -x 20 | 21 | # Debugging/logging--will not break scp, rsync 22 | #case "$-" in 23 | # *i*) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Interactive" \ 24 | # "$SETTINGS/bash_profile ssh=$SSH_CONNECTION" >> ~/rc.log ;; 25 | # * ) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Noninteractive" \ 26 | # "$SETTINGS/bash_profile ssh=$SSH_CONNECTION" >> ~/rc.log ;; 27 | #esac 28 | 29 | # Use the keychain (http://www.funtoo.org/Keychain/) shell script 30 | # to manage ssh-agent, if it's available. If it's not, you should look 31 | # into adding it. 32 | for path in $SETTINGS ${PATH//:/ }; do 33 | if [ -x "$path/keychain" ]; then 34 | # Load default id_rsa and/or id_dsa keys, add others here as needed 35 | # See also --clear --ignore-missing --noask --quiet --time-out 36 | $path/keychain ~/.ssh/id_?sa ~/.ssh/${USER}_?sa 37 | break 38 | fi 39 | done 40 | 41 | 42 | # Apply interactive subshell customizations to login shells too. 43 | # The system profile file in /etc probably already does this. 44 | # If not, it's probably better to do it manually in wherever you: 45 | # source "$SETTINGS/bash_profile" 46 | # But just in case... 47 | # for file in /etc/bash.bashrc /etc/bashrc ~/.bashrc; do 48 | # [ -r "$file" ] && source $file && break # Use the first one found 49 | #done 50 | 51 | 52 | # Do site- or host-specific things here 53 | case $HOSTNAME in 54 | *.company.com ) # source $SETTINGS/company.com 55 | ;; 56 | host1.* ) # host1 stuff 57 | ;; 58 | host2.company.com ) # source .bashrc.host2 59 | ;; 60 | drake.* ) # echo DRAKE in bash_profile.jp! 61 | ;; 62 | esac 63 | 64 | 65 | # Do this last because we basically fork off from here. If we exit screen 66 | # we return to a fully configured session. The screen session gets configured 67 | # as well, and if we never leave it, well, this session isn't that bloated. 68 | 69 | # Only run if we are interactive and not already running screen 70 | # AND '~/.use_screen' exists. 71 | if [ "$PS1" -a $TERM != "screen" -a "$USING_SCREEN" != "YES" -a -f ~/.use_screen ]; \ 72 | then 73 | # We'd rather use 'type -P' here, but that was added in bash-2.05b and we 74 | # use systems we don't control with versions older than that. We can't 75 | # easily use 'which' since on some systems that produces output whether 76 | # the file is found or not. 77 | for path in ${PATH//:/ }; do 78 | if [ -x "$path/screen" ]; then 79 | # If screen(1) exists and is executable, run our wrapper 80 | [ -x "$SETTINGS/run_screen" ] && $SETTINGS/run_screen 81 | fi 82 | done 83 | fi 84 | -------------------------------------------------------------------------------- /settings/bashrc: -------------------------------------------------------------------------------- 1 | # cookbook filename: bashrc 2 | 3 | # settings/bash_profile: subshell environment settings 4 | # To reread (and implement changes to this file) use: 5 | # source $SETTINGS/bashrc 6 | 7 | # Only if interactive bash with a terminal! 8 | [ -t 1 -a -n "$BASH_VERSION" ] || return 9 | 10 | # Failsafe. This should be set when we're called, but if not, the 11 | # "not found" error messages should be pretty clear. 12 | # Use leading ':' to prevent this from being run as a program after 13 | # it is expanded. 14 | : ${SETTINGS:='SETTINGS_variable_not_set'} 15 | 16 | # DEBUGGING only--will break scp, rsync 17 | # echo "Sourcing $SETTINGS/bash_profile..." 18 | # export PS4='+xtrace $LINENO: ' 19 | # set -x 20 | 21 | # Debugging/logging--will not break scp, rsync 22 | # case "$-" in 23 | # *i*) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Interactive" \ 24 | # "$SETTINGS/bashrc ssh=$SSH_CONNECTION" >> ~/rc.log ;; 25 | # * ) echo "$(date '+%Y-%m-%d_%H:%M:%S_%Z') Noninteractive" \ 26 | # "$SETTINGS/bashrc ssh=$SSH_CONNECTION" >> ~/rc.log ;; 27 | #esac 28 | 29 | # In theory this is also sourced from /etc/bashrc (/etc/bash.bashrc) 30 | # or ~/.bashrc to apply all these settings to login shells too. In practice 31 | # if these settings only work sometimes (like in subshells), verify that. 32 | 33 | # Source keychain file (if it exists) for SSH and GPG agents 34 | [ -r "$HOME/.keychain/${HOSTNAME}-sh" ] \ 35 | && source "$HOME/.keychain/${HOSTNAME}-sh" 36 | [ -r "$HOME/.keychain/${HOSTNAME}-sh-gpg" ] \ 37 | && source "$HOME/.keychain/${HOSTNAME}-sh-gpg" 38 | 39 | # Set some more useful prompts 40 | # Interactive command-line prompt 41 | # ONLY set one of these if we really are interactive, since lots of people 42 | # (even us sometimes) test to see if a shell is interactive using 43 | # something like: if [ "$PS1" ]; then 44 | case "$-" in 45 | *i*) 46 | #export PS1='\n[\u@\h t:\l l:$SHLVL h:\! j:\j v:\V]\n$PWD\$ ' 47 | #export PS1='\n[\u@\h:T\l:L$SHLVL:C\!:\D{%Y-%m-%d_%H:%M:%S_%Z}]\n$PWD\$ ' 48 | export PS1='\n[\u@\h:T\l:L$SHLVL:C\!:J\j:\D{%Y-%m-%d_%H:%M:%S_%Z}]\n$PWD\$ ' 49 | #export PS2='> ' # Secondary (i.e. continued) prompt 50 | 51 | #export PS3='Please make a choice: ' # Select prompt 52 | #export PS4='+xtrace $LINENO: ' # xtrace (debug) prompt 53 | export PS4='+xtrace $BASH_SOURCE::$FUNCNAME-$LINENO: ' # xtrace prompt 54 | 55 | # If this is an xterm set the title to user@host:dir 56 | case "$TERM" in 57 | xterm*|rxvt*) 58 | PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}:$PWD\007"' 59 | ;; 60 | esac 61 | ;; 62 | esac 63 | 64 | # Make sure custom inputrc is handled, if we can find it; note different 65 | # names. Also note different order, since for this one we probably want 66 | # our custom settings to override the system file, if present. 67 | for file in $SETTINGS/inputrc ~/.inputrc /etc/inputrc; do 68 | [ -r "$file" ] && export INPUTRC="$file" && break # Use first found 69 | done 70 | 71 | # No core files by default 72 | # See also /etc/security/limits.conf on many Linux systems. 73 | ulimit -S -c 0 > /dev/null 2>&1 74 | 75 | # Set various aspects of the bash history 76 | export HISTSIZE=5000 # Num. of commands in history stack in memory 77 | export HISTFILESIZE=5000 # Num. of commands in history file 78 | #export HISTCONTROL=ignoreboth # bash < 3, omit dups & lines starting with spaces 79 | export HISTCONTROL='erasedups:ignoredups:ignorespace' 80 | export HISTIGNORE='&:[ ]*' # bash >= 3, omit dups & lines starting with spaces 81 | #export HISTTIMEFORMAT='%Y-%m-%d_%H:%M:%S_%Z=' # bash >= 3, timestamp hist file 82 | shopt -s histappend # Append rather than overwrite history on exit 83 | shopt -q -s cdspell # Auto-fix minor typos in interactive use of 'cd' 84 | shopt -q -s checkwinsize # Update the values of LINES and COLUMNS 85 | shopt -q -s cmdhist # Make multiline commands 1 line in history 86 | set -o notify # (or set -b) # Immediate notif. of background job termination. 87 | set -o ignoreeof # Don't let Ctrl-D exit the shell 88 | 89 | # Other bash settings 90 | PATH="$PATH:/opt/bin" 91 | export MANWIDTH=80 # Manpage width, use < 80 if COLUMNS=80 & less -N 92 | export LC_COLLATE='C' # Set traditional C sort order (e.g. UC first) 93 | export HOSTFILE='/etc/hosts' # Use /etc/hosts for hostname completion 94 | export CDPATH='.:~/:..:../..' # Similar to $PATH, but for use by 'cd' 95 | # Note that the '.' in $CDPATH is needed so that cd will work under POSIX mode 96 | # but this will also cause cd to echo the new directory to STDOUT! 97 | # And see also "cdspell" above! 98 | 99 | # Import bash completion settings, if they exist in the default location 100 | # and if not already imported (e.g. "$BASH_COMPLETION_COMPAT_DIR" NOT set). 101 | # This can take a second or two on a slow system, so you may not always 102 | # want to do it, even if it does exist (which it doesn't by default on many 103 | # systems, e.g. Red Hat). 104 | if [ -z "$BASH_COMPLETION_COMPAT_DIR" ] && ! shopt -oq posix; then 105 | if [ -f /usr/share/bash-completion/bash_completion ]; then 106 | . /usr/share/bash-completion/bash_completion 107 | elif [ -f /etc/bash_completion ]; then 108 | . /etc/bash_completion 109 | fi 110 | fi 111 | 112 | # Use a lesspipe filter, if we can find it. This sets the $LESSOPEN variable. 113 | # Globally replace the $PATH ':' delimiter with space for use in a list. 114 | for path in $SETTINGS /opt/bin ~/ ${PATH//:/ }; do 115 | # Use first one found of 'lesspipe.sh' (preferred) or 'lesspipe' (Debian) 116 | [ -x "$path/lesspipe.sh" ] && eval $("$path/lesspipe.sh") && break 117 | [ -x "$path/lesspipe" ] && eval $("$path/lesspipe") && break 118 | done 119 | 120 | # Set other less & editor prefs (overkill) 121 | export LESS="--LONG-PROMPT --LINE-NUMBERS --ignore-case --QUIET --no-init" 122 | export VISUAL='vi' # Set a default that should always work 123 | # We'd rather use 'type -P' here, but that was added in bash-2.05b and we use 124 | # systems we don't control with versions older than that. We can't easily 125 | # use 'which' since that produces output whether the file is found or not. 126 | #for path in ${PATH//:/ }; do 127 | # # Overwrite VISUAL if we can find nano 128 | # [ -x "$path/nano" ] \ 129 | # && export VISUAL='nano --smooth --const --nowrap --suspend' && break 130 | #done 131 | # See above notes re: nano for why we're using this for loop 132 | for path in ${PATH//:/ }; do 133 | # Alias vi to vim in binary mode if we can 134 | [ -x "$path/vim" ] && alias vi='vim -b' && break 135 | done 136 | export EDITOR="$VISUAL" # Yet Another Possibility 137 | export SVN_EDITOR="$VISUAL" # Subversion 138 | alias edit=$VISUAL # Provide a command to use on all systems 139 | 140 | # Set ls options and aliases. 141 | # Note all the colorizing may or may not work depending on your terminal 142 | # emulation and settings, esp. ANSI color. But it shouldn't hurt to have. 143 | # See above notes re: nano for why we're using this for loop. 144 | for path in ${PATH//:/ }; do 145 | [ -r "$path/dircolors" ] && eval "$(dircolors)" \ 146 | && LS_OPTIONS='--color=auto' && break 147 | done 148 | export LS_OPTIONS="$LS_OPTIONS -F -h" 149 | # Using dircolors may cause csh scripts to fail with an 150 | # "Unknown colorls variable 'do'." error. The culprit is the ":do=01;35:" 151 | # part in the LS_COLORS environment variable. For a possible solution see 152 | # http://forums.macosxhints.com/showthread.php?t=7287 153 | # eval "$(dircolors)" 154 | alias ls="ls $LS_OPTIONS" 155 | alias ll="ls $LS_OPTIONS -l" 156 | alias ll.="ls $LS_OPTIONS -ld" # Usage: ll. ~/.* 157 | alias la="ls $LS_OPTIONS -la" 158 | alias lrt="ls $LS_OPTIONS -alrt" 159 | 160 | # Useful aliases 161 | # Moved to a function: alias bot='cd $(dirname $(find . | tail -1))' 162 | #alias clip='xsel -b' # pipe stuff into right "X" clipboard 163 | alias gc='xsel -b' # "GetClip" get stuff from right "X" clipboard 164 | alias pc='xsel -bi' # "PutClip" put stuff to right "X" clipboard 165 | alias clr='cd ~/ && clear' # Clear and return $HOME 166 | alias cls='clear' # DOS-ish for clear 167 | alias cal='cal -M' # Start calendars on Monday 168 | alias copy='cp' # DOS-ish for cp 169 | #alias cp='cp -i' # Annoying Red Hat default from /root/.bashrc 170 | alias cvsst='cvs -qn update' # Hack to get concise CVS status (like svn st) 171 | alias del='rm' # DOS-ish for rm 172 | alias df='df --print-type --exclude-type=tmpfs --exclude-type=devtmpfs' 173 | alias diff='diff -u' # Make unified diffs the default 174 | alias jdiff="\diff --side-by-side --ignore-case --ignore-blank-lines\ 175 | --ignore-all-space --suppress-common-lines" # Useful GNU diff command 176 | alias dir='ls' # DOS-ish for ls 177 | alias hu='history -n && history -a' # Read new hist. lines; append current lines 178 | alias hr='hu' # "History update" backward compat to 'hr' 179 | alias inxi='inxi -c19' # (Ubuntu) system information script 180 | alias ipconfig='ifconfig' # Windows-ish for ifconfig 181 | alias lesss='less -S' # Don't wrap lines 182 | alias locate='locate -i' # Case-insensitive locate 183 | alias man='LANG=C man' # Display manpages properly 184 | alias md='mkdir' # DOS-ish for mkdir 185 | alias move='mv' # DOS-ish for mv 186 | #alias mv='mv -i' # Annoying Red Hat default from /root/.bashrc 187 | alias ntsysv='rcconf' # Debian rcconf is pretty close to Red Hat ntsysv 188 | #alias open='gnome-open' # Open files & URLs using GNOME handlers; see run below 189 | alias pathping='mtr' # mtr - a network diagnostic tool 190 | alias ping='ping -c4' # Only 4 pings by default 191 | alias r='fc -s' # Recall and execute 'command' starting with... 192 | alias rd='rmdir' # DOS-ish for rmdir 193 | # Tweaked from http://bit.ly/2fc4e8Z 194 | alias randomwords="shuf -n102 /usr/share/dict/words \ 195 | | perl -ne 'print qq(\u\$_);' | column" 196 | alias ren='mv' # DOS-ish for mv/rename 197 | #alias rm='rm -i' # Annoying Red Hat default from /root/.bashrc 198 | alias reloadbind='rndc -k /etc/bind/rndc.key freeze \ 199 | && rndc -k /etc/bind/rndc.key reload && rndc -k /etc/bind/rndc.key thaw' 200 | # Reload dynamic BIND zones after editing db.* files 201 | alias svndiff='meld' # Cool GUI diff, similar to TortoiseMerge 202 | alias svnpropfix='svn propset svn:keywords "id url date"' 203 | alias svnkey='svn propset svn:keywords "id url"' 204 | alias svneol='svn propset svn:eol-style' # One of 'native', 'LF', 'CR', 'CRLF' 205 | alias svnexe='svn propset svn:executable on' 206 | alias top10='sort | uniq -c | sort -rn | head' 207 | alias tracert='traceroute' # DOS-ish for traceroute 208 | alias vzip='unzip -lvM' # View contents of ZIP file 209 | alias wgetdir="wget --no-verbose --recursive --no-parent --no-directories \ 210 | --level=1" # Grab a whole directory using wget 211 | alias wgetsdir="wget --no-verbose --recursive --timestamping --no-parent \ 212 | --no-host-directories --reject 'index.*'" # Grab a dir and subdirs 213 | alias zonex='host -l' # Extract (dump) DNS zone 214 | 215 | # Date/time 216 | alias iso8601="date '+%Y-%m-%dT%H:%M:%S%z'" # ISO 8601 time 217 | alias now="date '+%F %T %Z(%z)'" # More readable ISO 8601 local 218 | alias utc="date --utc '+%F %T %Z(%z)'" # More readable ISO 8601 UTC 219 | 220 | # Neat stuff from http://xmodulo.com/useful-bash-aliases-functions.html 221 | alias meminfo='free -m -l -t' # See how much memory you have left 222 | alias whatpid='ps auwx | grep' # Get PID and process info 223 | alias port='netstat -tulanp' # Show which apps are connecting to the network 224 | 225 | # If the script exists and is executable, create an alias to get 226 | # web server headers 227 | for path in ${PATH//:/ }; do 228 | [ -x "$path/lwp-request" ] && alias httpdinfo='lwp-request -eUd' && break 229 | done 230 | 231 | 232 | # Useful functions 233 | 234 | # Use 'gnome-open' to "run" things 235 | function run { 236 | [ -r "$*" ] && { 237 | gnome-open "$*" >& /dev/null 238 | } || { 239 | echo "'$*' not found or not readable!" 240 | } 241 | } 242 | 243 | 244 | # Python version of 'perl -c' 245 | function python-c { 246 | python -m py_compile "$1" && rm -f "${1}c" 247 | } 248 | 249 | 250 | # cd to the bottom of a narrow but deep dir tree 251 | function bot { 252 | local dir=${1:-.} 253 | #\cd $(dirname $(find $dir | tail -1)) 254 | \cd $(find . -name CVS -prune -o -type d -print | tail -1) 255 | } 256 | 257 | 258 | # mkdir newdir then cd into it 259 | # usage: mcd () 260 | function mcd { 261 | local newdir='_mcd_command_failed_' 262 | if [ -d "$1" ]; then # Dir exists, mention that... 263 | echo "$1 exists..." 264 | newdir="$1" 265 | else 266 | if [ -n "$2" ]; then # We've specified a mode 267 | command mkdir -p -m $1 "$2" && newdir="$2" 268 | else # Plain old mkdir 269 | command mkdir -p "$1" && newdir="$1" 270 | fi 271 | fi 272 | builtin cd "$newdir" # No matter what, cd into it 273 | } # end of mcd 274 | 275 | 276 | # Trivial command-line calculator 277 | function calc { 278 | # INTEGER ONLY! --> echo The answer is: $(( $* )) 279 | # Floating point 280 | awk "BEGIN {print \"$* = \" $* }"; 281 | #awk "BEGIN {printf \"$* = %f\", $* }"; 282 | } # end of calc 283 | function addup { 284 | awk '{sum += $1} END {print sum}' 285 | } 286 | 287 | 288 | # Allow use of 'cd ...' to cd up 2 levels, 'cd ....' up 3, etc. (like 4NT/4DOS) 289 | # Usage: cd ..., etc. 290 | function cd { 291 | 292 | local option= length= count= cdpath= i= # Local scope and start clean 293 | 294 | # If we have a -L or -P symlink option, save then remove it 295 | if [ "$1" = "-P" -o "$1" = "-L" ]; then 296 | option="$1" 297 | shift 298 | fi 299 | 300 | # Are we using the special syntax? Make sure $1 isn't empty, then 301 | # match the first 3 characters of $1 to see if they are '...', then 302 | # make sure there isn't a slash by trying a substitution; if it fails, 303 | # there's no slash. 304 | if [ -n "$1" -a "${1:0:3}" = '...' -a "$1" = "${1%/*}" ]; then 305 | # We are using special syntax 306 | length=${#1} # Assume that $1 has nothing but dots and count them 307 | count=2 # 'cd ..' still means up one level, so ignore first two 308 | 309 | # While we haven't run out of dots, keep cd'ing up 1 level 310 | for ((i=$count;i<=$length;i++)); do 311 | cdpath="${cdpath}../" # Build the cd path 312 | done 313 | 314 | # Actually do the cd 315 | builtin cd $option "$cdpath" 316 | elif [ -n "$1" ]; then 317 | # We are NOT using special syntax; just plain old cd by itself 318 | builtin cd $option "$*" 319 | else 320 | # We are NOT using special syntax; plain old cd by itself to home dir 321 | builtin cd $option 322 | fi 323 | } # end of cd 324 | 325 | 326 | # Do site- or host-specific things here 327 | case $HOSTNAME in 328 | *.company.com ) # source $SETTINGS/company.com 329 | ;; 330 | host1.* ) # host1 stuff 331 | ;; 332 | host2.company.com ) # source .bashrc.host2 333 | ;; 334 | drake.* ) # echo DRAKE in bashrc.jp! 335 | export TAPE=/dev/tape 336 | ;; 337 | esac 338 | -------------------------------------------------------------------------------- /settings/inputrc: -------------------------------------------------------------------------------- 1 | # cookbook filename: inputrc 2 | # settings/inputrc: # readline settings 3 | # To reread (and implement changes to this file) use: 4 | # bind -f $SETTINGS/inputrc 5 | 6 | # First, include any system-wide bindings and variable 7 | # assignments from /etc/inputrc 8 | # (fails silently if file doesn't exist) 9 | $include /etc/inputrc 10 | 11 | $if Bash 12 | # Ignore case when doing completion 13 | set completion-ignore-case on 14 | # Completed dir names have a slash appended 15 | set mark-directories on 16 | # Completed names which are symlinks to dirs have a slash appended 17 | set mark-symlinked-directories on 18 | # List ls -F for completion 19 | set visible-stats on 20 | # Cycle through ambiguous completions instead of list 21 | "\C-i": menu-complete 22 | # Set bell to audible 23 | set bell-style audible 24 | # List possible completions instead of ringing bell 25 | set show-all-if-ambiguous on 26 | 27 | # From the readline documentation at 28 | # https://cnswww.cns.cwru.edu/php/chet/readline/readline.html#SEC12 29 | # Macros that are convenient for shell interaction 30 | # edit the path 31 | "\C-xp": "PATH=${PATH}\e\C-e\C-a\ef\C-f" 32 | # prepare to type a quoted word -- insert open and close double quotes 33 | # and move to just after the open quote 34 | "\C-x\"": "\"\"\C-b" 35 | # insert a backslash (testing backslash escapes in sequences and macros) 36 | "\C-x\\": "\\" 37 | # Quote the current or previous word 38 | "\C-xq": "\eb\"\ef\"" 39 | # Add a binding to refresh the line, which is unbound 40 | "\C-xr": redraw-current-line 41 | # Edit variable on current line. 42 | #"\M-\C-v": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y=" 43 | "\C-xe": "\C-a\C-k$\C-y\M-\C-e\C-a\C-y=" 44 | $endif 45 | 46 | # some defaults / modifications for the emacs mode 47 | $if mode=emacs 48 | 49 | # allow the use of the Home/End keys 50 | "\e[1~": beginning-of-line 51 | "\e[4~": end-of-line 52 | 53 | # allow the use of the Delete/Insert keys 54 | "\e[3~": delete-char 55 | "\e[2~": quoted-insert 56 | 57 | # mappings for "page up" and "page down" to step to beginning/end of the history 58 | # "\e[5~": beginning-of-history 59 | # "\e[6~": end-of-history 60 | 61 | # alternate mappings for "page up" and "page down" to search the history 62 | # "\e[5~": history-search-backward 63 | # "\e[6~": history-search-forward 64 | 65 | # MUCH nicer up-arrow search behavior! 66 | "\e[A": history-search-backward ## up-arrow 67 | "\e[B": history-search-forward ## down-arrow 68 | 69 | # mappings for Ctrl-left-arrow and Ctrl-right-arrow for word moving 70 | ### These were/are broken, and /etc/inputrc has better anyway 71 | # "\e[5C": forward-word 72 | # "\e[5D": backward-word 73 | # "\e\e[C": forward-word 74 | # "\e\e[D": backward-word 75 | 76 | # for non RH/Debian xterm, can't hurt for RH/Debian xterm 77 | "\eOH": beginning-of-line 78 | "\eOF": end-of-line 79 | 80 | # for FreeBSD console 81 | "\e[H": beginning-of-line 82 | "\e[F": end-of-line 83 | 84 | $endif 85 | -------------------------------------------------------------------------------- /settings/run_screen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # cookbook filename: run_screen 3 | # run_screen--Wrapper script intended to run from a "profile" file to run 4 | # screen at login time with a friendly menu. 5 | 6 | # Sanity check 7 | if [ "$TERM" == "screen" -o "$TERM" == "screen-256color" ]; then 8 | printf "%b" "According to \$TERM = '$TERM' we're *already* using" \ 9 | " screen.\nAborting...\n" 10 | exit 1 11 | elif [ "$USING_SCREEN" == "YES" ]; then 12 | printf "%b" "According to \$USING_SCREEN = '$USING_SCREEN' we're" 13 | " *already* using screen.\nAborting...\n" 14 | exit 1 15 | fi 16 | 17 | # The "$USING_SCREEN" variable is for the rare cases when screen does NOT set 18 | # $TERM=screen. This can happen when 'screen' is not in TERMCAP or friends, 19 | # as is the case on a Solaris 9 box we use but don't control. If we don't 20 | # have some way to tell when we're inside screen, this wrapper goes into an 21 | # ugly and confusing endless loop. 22 | 23 | # Seed list with Exit and New options and see what screens are already running. 24 | # The select list is whitespace-delimited, and we only want actual screen 25 | # sessions, so use perl to remove whitespace, filter for sessions, and show 26 | # only useful info from 'screen -ls' output. 27 | available_screens="Exit New $(screen -ls \ 28 | | perl -ne 's/\s+//g; print if s/^(\d+\..*?)(?:\(.*?\))?(\(.*?\))$/$1$2\n/;')" 29 | 30 | # Print a warning if using runtime feedback 31 | run_time_feedback=0 32 | [ "$run_time_feedback" == 1 ] && printf "%b" " 33 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 34 | 'screen' Notes: 35 | 36 | 1) Sessions marked 'unreachable' or 'dead' should be investigated and 37 | removed with the -wipe option if appropriate.\n\n" 38 | 39 | 40 | # Present a list of choices 41 | PS3='Choose a screen for this session: ' 42 | select selection in $available_screens; do 43 | if [ "$selection" == "Exit" ]; then 44 | break 45 | elif [ "$selection" == "New" ]; then 46 | export USING_SCREEN=YES 47 | exec screen -c $SETTINGS/screenrc -a \ 48 | -S $USER.$(date '+%Y-%m-%d_%H:%M:%S%z') 49 | break 50 | elif [ "$selection" ]; then 51 | # Pull out just the part we need using cut 52 | # We'd rather use a 'here string' [$(cut -d'(' -f1 <<< $selection)] 53 | # than this echo, but they are only in bash-2.05b+ 54 | screen_to_use="$(echo $selection | cut -d'(' -f1)" 55 | # Old: exec screen -dr $screen_to_use 56 | # Alt: exec screen -x $USER/$screen_to_use 57 | exec screen -r $USER/$screen_to_use 58 | break 59 | else 60 | printf "%b" "Invalid selection.\n" 61 | fi 62 | done 63 | --------------------------------------------------------------------------------