<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Computer Support &#187; Shell scripts</title>
	<atom:link href="http://www.xiitec.com/blog/category/system-administration/unix/shell-scripts/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.xiitec.com/blog</link>
	<description></description>
	<lastBuildDate>Wed, 30 Dec 2009 08:40:28 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Avoiding Disaster with a Remote Archive</title>
		<link>http://www.xiitec.com/blog/2008/02/08/avoiding-disaster-with-a-remote-archive/</link>
		<comments>http://www.xiitec.com/blog/2008/02/08/avoiding-disaster-with-a-remote-archive/#comments</comments>
		<pubDate>Sat, 09 Feb 2008 00:24:02 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Shell scripts]]></category>
		<category><![CDATA[Remote archive]]></category>

		<guid isPermaLink="false">http://www.xiitec.com/blog/?p=105</guid>
		<description><![CDATA[Whether or not you have a good backup strategy, with tape rotation and so forth, it&#8217;s still a nice insurance policy to identify a half-dozen critical files and have them sent to a separate off-site archive system. Even if it&#8217;s just that one key file that contains customer addresses, invoices, or even email from your [...]]]></description>
			<content:encoded><![CDATA[<p>Whether or not you have a good backup strategy, with tape rotation and so forth, it&#8217;s still a nice insurance policy to identify a half-dozen critical files and have them sent to a separate off-site archive system. Even if it&#8217;s just that one key file that contains customer addresses, invoices, or even email from your sweetheart, having an occasional off-site archive can save your life when you least expect it.</p>
<p><span id="more-105"></span></p>
<p>This sounds more complex than it really is, because as you&#8217;ll see in this script, the archive is just a file emailed to a remote mailbox and could even be pointed to a Yahoo! or Hotmail mailbox. The list of files is kept in a separate data file, with shell wildcards allowed therein. Filenames can contain spaces too, something that rather complicates the script, as you&#8217;ll see.</p>
<blockquote><p>#!/bin/sh</p>
<p># remotebackup &#8211; Takes a list of files and directories,<br />
#    builds a single archive, compressed, then emails it off to a<br />
#    remote archive site for safekeeping. It&#8217;s intended to be run<br />
#    every night for critical user files, but not intended to<br />
#    replace a more rigorous backup scheme. You should strongly<br />
#    consider using unpacker, Script #88, on the remote end too.</p>
<p>uuencode=&#8221;/usr/bin/uuencode&#8221;<br />
outfile=&#8221;/tmp/rb.$$.tgz&#8221;<br />
outfname=&#8221;backup.$(date +%y%m%d).tgz&#8221;<br />
infile=&#8221;/tmp/rb.$$.in&#8221;</p>
<p>trap &#8220;/bin/rm -f $outfile $infile&#8221; 0</p>
<p>if [ $# -ne 2 -a $# -ne 3 ] ; then<br />
echo &#8220;Usage: $(basename $0) backup-file-list remoteaddr {targetdir}&#8221; &gt;&amp;2<br />
exit 1<br />
fi</p>
<p>if [ ! -s "$1" ] ; then<br />
echo &#8220;Error: backup list $1 is empty or missing&#8221; &gt;&amp;2<br />
exit 1<br />
fi</p>
<p># Scan entries and build fixed infile list. This expands wildcards<br />
# and escapes spaces in filenames with a backslash, producing a<br />
# change: &#8220;this file&#8221; becomes this\ file so quotes are not needed.</p>
<p>while read entry; do<br />
echo &#8220;$entry&#8221; | sed -e &#8217;s/ /\\ /g&#8217; &gt;&gt; $infile<br />
done &lt; &#8220;$1&#8243;</p>
<p># The actual work of building the archive, encoding it, and sending it</p>
<p>tar czf &#8211; $(cat $infile) | \<br />
$uuencode $outfname | \<br />
mail -s &#8220;${3:-Backup archive for $(date)}&#8221; &#8220;$2&#8243;</p>
<p>echo &#8220;Done. $(basename $0) backed up the following files:&#8221;<br />
sed &#8217;s/^/   /&#8217; $infile<br />
echo -n &#8220;and mailed them to $2 &#8221;<br />
if [ ! -z "$3" ] ; then<br />
echo &#8220;with requested target directory $3&#8243;<br />
else<br />
echo &#8220;&#8221;<br />
fi</p>
<p>exit 0</p></blockquote>
<p>After the basic validity checks, the script processes the file containing the list of critical files, which is supplied as the first command argument, to ensure that spaces embedded in its filenames will work in the <code>while </code>loop (remember, by default spaces delimit arguments, so without some additional help, the shell will think that &#8220;test file&#8221; is two arguments, not one). It does this by prefacing every space with a backslash. Then it builds the archive with the primitive but useful tar command, which lacks the ability to read standard input for its file list and thus must be fed the filenames via a cat invocation.</p>
<blockquote><p>tar czf &#8211; $(cat $infile)</p></blockquote>
<p>The <code>tar </code>invocation automatically compresses the archive, and <code>uuencode </code>is then utilized to ensure that the resultant archive data file can be successfully emailed without corruption. The end result is that the remote address receives an email message with the uuencoded tar archive as an attachment. This should be a straightforward script.</p>
<p>This script expects two arguments: the name of a file that contains a list of files to archive and back up, and the destination email address for the compressed, uuencoded archive file. The file list can be as simple as</p>
<blockquote><p>$ cat filelist<br />
*.sh<br />
*.html</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.xiitec.com/blog/2008/02/08/avoiding-disaster-with-a-remote-archive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Rotating Log Files</title>
		<link>http://www.xiitec.com/blog/2008/02/04/rotating-log-files/</link>
		<comments>http://www.xiitec.com/blog/2008/02/04/rotating-log-files/#comments</comments>
		<pubDate>Mon, 04 Feb 2008 19:28:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Shell scripts]]></category>
		<category><![CDATA[logs]]></category>
		<category><![CDATA[rotate]]></category>

		<guid isPermaLink="false">http://www.xiitec.com/blog/?p=96</guid>
		<description><![CDATA[Users who don&#8217;t have much experience with Unix can be quite surprised by how many commands, utilities, and daemons log events to system log files. Even on a computer with lots of disk space, it&#8217;s important to keep an eye on the size of these files and, of course, on their contents too.

As a result, [...]]]></description>
			<content:encoded><![CDATA[<p>Users who don&#8217;t have much experience with Unix can be quite surprised by how many commands, utilities, and daemons log events to system log files. Even on a computer with lots of disk space, it&#8217;s important to keep an eye on the size of these files and, of course, on their contents too.</p>
<p><span id="more-96"></span></p>
<p>As a result, most sysadmins have a set of instructions that they place at the top of their log file analysis utilities, similar to the following:</p>
<blockquote><p>mv $log.2 $log.3<br />
mv $log.1 $log.2<br />
mv $log $log.1<br />
touch $log</p></blockquote>
<p>If run weekly, this would produce a rolling one-month archive of log file information divided into week-size portions of data. However, it&#8217;s just as easy to create a script that accomplishes this for all log files in the /var/log directory at once, thereby relieving any log file analysis scripts of the burden.</p>
<p>The script steps through each file in the /var/log directory that matches a particular set of criteria, checking each matching file&#8217;s rotation schedule and last-modified date to see if it&#8217;s time for it to be rotated.</p>
<blockquote><p>#!/bin/sh<br />
# rotatelogs &#8211; Rolls logfiles in /var/log for archival purposes.<br />
#    Uses a config file to allow customization of how frequently<br />
#    each log should be rolled. The config file is in<br />
#       logfilename=duration<br />
#    format, where duration is in days. If, in the config<br />
#    file, an entry is missing for a particular logfilename,<br />
#    rotatelogs won&#8217;t rotate the file more frequently than every seven days.</p>
<p>logdir=&#8221;/var/log&#8221;<br />
config=&#8221;/var/log/rotatelogs.conf&#8221;<br />
mv=&#8221;/bin/mv&#8221;<br />
default_duration=7     count=0</p>
<p>duration=$default_duration</p>
<p>if [ ! -f $config ] ; then<br />
echo &#8220;$0: no config file found. Can&#8217;t proceed.&#8221; &gt;&amp;2; exit 1<br />
fi</p>
<p>if [ ! -w $logdir -o ! -x $logdir ] ; then<br />
echo &#8220;$0: you don&#8217;t have the appropriate permissions in $logdir&#8221; &gt;&amp;2<br />
exit 1<br />
fi</p>
<p>cd $logdir</p>
<p># While we&#8217;d like to use &#8216;:digit:&#8217; with the find, many versions of<br />
# find don&#8217;t support POSIX character class identifiers, hence [0-9]</p>
<p>for name in $(find . -type f -size +0c ! -name &#8216;*[0-9]*&#8217; \<br />
! -name &#8216;\.*&#8217; ! -name &#8216;*conf&#8217; -maxdepth 1 -print | sed &#8217;s/^\.\///&#8217;)<br />
do</p>
<p>count=$(( $count + 1 ))</p>
<p># Grab this entry from the config file</p>
<p>duration=&#8221;$(grep &#8220;^${name}=&#8221; $config|cut -d= -f2)&#8221;<br />
if [ -z $duration ] ; then<br />
duration=$default_duration<br />
elif [ "$duration" = "0" ] ; then<br />
echo &#8220;Duration set to zero: skipping $name&#8221;<br />
continue<br />
fi</p>
<p>back1=&#8221;${name}.1&#8243;; back2=&#8221;${name}.2&#8243;;<br />
back3=&#8221;${name}.3&#8243;; back4=&#8221;${name}.4&#8243;;</p>
<p># If the most recently rolled log file (back1) has been modified within<br />
# the specific quantum, then it&#8217;s not time to rotate it.</p>
<p>if [ -f "$back1" ] ; then<br />
if [ -z $(find \"$back1\" -mtime +$duration -print 2&gt;/dev/null) ]<br />
then<br />
echo -n &#8220;$name&#8217;s most recent backup is more recent than $duration &#8221;<br />
echo &#8220;days: skipping&#8221; ;   continue<br />
fi<br />
fi</p>
<p>echo &#8220;Rotating log $name (using a $duration day schedule)&#8221;</p>
<p># Rotate, starting with the oldest log<br />
if [ -f "$back3" ] ; then<br />
echo &#8220;&#8230; $back3 -&gt; $back4&#8243; ; $mv -f &#8220;$back3&#8243; &#8220;$back4&#8243;<br />
fi<br />
if [ -f "$back2" ] ; then<br />
echo &#8220;&#8230; $back2 -&gt; $back3&#8243; ; $mv -f &#8220;$back2&#8243; &#8220;$back3&#8243;<br />
fi<br />
if [ -f "$back1" ] ; then<br />
echo &#8220;&#8230; $back1 -&gt; $back2&#8243; ; $mv -f &#8220;$back1&#8243; &#8220;$back2&#8243;<br />
fi<br />
if [ -f "$name" ] ; then<br />
echo &#8220;&#8230; $name -&gt; $back1&#8243; ; $mv -f &#8220;$name&#8221; &#8220;$back1&#8243;<br />
fi<br />
touch &#8220;$name&#8221;<br />
chmod 0600 &#8220;$name&#8221;<br />
done</p>
<p>if [ $count -eq 0 ] ; then<br />
echo &#8220;Nothing to do: no log files big enough or old enough to rotate&#8221;<br />
fi<br />
exit 0</p></blockquote>
<p>To truly be useful, the script needs to work with a configuration file that lives in /var/log, which allows different log files to be set to different rotation schedules. The contents of a typical configuration file are as follows:</p>
<blockquote><p># Configuration file for the log rotation script.<br />
# Format is    name=duration    where &#8216;name&#8217; can be any<br />
# filename that appears in the /var/log directory. Duration<br />
# is measured in days.</p>
<p>ftp.log=30<br />
lastlog=14<br />
lookupd.log=7<br />
lpr.log=30<br />
mail.log=7<br />
netinfo.log=7<br />
secure.log=7<br />
statistics=7<br />
system.log=14<br />
# Anything with a duration of zero is not rotated<br />
wtmp=0</p></blockquote>
<p>The heart of this script is the find statement:</p>
<blockquote><p> for name in $(find . -type f -size +0c ! -name &#8216;*[0-9]*&#8217; \<br />
! -name &#8216;\.*&#8217; ! -name &#8216;*conf&#8217; -maxdepth 1 -print | sed &#8217;s/^\.\///&#8217;)</p></blockquote>
<p>This creates a loop, returning all files in the /var/log directory that are greater than 0 characters in size, don&#8217;t contain a number in their name, don&#8217;t start with a period (Mac OS X in particular dumps a lot of oddly named log files in this directory; they all need to be skipped), and don&#8217;t end with the word &#8220;conf&#8221; (we don&#8217;t want to rotate out the rotatelogs.conf file, for obvious reasons!). The maxdepth 1 ensures that find doesn&#8217;t step into subdirectories. Finally, the sed invocation removes any leading ./ sequences.</p>
<p>The Results</p>
<blockquote><p>$ sudo rotatelogs<br />
ftp.log&#8217;s most recent backup is more recent than 30 days: skipping<br />
Rotating log lastlog (using a 14 day schedule)<br />
&#8230; lastlog -&gt; lastlog.1<br />
lpr.log&#8217;s most recent backup is more recent than 30 days: skipping</p></blockquote>
<p>Notice that of all the log files in /var/log, only three matched the specified find criteria, and of those only one, lastlog, hadn&#8217;t been backed up sufficiently recently, according to the duration values in the configuration file shown earlier.</p>
<p>One example of how this script could be even more useful is to have the oldest archive file, the old $back4 file, emailed to a central storage site before it&#8217;s over-written by the mv command in the following statement:</p>
<blockquote><p>echo &#8220;&#8230; $back3 -&gt; $back4&#8243; ; $mv -f &#8220;$back3&#8243; &#8220;$back4&#8243;</p></blockquote>
<p>Another useful enhancement to rotatelogs would be to compress all rotated logs to further save on disk space, which would also require that the script recognize and work properly with compressed files as it proceeded.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xiitec.com/blog/2008/02/04/rotating-log-files/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Managing Backups</title>
		<link>http://www.xiitec.com/blog/2008/02/04/managing-backups/</link>
		<comments>http://www.xiitec.com/blog/2008/02/04/managing-backups/#comments</comments>
		<pubDate>Mon, 04 Feb 2008 19:24:57 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Shell scripts]]></category>
		<category><![CDATA[backups]]></category>
		<category><![CDATA[manage]]></category>

		<guid isPermaLink="false">http://www.xiitec.com/blog/?p=95</guid>
		<description><![CDATA[Managing system backups is a task that all system administrators are familiar with, and it&#8217;s something that no one thanks you for doing unless something goes horribly wrong. Even on a single-user personal computer running Linux, some sort of backup schedule is essential, and it&#8217;s usually only after you&#8217;ve been burned once, losing a chunk [...]]]></description>
			<content:encoded><![CDATA[<p>Managing system backups is a task that all system administrators are familiar with, and it&#8217;s something that no one thanks you for doing unless something goes horribly wrong. Even on a single-user personal computer running Linux, some sort of backup schedule is essential, and it&#8217;s usually only after you&#8217;ve been burned once, losing a chunk of data and files, that you realize the value of a regular backup.</p>
<p><span id="more-95"></span></p>
<p>One of the reasons so many systems neglect backups is that many of the backup tools are crude and difficult to understand. The dump and restore commands (called ufsdump and restore in Solaris) are typical, with five &#8220;dump levels&#8221; and an intimidating configuration file required.</p>
<p>A shell script can solve this problem. This script backs up a specified set of directories, either incrementally (that is, only those files that have changed since the last backup) or full backup (all files). The backup is compressed on the fly to minimize space usage, and the script output can be directed to a file, a tape device, a remotely mounted NFS partition, or even a CD burner on compatible systems.</p>
<blockquote><p>#!/bin/sh</p>
<p># backup &#8211; Creates either a full or incremental backup of a set of<br />
#     defined directories on the system. By default, the output<br />
#     file is saved in /tmp with a timestamped filename, compressed.<br />
#     Otherwise, specify an output device (another disk, or a<br />
#     removable storage device).</p>
<p>usageQuit()<br />
{<br />
cat &lt;&lt; &#8220;EOF&#8221; &gt;&amp;2<br />
Usage: $0 [-o output] [-i|-f] [-n]<br />
-o lets you specify an alternative backup file/device<br />
-i is an incremental or -f is a full backup, and -n prevents<br />
updating the timestamp if an incremental backup is done.<br />
EOF<br />
exit 1<br />
}</p>
<p>compress=&#8221;bzip2&#8243;                # change for your favorite compression app<br />
inclist=&#8221;/tmp/backup.inclist.$(date +%d%m%y)&#8221;<br />
output=&#8221;/tmp/backup.$(date +%d%m%y).bz2&#8243;<br />
tsfile=&#8221;$HOME/.backup.timestamp&#8221;<br />
btype=&#8221;incremental&#8221;           # default to an incremental backup<br />
noinc=0                       # and an update of the timestamp</p>
<p>trap &#8220;/bin/rm -f $inclist&#8221; EXIT</p>
<p>while getopts &#8220;o:ifn&#8221; arg; do<br />
case &#8220;$arg&#8221; in<br />
o ) output=&#8221;$OPTARG&#8221;;        ;;<br />
i ) btype=&#8221;incremental&#8221;;     ;;<br />
f ) btype=&#8221;full&#8221;;            ;;<br />
n ) noinc=1;                 ;;<br />
? ) usageQuit                ;;<br />
esac<br />
done</p>
<p>shift $(($OPTIND &#8211; 1))</p>
<p>echo &#8220;Doing $btype backup, saving output to $output&#8221;</p>
<p>timestamp=&#8221;$(date +&#8217;%m%d%I%M&#8217;)&#8221;</p>
<p>if [ "$btype" = "incremental" ] ; then<br />
if [ ! -f $tsfile ] ; then<br />
echo &#8220;Error: can&#8217;t do an incremental backup: no timestamp file&#8221; &gt;&amp;2<br />
exit 1<br />
fi<br />
find $HOME -depth -type f -newer $tsfile -user ${USER:-LOGNAME} | \<br />
pax -w -x tar | $compress &gt; $output<br />
failure=&#8221;$?&#8221;<br />
else<br />
find $HOME -depth -type f -user ${USER:-LOGNAME} | \<br />
pax -w -x tar | $compress &gt; $output<br />
failure=&#8221;$?&#8221;<br />
fi</p>
<p>if [ "$noinc" = "0" -a "$failure" = "0" ] ; then<br />
touch -t $timestamp $tsfile<br />
fi<br />
exit 0</p></blockquote>
<p>For a full system backup, the pax command does all the work, piping its output to a compression program (bzip2 by default) and then to an output file or device. An incremental backup is a bit more tricky because the standard version of tar doesn&#8217;t include any sort of modification time test, unlike the GNU version of tar. The list of files modified since the previous backup is built with find and saved in the inclist temporary file. That file, emulating the tar output format for increased portability, is then fed to pax directly.</p>
<p>Choosing when to mark the timestamp for a backup is an area in which many backup programs get messed up, typically marking the &#8220;last backup time&#8221; when the program has finished the backup, rather than when it started. Setting the timestamp to the time of backup completion can be a problem if any files are modified during the backup process (which can take quite a while if the backup is being fed to a tape device). Because files modified under this scenario would have a last-modified date older than the timestamp date, they would not be backed up the next night.</p>
<p>However, timestamping before the backup takes place is wrong too, because if the backup fails, there&#8217;s no way to reverse the updated timestamp. Both of these problems are avoided by saving the date and time before the backup starts (in the timestamp variable), but applying the value of $timestamp to $tsfile using the -t flag to touch only after the backup has succeeded.</p>
<p>This script has a number of options, all of which can be ignored to perform the default incremental backup based on the timestamp for the last incremental backup. The flags allow you to specify a different output file or device (-o output), to choose a full backup (-f), to actively choose an incremental backup (-i), or to prevent the timestamp file from being updated in the case of an incremental backup (-n).</p>
<blockquote><p>$ backup<br />
Doing incremental backup, saving output to /tmp/backup.140703.bz2</p></blockquote>
<p>As you would expect, the output of a backup program isn&#8217;t very scintillating. But the resulting compressed file is sufficiently large that it shows plenty of data is within:</p>
<blockquote><p> $ ls -l /tmp/backup*<br />
-rw-r&#8211;r&#8211;  1 taylor  wheel  61739008 Jul 14 07:31 backup.140703.bz2</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.xiitec.com/blog/2008/02/04/managing-backups/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Backing Up Directories</title>
		<link>http://www.xiitec.com/blog/2008/02/04/backing-up-directories/</link>
		<comments>http://www.xiitec.com/blog/2008/02/04/backing-up-directories/#comments</comments>
		<pubDate>Mon, 04 Feb 2008 19:21:46 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Shell scripts]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[directory]]></category>

		<guid isPermaLink="false">http://www.xiitec.com/blog/?p=94</guid>
		<description><![CDATA[Related to the task of backing up entire file systems is the user-centric task of taking a snapshot of a specific directory or directory tree. This simple script allows users to easily create a compressed tar archive of a specified directory.

#!/bin/sh
# archivedir &#8211; Creates a compressed archive of the specified directory.
maxarchivedir=10     [...]]]></description>
			<content:encoded><![CDATA[<p>Related to the task of backing up entire file systems is the user-centric task of taking a snapshot of a specific directory or directory tree. This simple script allows users to easily create a compressed tar archive of a specified directory.</p>
<p><span id="more-94"></span></p>
<blockquote><p>#!/bin/sh</p>
<p># archivedir &#8211; Creates a compressed archive of the specified directory.</p>
<p>maxarchivedir=10        # size, in blocks, of &#8216;big&#8217; directory<br />
compress=gzip           # change to your favorite compress app<br />
progname=$(basename $0)</p>
<p>if [ $# -eq 0 ] ; then<br />
echo &#8220;Usage: $progname directory&#8221; &gt;&amp;2 ;exit 1<br />
fi</p>
<p>if [ ! -d $1 ] ; then<br />
echo &#8220;${progname}: can&#8217;t find directory $1 to archive.&#8221; &gt;&amp;2; exit 1<br />
fi</p>
<p>if [ "$(basename $1)" != "$1" -o "$1" = "." ] ; then<br />
echo &#8220;${progname}: You must specify a subdirectory&#8221; &gt;&amp;2<br />
exit 1<br />
fi</p>
<p>if [ ! -w . ] ; then<br />
echo &#8220;${progname}: cannot write archive file to current directory.&#8221; &gt;&amp;2<br />
exit 1<br />
fi</p>
<p>dirsize=&#8221;$(du -s $1 | awk &#8216;{print $1}&#8217;)&#8221;</p>
<p>if [ $dirsize -gt $maxarchivedir ] ; then<br />
echo -n &#8220;Warning: directory $1 is $dirsize blocks. Proceed? [n] &#8221;<br />
read answer<br />
answer=&#8221;$(echo $answer | tr &#8216;[:upper:]&#8216; &#8216;[:lower:]&#8216; | cut -c1)&#8221;<br />
if [ "$answer" != "y" ] ; then<br />
echo &#8220;${progname}: archive of directory $1 canceled.&#8221; &gt;&amp;2<br />
exit 0<br />
fi<br />
fi<br />
archivename=&#8221;$(echo $1 | sed &#8217;s/$/.tgz/&#8217;)&#8221;</p>
<p>if tar cf &#8211; $1 | $compress &gt; $archivename ; then<br />
echo &#8220;Directory $1 archived as $archivename&#8221;<br />
else<br />
echo &#8220;Warning: tar encountered errors archiving $1&#8243;<br />
fi</p>
<p>exit 0</p></blockquote>
<p>This script is almost all error-checking code, to ensure that it never causes a loss of data or creates an incorrect snapshot. In addition to the typical tests to validate the presence and appropriateness of the starting argument, this script also forces the user to be in the parent directory of the subdirectory to be compressed and archived, which ensures that the archive file is saved in the proper place upon completion. The conditional if [ ! -w . ] ; then verifies that the user has write permission on the current directory. And this script even warns users before archiving if the resultant backup file would be unusually large.</p>
<p>Finally, the actual command that archives the specified directory is</p>
<blockquote><p>tar cf &#8211; $1 | $compress &gt; $archivename</p></blockquote>
<p>The return code of this command is tested to ensure that the script never deletes the directory if an error of any sort occurs.</p>
<p>This script should be invoked with the name of the desired directory to archive as its only argument. To ensure that the script doesn&#8217;t try to archive itself, it requires that a subdirectory of the current directory be specified as the argument, rather than &#8220;.&#8221;.</p>
<blockquote><p>$ archivedir scripts<br />
Warning: directory scripts is 2224 blocks. Proceed? [n] n<br />
archivedir: archive of directory scripts canceled.</p></blockquote>
<p>This seemed as though it might be a big archive, so I hesitated to create it, but thinking about it, there&#8217;s no reason not to proceed after all:</p>
<blockquote><p>$ archivedir scripts<br />
Warning: directory scripts is 2224 blocks. Proceed? [n] y<br />
Directory scripts archived as scripts.tgz</p></blockquote>
<p>The results:</p>
<blockquote><p>$ ls -l scripts.tgz<br />
-rw-r&#8211;r&#8211;  1 taylor  staff  325648 Jul 14 08:01 scripts.tgz</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.xiitec.com/blog/2008/02/04/backing-up-directories/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Figuring Out Available Disk Space</title>
		<link>http://www.xiitec.com/blog/2008/02/04/figuring-out-available-disk-space/</link>
		<comments>http://www.xiitec.com/blog/2008/02/04/figuring-out-available-disk-space/#comments</comments>
		<pubDate>Mon, 04 Feb 2008 19:17:06 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Shell scripts]]></category>
		<category><![CDATA[disk space]]></category>

		<guid isPermaLink="false">http://www.xiitec.com/blog/?p=93</guid>
		<description><![CDATA[Related to disk quota management is the simpler question of how much disk space is available on the system. The df command reports disk usage on a per-disk basis, but the output can be a bit baffling:
$ df
Filesystem          1K-blocks      Used Available [...]]]></description>
			<content:encoded><![CDATA[<p>Related to disk quota management is the simpler question of how much disk space is available on the system. The df command reports disk usage on a per-disk basis, but the output can be a bit baffling:</p>
<blockquote><p>$ df<br />
Filesystem          1K-blocks      Used Available Use% Mounted on<br />
/dev/hdb2            25695892   1871048  22519564   8% /<br />
/dev/hdb1              101089      6218     89652   7% /boot<br />
none                   127744         0    127744   0% /dev/shm</p></blockquote>
<p>What would be much more useful is a version of df that summarizes the available capacity values in column four and then presents the summary in a way that is easily understood. It&#8217;s a task easily accomplished in a script.</p>
<p><span id="more-93"></span></p>
<blockquote><p>#!/bin/sh</p>
<p># diskspace &#8211; Summarizes available disk space and presents it in a logical<br />
#    and readable fashion.</p>
<p>tempfile=&#8221;/tmp/available.$$&#8221;<br />
trap &#8220;rm -f $tempfile&#8221; EXIT</p>
<p>cat &lt;&lt; &#8216;EOF&#8217; &gt; $tempfile<br />
{ sum += $4 }<br />
END { mb = sum / 1024<br />
gb = mb / 1024<br />
printf &#8220;%.0f MB (%.2fGB) of available disk space\n&#8221;, mb, gb<br />
}<br />
EOF</p>
<p>df -k | awk -f $tempfile</p>
<p>exit 0</p></blockquote>
<p>This script can be run as any user and produces a succinct one-line summary of available disk space.</p>
<p>On the same system on which the df output shown earlier was generated, the script reports the following:</p>
<blockquote><p>$ diskspace<br />
96199 MB (93.94GB) of available disk space</p></blockquote>
<p>Another issue to consider is whether it&#8217;s more useful to know about the available disk space on all devices, including those partitions that cannot grow (like /boot), or whether reporting on user volumes is sufficient. If the latter is the case, you can improve this script by making a call to grep immediately after the df call. Use grep with the desired device names to include only particular devices, or use grep -v followed by the unwanted device names to screen out devices you don&#8217;t want included.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.xiitec.com/blog/2008/02/04/figuring-out-available-disk-space/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Reporting Disk Hogs</title>
		<link>http://www.xiitec.com/blog/2008/02/04/reporting-disk-hogs/</link>
		<comments>http://www.xiitec.com/blog/2008/02/04/reporting-disk-hogs/#comments</comments>
		<pubDate>Mon, 04 Feb 2008 19:12:54 +0000</pubDate>
		<dc:creator>admin</dc:creator>
				<category><![CDATA[Shell scripts]]></category>
		<category><![CDATA[disk hogs]]></category>

		<guid isPermaLink="false">http://www.xiitec.com/blog/?p=92</guid>
		<description><![CDATA[#!/bin/sh
# diskhogs &#8211; Disk quota analysis tool for Unix; assumes all user
#   accounts are &#62;= UID 100. Emails message to each violating user
#   and reports a summary to the screen.MAXDISKUSAGE=20
violators=&#8221;/tmp/diskhogs0.$$&#8221;
trap &#8220;/bin/rm -f $violators&#8221; 0
for name in $(cut -d: -f1,3 /etc/passwd &#124; awk -F: &#8216;$2 &#62; 99 { print $1 }&#8217;)
do
echo -n [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>#!/bin/sh<br />
# diskhogs &#8211; Disk quota analysis tool for Unix; assumes all user<br />
#   accounts are &gt;= UID 100. Emails message to each violating user<br />
#   and reports a summary to the screen.MAXDISKUSAGE=20<br />
violators=&#8221;/tmp/diskhogs0.$$&#8221;</p>
<p>trap &#8220;/bin/rm -f $violators&#8221; 0</p>
<p>for name in $(cut -d: -f1,3 /etc/passwd | awk -F: &#8216;$2 &gt; 99 { print $1 }&#8217;)<br />
do<br />
echo -n &#8220;$name &#8221;<br />
# You might need to modify the following list of directories to match<br />
# the layout of your disk. Most likely change: /Users to /home<br />
find / /usr /var /Users -user $name -xdev -type f -ls | \<br />
awk &#8216;{ sum += $7 } END { print sum / (1024*1024) }&#8217;</p>
<p>done | awk &#8220;\$2 &gt; $MAXDISKUSAGE { print \$0 }&#8221; &gt; $violators</p>
<p>if [ ! -s $violators ] ; then<br />
echo &#8220;No users exceed the disk quota of ${MAXDISKUSAGE}MB&#8221;<br />
cat $violators<br />
exit 0<br />
fi</p>
<p>while read account usage ; do</p>
<p>cat &lt;&lt; EOF | fmt | mail -s &#8220;Warning: $account Exceeds Quota&#8221; $account<br />
Your disk usage is ${usage}MB, but you have been allocated only<br />
${MAXDISKUSAGE}MB.  This means that you need to either delete some of<br />
your files, compress your files (see &#8216;gzip&#8217; or &#8216;bzip2&#8242; for powerful and<br />
easy-to-use compression programs), or talk with us about increasing<br />
your disk allocation.</p>
<p>Thanks for your cooperation in this matter.</p>
<p>Dave Taylor @ x554<br />
EOF<br />
echo &#8220;Account $account has $usage MB of disk space. User notified.&#8221;</p>
<p>done &lt; $violators</p>
<p>exit 0</p></blockquote>
<p>This script has no starting arguments and should be run as root for accurate results. This can most safely be accomplished by using the sudo command.</p>
<blockquote><p>$ sudo diskhogs<br />
Account linda has 39.7 MB of disk space. User notified.<br />
Account taylor has 21799.5 MB of disk space. User notified.</p></blockquote>
<p>If we now peek into the linda account mailbox, we&#8217;ll see that a message from the script has been delivered:</p>
<blockquote><p>Subject: Warning: linda Exceeds Quota</p>
<p>Your disk usage is 39.7MB, but you have been allocated only 20MB.  This means<br />
that you need to either delete some of your files, compress your files (see<br />
&#8216;gzip&#8217; or &#8216;bzip2&#8242; for powerful and easy-to-use compression programs), or talk<br />
with us about increasing your disk allocation.</p>
<p>Thanks for your cooperation on this matter.</p>
<p>Dave Taylor @ x554</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://www.xiitec.com/blog/2008/02/04/reporting-disk-hogs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
