mirror of https://github.com/tLDP/LDP
updated
This commit is contained in:
parent
38ee11bee1
commit
fb665c619a
|
@ -6,8 +6,146 @@
|
|||
http://personal.riverusers.com/~thegrendel/Change.log
|
||||
------------------------------------------------------------------------
|
||||
|
||||
Intermediate release.
|
||||
Working toward version 4.0, Winterberry release.
|
||||
Version 4.1, Waxberry release.
|
||||
10/08/06
|
||||
|
||||
1) In the "Starting Off With a Sha-Bang" chapter:
|
||||
Added Sven Mascheck's note to the footnote on magic numbers in 4.2 BSD.
|
||||
(Thanks, Sven.)
|
||||
|
||||
2) In "Special Characters" chapter:
|
||||
At "$$" entry, in footnote, added definition of a "process."
|
||||
|
||||
3) In "Here Strings" section of "Here Documents" chapter:
|
||||
Added short intro example.
|
||||
Thank you, Sebastian Kaminski, for the suggestion.
|
||||
|
||||
4) In "Functions" chapter:
|
||||
Added note about single-line functions, with warning that a semicolon
|
||||
must terminate the final command in such a function.
|
||||
Embedded Christopher Head's function definition snippet in S.C.'s
|
||||
inline example.
|
||||
|
||||
5) "System and Administrative Commands" chapter:
|
||||
At "stat" entry, added in-line example script showing setting of
|
||||
file-descriptive variables.
|
||||
(Thank you, Joël Bourquard, for the suggestion.)
|
||||
At "netstat" entry, added note about "netstat -lptu."
|
||||
|
||||
6) In "Communications Commands" section of "External Commands" chapter:
|
||||
At "rsync" entry, changed final paragraph to a "note."
|
||||
|
||||
7) In "Text Processing" section of "External Commands" Chapter:
|
||||
At "tail" entry, noted that "tail -$LINES" is now deprecated,
|
||||
and corrected examples.
|
||||
Also cleaned up "head" references and examples, as above.
|
||||
At "tr" entry, in sidebar, removed misleading statement about mandatory
|
||||
quoting of letter ranges within brackets.
|
||||
(Thank you, Omair Eshkenazi, for pointing this out.)
|
||||
At "nl" entry, changed "cat -n" reference to "cat -b" for clarity.
|
||||
(Thank you, Omair Eshkenazi, for pointing this out.)
|
||||
|
||||
8) In "Special Variable Types" section of "Introduction to Variables and
|
||||
Added short definition of "child process" to note about exporting
|
||||
variable to child processes.
|
||||
At "shift" entry, added paragraph and short in-line example code
|
||||
listing on passing a numerical parameter indicating how many positions
|
||||
to shift.
|
||||
|
||||
9) In "Subshells" chapter:
|
||||
Added definition of "subshell" in a sidebar box.
|
||||
Added in-line example, showing subshell with "ps."
|
||||
Added footnote that "exec" does not fork off a subprocess/subshell.
|
||||
At "dedicated environment" inline example, noted that the "exit"
|
||||
only terminates the subshell, not the parent process.
|
||||
Removed "note" markers from paragraph about variables in a subshell
|
||||
not being visible outside the subshell.
|
||||
Added note, with example, about use of "$BASH_SUBSHELL" --
|
||||
but _not_ "$SHLVL" to indicate level of nesting within a subshell.
|
||||
|
||||
10) In "Loops and Branches" chapter:
|
||||
Corrected minor grammar error in "findstring.sh" example.
|
||||
Added footnote defining "iteration."
|
||||
Added (needed!) spaces in definitions of "while" and "until" loops.
|
||||
Noted that "while loop" uses previously-discussed "test brackets,"
|
||||
and can use double-brackets construct.
|
||||
|
||||
11) In "Internal Commands and Builtins" chapter:
|
||||
At "unset" entry, added string test to "unset.sh" example.
|
||||
At "exit" entry, added note that this command may also terminate
|
||||
a subshell.
|
||||
At "exit" entry, added footnote that this command *only* terminates
|
||||
the process it is running within.
|
||||
At footnote to "hash" entry, gave a couple of synonyms for "algorithm."
|
||||
|
||||
12) In "Internal Variables" section of "Variables Revisited" chapter:
|
||||
At "$SHLVL" entry, added note that this variable not affected
|
||||
by subshells.
|
||||
At "$!" entry, added Matthew Sage's "hanging job" example (thank you!).
|
||||
|
||||
13) In "/proc" section of "/dev and /proc" chapter:
|
||||
Added "cat /proc/acpi/battery/BAT0/info" to introductory usage
|
||||
examples.
|
||||
Added note about controlling peripherals by sending commands to /proc.
|
||||
|
||||
14) In "Miscellaneous Commands" section of "External Commands" chapter:
|
||||
At "mkfifo" entry, added Omair Eshkenazi's example script (thanks!).
|
||||
|
||||
15) In "Time/Date Commands" section of "External Commands" chapter:
|
||||
At "batch" entry, added short definition of "batch processing."
|
||||
|
||||
16) In "Manipulating Strings" section of "Variables Revisited" chapter:
|
||||
At "${string%substring}" entry, added Rory Winston's usage example
|
||||
(thanks!).
|
||||
|
||||
17) In "Colorizing Scripts" section of "Miscellany" chapter:
|
||||
Modified "Draw-box.sh" example per suggestions of Jim Angstadt
|
||||
(thanks!).
|
||||
|
||||
18) In "Of Zeroes and Nulls" chapter
|
||||
Added comments to "ex73.sh" and "ramdisk.sh" example scripts.
|
||||
|
||||
19) In "Contributed Scripts" appendix:
|
||||
In "days-between.sh" example,
|
||||
Corrected Gauss' Formula comment (reference date is March 1,
|
||||
1600, *not* January 1).
|
||||
Corrected broken link in above comment.
|
||||
(Thank you, Nick Alexeev, for the pointers.)
|
||||
Added "nightly-backup.sh" example.
|
||||
(Thank you, Richard Neill.)
|
||||
|
||||
20) In the "Sed and Awk Micro-primer" appendix:
|
||||
Added note about other "sed" delimiters, such as "%" ...
|
||||
(Thank you, Omair Eshkenazi.)
|
||||
|
||||
21) In "Analyzing Scripts" section of "Exercises" appendix:
|
||||
Added Rory Winston's one-liner script (thanks!).
|
||||
|
||||
22) In "Writing Scripts" section of "Exercises" appendix:
|
||||
In EASY section, added "Self-reproducing" script.
|
||||
In DIFFICULT section,
|
||||
added "Cross Reference" script.
|
||||
added "Square Roots" script.
|
||||
|
||||
23) In "Bibliography" section:
|
||||
At reference and URL for Col Needham's original IBDB scripts,
|
||||
Noted that the link no longer works.
|
||||
(Thank you, Colin Brace, for pointing this out.)
|
||||
|
||||
24) In "Assorted Tips" section of "Miscellany" chapter:
|
||||
Added "pseudo-code" entry.
|
||||
|
||||
25) In "Copyright" chapter:
|
||||
Added footnote stating author's intention to commit the book
|
||||
to the Public Domain in 2014.
|
||||
|
||||
26) Miscellaneous:
|
||||
Added footnote defining "deprecate."
|
||||
|
||||
|
||||
|
||||
Version 4.0, Winterberry release.
|
||||
06/18/06
|
||||
|
||||
1) "System and Administrative Commands" chapter:
|
||||
Added "gnome-mount" entry.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# Draw-box.sh: Drawing a box using ASCII characters.
|
||||
|
||||
# Script by Stefano Palmeri, with minor editing by document author.
|
||||
# Minor edits suggested by Jim Angstadt.
|
||||
# Used in the "ABS Guide" with permission.
|
||||
|
||||
|
||||
|
@ -118,9 +119,9 @@ for (( c=$2; count<=$BOX_WIDTH; c++)); do
|
|||
done
|
||||
|
||||
plot_char $1 $2 $CORNER_CHAR # Draw box angles.
|
||||
plot_char $1 `expr $2 + $BOX_WIDTH` +
|
||||
plot_char `expr $1 + $BOX_HEIGHT` $2 +
|
||||
plot_char `expr $1 + $BOX_HEIGHT` `expr $2 + $BOX_WIDTH` +
|
||||
plot_char $1 `expr $2 + $BOX_WIDTH` $CORNER_CHAR
|
||||
plot_char `expr $1 + $BOX_HEIGHT` $2 $CORNER_CHAR
|
||||
plot_char `expr $1 + $BOX_HEIGHT` `expr $2 + $BOX_WIDTH` $CORNER_CHAR
|
||||
|
||||
echo -ne "\E[0m" # Restore old colors.
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ ex71.sh (line 7)
|
|||
ex71a.sh (line 8)
|
||||
ex71b.sh (line 22)
|
||||
logevents.sh (lines 29, 36-39, 44, 53, 55, 60, 64)
|
||||
m4.sh (line 8)
|
||||
m4.sh (line 8: "\&" --> &)
|
||||
pw.sh (comment in line 4)
|
||||
read-r.sh (lines 5, 6, 19, 26)
|
||||
rnd.sh (comments in lines 38, 55, 64)
|
||||
|
@ -61,4 +61,7 @@ hash-example.sh (comment in line 3: < --> <, > --> >)
|
|||
quote-fetch.sh (comment in line 26: & --> &)
|
||||
ftpget.sh (comment in line 28)
|
||||
whx.sh (comment in line 259)
|
||||
nightly-backup.sh (comment in line 4)
|
||||
In-line code block at beginning of "I/O Redirection" chapter, line 41.
|
||||
In-line code block at "mkfifo" entro in "Miscellaneous Commands" section of
|
||||
"External Filters, Programs and Commands" chapter.
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -44,7 +44,7 @@ PMULTIPLIER=4.0 # Scaling factor to approximate PI.
|
|||
|
||||
get_random ()
|
||||
{
|
||||
SEED=$(head -1 /dev/urandom | od -N 1 | awk '{ print $2 }')
|
||||
SEED=$(head -n 1 /dev/urandom | od -N 1 | awk '{ print $2 }')
|
||||
RANDOM=$SEED # From "seeding-random.sh"
|
||||
#+ example script.
|
||||
let "rnum = $RANDOM % $DIMENSION" # Range less than 10000.
|
||||
|
|
|
@ -61,7 +61,7 @@ strip_leading_zero () # Better to strip possible leading zero(s)
|
|||
|
||||
|
||||
day_index () # Gauss' Formula:
|
||||
{ # Days from Jan. 3, 1600 to date passed as param.
|
||||
{ # Days from Mar. 1, 1600 to date passed as param.
|
||||
|
||||
day=$1
|
||||
month=$2
|
||||
|
@ -80,7 +80,7 @@ day_index () # Gauss' Formula:
|
|||
|
||||
let "Days = $DIY*$year + $year/$LEAPCYCLE - $indexyr + $indexyr/$LEAPCYCLE + $ADJ_DIY*$month/$MIY + $day - $DIM"
|
||||
# For an in-depth explanation of this algorithm, see
|
||||
#+ http://home.t-online.de/home/berndt.schwerdtfeger/cal.htm
|
||||
#+ http://weblogs.asp.net/pgreborio/archive/2005/01/06/347968.aspx
|
||||
|
||||
|
||||
echo $Days
|
||||
|
|
|
@ -67,8 +67,8 @@ fi # Doublecheck if in right directory, before messing with log file.
|
|||
|
||||
|
||||
|
||||
tail -$lines messages > mesg.temp # Saves last section of message log file.
|
||||
mv mesg.temp messages # Becomes new log directory.
|
||||
tail -n $lines messages > mesg.temp # Saves last section of message log file.
|
||||
mv mesg.temp messages # Becomes new log directory.
|
||||
|
||||
|
||||
# cat /dev/null > messages
|
||||
|
|
|
@ -4,6 +4,8 @@ var0=0
|
|||
LIMIT=10
|
||||
|
||||
while [ "$var0" -lt "$LIMIT" ]
|
||||
# ^ ^
|
||||
# Spaces, because these are "test-brackets" . . .
|
||||
do
|
||||
echo -n "$var0 " # -n suppresses newline.
|
||||
# ^ Space, to separate printed out numbers.
|
||||
|
|
|
@ -12,7 +12,7 @@ LINES=5
|
|||
( date; uname -a ) >>logfile
|
||||
# Time and machine name
|
||||
echo --------------------------------------------------------------------- >>logfile
|
||||
tail -$LINES /var/log/messages | xargs | fmt -s >>logfile
|
||||
tail -n $LINES /var/log/messages | xargs | fmt -s >>logfile
|
||||
echo >>logfile
|
||||
echo >>logfile
|
||||
|
||||
|
@ -25,7 +25,7 @@ exit 0
|
|||
#+ may give xargs indigestion.
|
||||
#
|
||||
# He suggests the following substitution for line 15:
|
||||
# tail -$LINES /var/log/messages | tr -d "\"'" | xargs | fmt -s >>logfile
|
||||
# tail -n $LINES /var/log/messages | tr -d "\"'" | xargs | fmt -s >>logfile
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -5,8 +5,8 @@ lines=35 # Allow 35 lines for the header (very generous).
|
|||
|
||||
for File in * # Test all the files in $PWD.
|
||||
do
|
||||
search1=`head -$lines $File | grep begin | wc -w`
|
||||
search2=`tail -$lines $File | grep end | wc -w`
|
||||
search1=`head -n $lines $File | grep begin | wc -w`
|
||||
search2=`tail -n $lines $File | grep end | wc -w`
|
||||
# Uuencoded files have a "begin" near the beginning,
|
||||
#+ and an "end" near the end.
|
||||
if [ "$search1" -gt 0 ]
|
||||
|
|
|
@ -37,11 +37,22 @@ then
|
|||
fi
|
||||
|
||||
|
||||
######################################################################
|
||||
echo "Creating swap file of size $blocks blocks (KB)."
|
||||
dd if=/dev/zero of=$FILE bs=$BLOCKSIZE count=$blocks # Zero out file.
|
||||
|
||||
mkswap $FILE $blocks # Designate it a swap file.
|
||||
swapon $FILE # Activate swap file.
|
||||
# Note that if one or more of these commands fails,
|
||||
#+ then it could cause nasty problems.
|
||||
######################################################################
|
||||
|
||||
# Exercise:
|
||||
# Rewrite the above block of code so that if it does not execute
|
||||
#+ successfully, then:
|
||||
# 1) an error message is echoed to stderr,
|
||||
# 2) all temporary files are cleaned up, and
|
||||
# 3) the script exits in an orderly fashion with an
|
||||
#+ appropriate error code.
|
||||
|
||||
echo "Swap file created and activated."
|
||||
|
||||
|
|
|
@ -18,5 +18,5 @@ exit 0
|
|||
|
||||
# Exercise (easy):
|
||||
# ---------------
|
||||
# Convert this script to taking command-line parameters
|
||||
# Convert this script to take command-line parameters
|
||||
#+ for $directory and $fstring.
|
||||
|
|
|
@ -287,8 +287,8 @@ while [ $COL -lt $WINNING_POS ]; do
|
|||
done
|
||||
|
||||
# Define old type and position of the "randomized horse".
|
||||
HORSE_TYPE=`cat horse_${MOVE_HORSE}_position | tail -1`
|
||||
COL=$(expr `cat horse_${MOVE_HORSE}_position | head -1`)
|
||||
HORSE_TYPE=`cat horse_${MOVE_HORSE}_position | tail -n 1`
|
||||
COL=$(expr `cat horse_${MOVE_HORSE}_position | head -n 1`)
|
||||
|
||||
ADD_POS=1
|
||||
# Check if the current position is an handicap position.
|
||||
|
@ -317,7 +317,7 @@ while [ $COL -lt $WINNING_POS ]; do
|
|||
echo -ne '\E[30;42m'
|
||||
|
||||
# Move the cursor to new horse position.
|
||||
tput cup `expr $MOVE_HORSE + 5` `cat horse_${MOVE_HORSE}_position | head -1`
|
||||
tput cup `expr $MOVE_HORSE + 5` `cat horse_${MOVE_HORSE}_position | head -n 1`
|
||||
|
||||
# Draw the horse.
|
||||
$DRAW_HORSE
|
||||
|
@ -351,7 +351,7 @@ echo -ne '\E[30;42m'
|
|||
echo -en '\E[5m'
|
||||
|
||||
# Make the winning horse blink.
|
||||
tput cup `expr $MOVE_HORSE + 5` `cat horse_${MOVE_HORSE}_position | head -1`
|
||||
tput cup `expr $MOVE_HORSE + 5` `cat horse_${MOVE_HORSE}_position | head -n 1`
|
||||
$DRAW_HORSE
|
||||
|
||||
# Disable blinking text.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/bash
|
||||
# hypotenuse.sh: Returns the "hypotenuse" of a right triangle.
|
||||
# ( square root of sum of squares of the "legs")
|
||||
# (square root of sum of squares of the "legs")
|
||||
|
||||
ARGS=2 # Script needs sides of triangle passed.
|
||||
E_BADARGS=65 # Wrong number of arguments.
|
||||
|
@ -13,11 +13,16 @@ fi
|
|||
|
||||
|
||||
AWKSCRIPT=' { printf( "%3.7f\n", sqrt($1*$1 + $2*$2) ) } '
|
||||
# command(s) / parameters passed to awk
|
||||
# command(s) / parameters passed to awk
|
||||
|
||||
|
||||
# Now, pipe the parameters to awk.
|
||||
echo -n "Hypotenuse of $1 and $2 = "
|
||||
echo $1 $2 | awk "$AWKSCRIPT"
|
||||
echo -n "Hypotenuse of $1 and $2 = "
|
||||
echo $1 $2 | awk "$AWKSCRIPT"
|
||||
# ^^^^^^^^^^^^
|
||||
# An echo-and-pipe is an easy way of passing shell parameters to awk.
|
||||
|
||||
exit 0
|
||||
|
||||
# Exercise: Rewrite this script using 'bc' rather than awk.
|
||||
# Which method is more intuitive?
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
string=abcdA01
|
||||
echo "len($string)" | m4 # 7
|
||||
echo "substr($string,4)" | m4 # A01
|
||||
echo "regexp($string,[0-1][0-1],\&Z)" | m4 # 01Z
|
||||
echo "regexp($string,[0-1][0-1],\&Z)" | m4 # 01Z
|
||||
|
||||
# Arithmetic
|
||||
echo "incr(22)" | m4 # 23
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#+ and slightly modified and commented by ABS Guide author.
|
||||
# Used in ABS Guide with permission. (Thank you!)
|
||||
|
||||
# This script will not run under Bash version < 3.0.
|
||||
# This script will not run under Bash versions < 3.0.
|
||||
|
||||
|
||||
E_MISSING_ARG=67
|
||||
|
|
|
@ -0,0 +1,359 @@
|
|||
#!/bin/bash
|
||||
# nightly-backup.sh
|
||||
# http://www.richardneill.org/source.php#nightly-backup-rsync
|
||||
# Copyright (c) 2005 Richard Neill <backup@richardneill.org>.
|
||||
# This is Free Software licensed under the GNU GPL.
|
||||
# ==> Included in ABS Guide with script author's kind permission.
|
||||
# ==> (Thanks!)
|
||||
|
||||
# This does a backup from the host computer to a locally connected
|
||||
#+ firewire HDD using rsync and ssh.
|
||||
# It then rotates the backups.
|
||||
# Run it via cron every night at 5am.
|
||||
# This only backs up the home directory.
|
||||
# If ownerships (other than the user's) should be preserved,
|
||||
#+ then run the rsync process as root (and re-instate the -o).
|
||||
# We save every day for 7 days, then every week for 4 weeks,
|
||||
#+ then every month for 3 months.
|
||||
|
||||
# See: http://www.mikerubel.org/computers/rsync_snapshots/
|
||||
#+ for more explanation of the theory.
|
||||
# Save as: $HOME/bin/nightly-backup_firewire-hdd.sh
|
||||
|
||||
# Known bugs:
|
||||
# ----------
|
||||
# i) Ideally, we want to exclude ~/.tmp and the browser caches.
|
||||
|
||||
# ii) If the user is sitting at the computer at 5am,
|
||||
#+ and files are modified while the rsync is occurring,
|
||||
#+ then the BACKUP_JUSTINCASE branch gets triggered.
|
||||
# To some extent, this is a
|
||||
#+ feature, but it also causes a "disk-space leak".
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
##### BEGIN CONFIGURATION SECTION ############################################
|
||||
LOCAL_USER=rjn # User whose home directory should be backed up.
|
||||
MOUNT_POINT=/backup # Mountpoint of backup drive.
|
||||
# NO trailing slash!
|
||||
# This must be unique (eg using a udev symlink)
|
||||
SOURCE_DIR=/home/$LOCAL_USER # NO trailing slash - it DOES matter to rsync.
|
||||
BACKUP_DEST_DIR=$MOUNT_POINT/backup/`hostname -s`.${LOCAL_USER}.nightly_backup
|
||||
DRY_RUN=false #If true, invoke rsync with -n, to do a dry run.
|
||||
# Comment out or set to false for normal use.
|
||||
VERBOSE=false # If true, make rsync verbose.
|
||||
# Comment out or set to false otherwise.
|
||||
COMPRESS=false # If true, compress.
|
||||
# Good for internet, bad on LAN.
|
||||
# Comment out or set to false otherwise.
|
||||
|
||||
### Exit Codes ###
|
||||
E_VARS_NOT_SET=64
|
||||
E_COMMANDLINE=65
|
||||
E_MOUNT_FAIL=70
|
||||
E_NOSOURCEDIR=71
|
||||
E_UNMOUNTED=72
|
||||
E_BACKUP=73
|
||||
##### END CONFIGURATION SECTION ##############################################
|
||||
|
||||
|
||||
# Check that all the important variables have been set:
|
||||
if [ -z "$LOCAL_USER" ] ||
|
||||
[ -z "$SOURCE_DIR" ] ||
|
||||
[ -z "$MOUNT_POINT" ] ||
|
||||
[ -z "$BACKUP_DEST_DIR" ]
|
||||
then
|
||||
echo 'One of the variables is not set! Edit the file: $0. BACKUP FAILED.'
|
||||
exit $E_VARS_NOT_SET
|
||||
fi
|
||||
|
||||
if [ "$#" != 0 ] # If command-line param(s) . . .
|
||||
then # Here document(ation).
|
||||
cat <<-ENDOFTEXT
|
||||
Automatic Nightly backup run from cron.
|
||||
Read the source for more details: $0
|
||||
The backup directory is $BACKUP_DEST_DIR .
|
||||
It will be created if necessary; initialisation is no longer required.
|
||||
|
||||
WARNING: Contents of $BACKUP_DEST_DIR are rotated.
|
||||
Directories named 'backup.\$i' will eventually be DELETED.
|
||||
We keep backups from every day for 7 days (1-8),
|
||||
then every week for 4 weeks (9-12),
|
||||
then every month for 3 months (13-15).
|
||||
|
||||
You may wish to add this to your crontab using 'crontab -e'
|
||||
# Back up files: $SOURCE_DIR to $BACKUP_DEST_DIR
|
||||
#+ every night at 3:15 am
|
||||
15 03 * * * /home/$LOCAL_USER/bin/nightly-backup_firewire-hdd.sh
|
||||
|
||||
Don't forget to verify the backups are working,
|
||||
especially if you don't read cron's mail!"
|
||||
ENDOFTEXT
|
||||
exit $E_COMMANDLINE
|
||||
fi
|
||||
|
||||
|
||||
# Parse the options.
|
||||
# ==================
|
||||
|
||||
if [ "$DRY_RUN" == "true" ]; then
|
||||
DRY_RUN="-n"
|
||||
echo "WARNING:"
|
||||
echo "THIS IS A 'DRY RUN'!"
|
||||
echo "No data will actually be transferred!"
|
||||
else
|
||||
DRY_RUN=""
|
||||
fi
|
||||
|
||||
if [ "$VERBOSE" == "true" ]; then
|
||||
VERBOSE="-v"
|
||||
else
|
||||
VERBOSE=""
|
||||
fi
|
||||
|
||||
if [ "$COMPRESS" == "true" ]; then
|
||||
COMPRESS="-z"
|
||||
else
|
||||
COMPRESS=""
|
||||
fi
|
||||
|
||||
|
||||
# Every week (actually of 8 days) and every month,
|
||||
#+ extra backups are preserved.
|
||||
DAY_OF_MONTH=`date +%d` # Day of month (01..31).
|
||||
if [ $DAY_OF_MONTH = 01 ]; then # First of month.
|
||||
MONTHSTART=true
|
||||
elif [ $DAY_OF_MONTH = 08 \
|
||||
-o $DAY_OF_MONTH = 16 \
|
||||
-o $DAY_OF_MONTH = 24 ]; then
|
||||
# Day 8,16,24 (use 8, not 7 to better handle 31-day months)
|
||||
WEEKSTART=true
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Check that the HDD is mounted.
|
||||
# At least, check that *something* is mounted here!
|
||||
# We can use something unique to the device, rather than just guessing
|
||||
#+ the scsi-id by having an appropriate udev rule in
|
||||
#+ /etc/udev/rules.d/10-rules.local
|
||||
#+ and by putting a relevant entry in /etc/fstab.
|
||||
# Eg: this udev rule:
|
||||
# BUS="scsi", KERNEL="sd*", SYSFS{vendor}="WDC WD16",
|
||||
# SYSFS{model}="00JB-00GVA0 ", NAME="%k", SYMLINK="lacie_1394d%n"
|
||||
|
||||
if mount | grep $MOUNT_POINT >/dev/null; then
|
||||
echo "Mount point $MOUNT_POINT is indeed mounted. OK"
|
||||
else
|
||||
echo -n "Attempting to mount $MOUNT_POINT..."
|
||||
# If it isn't mounted, try to mount it.
|
||||
sudo mount $MOUNT_POINT 2>/dev/null
|
||||
|
||||
if mount | grep $MOUNT_POINT >/dev/null; then
|
||||
UNMOUNT_LATER=TRUE
|
||||
echo "OK"
|
||||
# Note: Ensure that this is also unmounted
|
||||
#+ if we exit prematurely with failure.
|
||||
else
|
||||
echo "FAILED"
|
||||
echo -e "Nothing is mounted at $MOUNT_POINT. BACKUP FAILED!"
|
||||
exit $E_MOUNT_FAIL
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Check that source dir exists and is readable.
|
||||
if [ ! -r $SOURCE_DIR ] ; then
|
||||
echo "$SOURCE_DIR does not exist, or cannot be read. BACKUP FAILED."
|
||||
exit $E_NOSOURCEDIR
|
||||
fi
|
||||
|
||||
|
||||
# Check that the backup directory structure is as it should be.
|
||||
# If not, create it.
|
||||
# Create the subdirectories.
|
||||
# Note that backup.0 will be created as needed by rsync.
|
||||
|
||||
for ((i=1;i<=15;i++)); do
|
||||
if [ ! -d $BACKUP_DEST_DIR/backup.$i ]; then
|
||||
if /bin/mkdir -p $BACKUP_DEST_DIR/backup.$i ; then
|
||||
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ No [ ] test brackets. Why?
|
||||
echo "Warning: directory $BACKUP_DEST_DIR/backup.$i is missing,"
|
||||
echo "or was not initialised. (Re-)creating it."
|
||||
else
|
||||
echo "ERROR: directory $BACKUP_DEST_DIR/backup.$i"
|
||||
echo "is missing and could not be created."
|
||||
if [ "$UNMOUNT_LATER" == "TRUE" ]; then
|
||||
# Before we exit, unmount the mount point if necessary.
|
||||
cd
|
||||
sudo umount $MOUNT_POINT &&
|
||||
echo "Unmounted $MOUNT_POINT again. Giving up."
|
||||
fi
|
||||
exit $E_UNMOUNTED
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
# Set the permission to 700 for security
|
||||
#+ on an otherwise permissive multi-user system.
|
||||
if ! /bin/chmod 700 $BACKUP_DEST_DIR ; then
|
||||
echo "ERROR: Could not set permissions on $BACKUP_DEST_DIR to 700."
|
||||
|
||||
if [ "$UNMOUNT_LATER" == "TRUE" ]; then
|
||||
# Before we exit, unmount the mount point if necessary.
|
||||
cd ; sudo umount $MOUNT_POINT && echo "Unmounted $MOUNT_POINT again. Giving up."
|
||||
fi
|
||||
|
||||
exit $E_UNMOUNTED
|
||||
fi
|
||||
|
||||
# Create the symlink: current -> backup.1 if required.
|
||||
# A failure here is not critical.
|
||||
cd $BACKUP_DEST_DIR
|
||||
if [ ! -h current ] ; then
|
||||
if ! /bin/ln -s backup.1 current ; then
|
||||
echo "WARNING: could not create symlink current -> backup.1"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# Now, do the rsync.
|
||||
echo "Now doing backup with rsync..."
|
||||
echo "Source dir: $SOURCE_DIR"
|
||||
echo -e "Backup destination dir: $BACKUP_DEST_DIR\n"
|
||||
|
||||
|
||||
/usr/bin/rsync $DRY_RUN $VERBOSE -a -S --delete --modify-window=60 \
|
||||
--link-dest=../backup.1 $SOURCE_DIR $BACKUP_DEST_DIR/backup.0/
|
||||
|
||||
# Only warn, rather than exit if the rsync failed,
|
||||
#+ since it may only be a minor problem.
|
||||
# E.g., if one file is not readable, rsync will fail.
|
||||
# This shouldn't prevent the rotation.
|
||||
# Not using, e.g., `date +%a` since these directories
|
||||
#+ are just full of links and don't consume *that much* space.
|
||||
|
||||
if [ $? != 0 ]; then
|
||||
BACKUP_JUSTINCASE=backup.`date +%F_%T`.justincase
|
||||
echo "WARNING: the rsync process did not entirely succeed."
|
||||
echo "Something might be wrong. Saving an extra copy at: $BACKUP_JUSTINCASE"
|
||||
echo "WARNING: if this occurs regularly, a LOT of space will be consumed,"
|
||||
echo "even though these are just hard-links!"
|
||||
fi
|
||||
|
||||
# Save a readme in the backup parent directory.
|
||||
# Save another one in the recent subdirectory.
|
||||
echo "Backup of $SOURCE_DIR on `hostname` was last run on \
|
||||
`date`" > $BACKUP_DEST_DIR/README.txt
|
||||
echo "This backup of $SOURCE_DIR on `hostname` was created on \
|
||||
`date`" > $BACKUP_DEST_DIR/backup.0/README.txt
|
||||
|
||||
# If we are not in a dry run, rotate the backups.
|
||||
[ -z "$DRY_RUN" ] &&
|
||||
|
||||
# Check how full the backup disk is.
|
||||
# Warn if 90%. if 98% or more, we'll probably fail, so give up.
|
||||
# (Note: df can output to more than one line.)
|
||||
# We test this here, rather than before
|
||||
#+ so that rsync may possibly have a chance.
|
||||
DISK_FULL_PERCENT=`/bin/df $BACKUP_DEST_DIR |
|
||||
tr "\n" ' ' | awk '{print $12}' | grep -oE [0-9]+ `
|
||||
echo "Disk space check on backup partition \
|
||||
$MOUNT_POINT $DISK_FULL_PERCENT% full."
|
||||
if [ $DISK_FULL_PERCENT -gt 90 ]; then
|
||||
echo "Warning: Disk is greater than 90% full."
|
||||
fi
|
||||
if [ $DISK_FULL_PERCENT -gt 98 ]; then
|
||||
echo "Error: Disk is full! Giving up."
|
||||
if [ "$UNMOUNT_LATER" == "TRUE" ]; then
|
||||
# Before we exit, unmount the mount point if necessary.
|
||||
cd; sudo umount $MOUNT_POINT &&
|
||||
echo "Unmounted $MOUNT_POINT again. Giving up."
|
||||
fi
|
||||
exit $E_UNMOUNTED
|
||||
fi
|
||||
|
||||
|
||||
# Create an extra backup.
|
||||
# If this copy fails, give up.
|
||||
if [ -n "$BACKUP_JUSTINCASE" ]; then
|
||||
if ! /bin/cp -al $BACKUP_DEST_DIR/backup.0 $BACKUP_DEST_DIR/$BACKUP_JUSTINCASE
|
||||
then
|
||||
echo "ERROR: Failed to create extra copy \
|
||||
$BACKUP_DEST_DIR/$BACKUP_JUSTINCASE"
|
||||
if [ "$UNMOUNT_LATER" == "TRUE" ]; then
|
||||
# Before we exit, unmount the mount point if necessary.
|
||||
cd ;sudo umount $MOUNT_POINT &&
|
||||
echo "Unmounted $MOUNT_POINT again. Giving up."
|
||||
fi
|
||||
exit $E_UNMOUNTED
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
# At start of month, rotate the oldest 8.
|
||||
if [ "$MONTHSTART" == "true" ]; then
|
||||
echo -e "\nStart of month. \
|
||||
Removing oldest backup: $BACKUP_DEST_DIR/backup.15" &&
|
||||
/bin/rm -rf $BACKUP_DEST_DIR/backup.15 &&
|
||||
echo "Rotating monthly,weekly backups: \
|
||||
$BACKUP_DEST_DIR/backup.[8-14] -> $BACKUP_DEST_DIR/backup.[9-15]" &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.14 $BACKUP_DEST_DIR/backup.15 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.13 $BACKUP_DEST_DIR/backup.14 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.12 $BACKUP_DEST_DIR/backup.13 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.11 $BACKUP_DEST_DIR/backup.12 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.10 $BACKUP_DEST_DIR/backup.11 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.9 $BACKUP_DEST_DIR/backup.10 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.8 $BACKUP_DEST_DIR/backup.9
|
||||
|
||||
# At start of week, rotate the second-oldest 4.
|
||||
elif [ "$WEEKSTART" == "true" ]; then
|
||||
echo -e "\nStart of week. \
|
||||
Removing oldest weekly backup: $BACKUP_DEST_DIR/backup.12" &&
|
||||
/bin/rm -rf $BACKUP_DEST_DIR/backup.12 &&
|
||||
|
||||
echo "Rotating weekly backups: \
|
||||
$BACKUP_DEST_DIR/backup.[8-11] -> $BACKUP_DEST_DIR/backup.[9-12]" &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.11 $BACKUP_DEST_DIR/backup.12 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.10 $BACKUP_DEST_DIR/backup.11 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.9 $BACKUP_DEST_DIR/backup.10 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.8 $BACKUP_DEST_DIR/backup.9
|
||||
|
||||
else
|
||||
echo -e "\nRemoving oldest daily backup: $BACKUP_DEST_DIR/backup.8" &&
|
||||
/bin/rm -rf $BACKUP_DEST_DIR/backup.8
|
||||
|
||||
fi &&
|
||||
|
||||
# Every day, rotate the newest 8.
|
||||
echo "Rotating daily backups: \
|
||||
$BACKUP_DEST_DIR/backup.[1-7] -> $BACKUP_DEST_DIR/backup.[2-8]" &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.7 $BACKUP_DEST_DIR/backup.8 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.6 $BACKUP_DEST_DIR/backup.7 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.5 $BACKUP_DEST_DIR/backup.6 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.4 $BACKUP_DEST_DIR/backup.5 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.3 $BACKUP_DEST_DIR/backup.4 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.2 $BACKUP_DEST_DIR/backup.3 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.1 $BACKUP_DEST_DIR/backup.2 &&
|
||||
/bin/mv $BACKUP_DEST_DIR/backup.0 $BACKUP_DEST_DIR/backup.1 &&
|
||||
|
||||
SUCCESS=true
|
||||
|
||||
|
||||
if [ "$UNMOUNT_LATER" == "TRUE" ]; then
|
||||
# Unmount the mount point if it wasn't mounted to begin with.
|
||||
cd ; sudo umount $MOUNT_POINT && echo "Unmounted $MOUNT_POINT again."
|
||||
fi
|
||||
|
||||
|
||||
if [ "$SUCCESS" == "true" ]; then
|
||||
echo 'SUCCESS!'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Should have already exited if backup worked.
|
||||
echo 'BACKUP FAILED! Is this just a dry run? Is the disk full?) '
|
||||
exit $E_BACKUP
|
|
@ -28,7 +28,7 @@ echo
|
|||
|
||||
while [ $TRUE ] #Endless loop.
|
||||
do
|
||||
tail -$CHECK_LINES $LOGFILE> $TEMPFILE
|
||||
tail -n $CHECK_LINES $LOGFILE> $TEMPFILE
|
||||
# Saves last 100 lines of system log file as temp file.
|
||||
# Necessary, since newer kernels generate many log messages at log on.
|
||||
search=`grep $KEYWORD $TEMPFILE`
|
||||
|
@ -77,7 +77,7 @@ done
|
|||
|
||||
CHECK_INTERVAL=1
|
||||
|
||||
while ! tail -1 "$LOGFILE" | grep -q "$KEYWORD"
|
||||
while ! tail -n 1 "$LOGFILE" | grep -q "$KEYWORD"
|
||||
do echo -n .
|
||||
sleep $CHECK_INTERVAL
|
||||
done
|
||||
|
|
|
@ -32,12 +32,16 @@ then #+ so no error if this script is run
|
|||
mkdir $MOUNTPT #+ multiple times.
|
||||
fi
|
||||
|
||||
##############################################################################
|
||||
dd if=/dev/zero of=$DEVICE count=$SIZE bs=$BLOCKSIZE # Zero out RAM device.
|
||||
# Why is this necessary?
|
||||
mke2fs $DEVICE # Create an ext2 filesystem on it.
|
||||
mount $DEVICE $MOUNTPT # Mount it.
|
||||
chmod 777 $MOUNTPT # Enables ordinary user to access ramdisk.
|
||||
# However, must be root to unmount it.
|
||||
##############################################################################
|
||||
# Need to test whether above commands succeed. Could cause problems otherwise.
|
||||
# Exercise: modify this script to make it safer.
|
||||
|
||||
echo "\"$MOUNTPT\" now available for use."
|
||||
# The ramdisk is now accessible for storing files, even by an ordinary user.
|
||||
|
|
|
@ -8,6 +8,7 @@ set -- $variable
|
|||
first_param=$1
|
||||
second_param=$2
|
||||
shift; shift # Shift past first two positional params.
|
||||
# shift 2 also works.
|
||||
remaining_params="$*"
|
||||
|
||||
echo
|
||||
|
|
|
@ -3,21 +3,30 @@
|
|||
|
||||
echo
|
||||
|
||||
echo "We are outside the subshell."
|
||||
echo "Subshell level OUTSIDE subshell = $BASH_SUBSHELL"
|
||||
# Bash, version 3, adds the new $BASH_SUBSHELL variable.
|
||||
echo
|
||||
echo; echo
|
||||
|
||||
outer_variable=Outer
|
||||
global_variable=
|
||||
# Define global variable for "storage" of
|
||||
#+ value of subshell variable.
|
||||
|
||||
(
|
||||
echo "We are inside the subshell."
|
||||
echo "Subshell level INSIDE subshell = $BASH_SUBSHELL"
|
||||
inner_variable=Inner
|
||||
|
||||
echo "From subshell, \"inner_variable\" = $inner_variable"
|
||||
echo "From subshell, \"outer\" = $outer_variable"
|
||||
echo "From inside subshell, \"inner_variable\" = $inner_variable"
|
||||
echo "From inside subshell, \"outer\" = $outer_variable"
|
||||
|
||||
global_variable="$inner_variable" # Will this allow "exporting"
|
||||
#+ a subshell variable?
|
||||
)
|
||||
|
||||
echo
|
||||
echo; echo
|
||||
echo "We are outside the subshell."
|
||||
echo "Subshell level OUTSIDE subshell = $BASH_SUBSHELL"
|
||||
echo
|
||||
|
||||
|
@ -29,10 +38,18 @@ else
|
|||
fi
|
||||
|
||||
echo "From main body of shell, \"inner_variable\" = $inner_variable"
|
||||
# $inner_variable will show as uninitialized
|
||||
# $inner_variable will show as blank (uninitialized)
|
||||
#+ because variables defined in a subshell are "local variables".
|
||||
# Is there any remedy for this?
|
||||
# Is there a remedy for this?
|
||||
echo "global_variable = "$global_variable"" # Why doesn't this work?
|
||||
|
||||
|
||||
echo
|
||||
|
||||
exit 0
|
||||
|
||||
# Question:
|
||||
# --------
|
||||
# Once having exited a subshell,
|
||||
#+ is there any way to reenter that very same subshell
|
||||
#+ to modify or access the subshell variables?
|
||||
|
|
|
@ -8,4 +8,9 @@ unset variable # Unset.
|
|||
# Same effect as: variable=
|
||||
echo "(unset) variable = $variable" # $variable is null.
|
||||
|
||||
if [ -z "$variable" ] # Try a string-length test.
|
||||
then
|
||||
echo "\$variable has zero length."
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -57,4 +57,4 @@ exit 0
|
|||
# 1) Add 'sed' commands to filter out other punctuation,
|
||||
#+ such as semicolons.
|
||||
# 2) Modify the script to also filter out multiple spaces and
|
||||
# other whitespace.
|
||||
#+ other whitespace.
|
||||
|
|
Loading…
Reference in New Issue