├── 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 | First |
54 | $PREVLINE
55 | $NEXTLINE
56 | Last |
57 |
58 |
59 |
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/\
61 | contentwnl.xml
62 |
63 | cd "$PRIV2"
64 | unzip -q "$FULL2"
65 | sed -e 's/>/>\
66 | /g' -e 's/\
67 | 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 |
--------------------------------------------------------------------------------