Showing posts with label local. Show all posts
Showing posts with label local. Show all posts

Tuesday, November 25, 2008

Quick And Easy Local Filesystem Troubleshooting For SUSE Linux

Hey There,

Today we're going to take a look at some quick and easy ways to determine if you have a problem with your local filesystem on SUSE Linux (tested on 8.x and 9.x). Of course, we're assuming that you have some sort of an i/o wait issue and the users are blaming it on the local disk. While it's not always the case (i/o wait can occur because of CPU, memory and even network latency), it never hurts to be able to put out a fire when you need to. And, when the mob's pounding on your door with lit torches, that analogue is never more appropriate ;)

Just as in previous "quick troubleshooting" posts, like the week before last's on basic VCS troubleshooting, we'll be running through this with quick bullets. This won't be too in-depth, but it should cover the basics.

1. Figure out where you are and what OS you're on:

Generally, something as simple as:

host # uname -a

will get you the info you need. For instance, with SUSE Linux (and most others), you'll get output like:

Linux “hostname” kernel-version blah..blah Date Architecture... yada yada

The kernel version in that string is your best indicator. Generally, a kernel-version starting with 2.4.x will be fore SUSE 8.x and 2.6.x will be for SUSE 9.x. Of course, also avail yourselves of the, possibly available, /etc/release, /etc/issue, /etc/motd, /etc/issue.net files and others like them. It's important that you know what you're working with when you get started. Even if it doesn't matter now, it might later :)

2. Figure out how many local disks and/or volume groups you have active and running on your system:

Determine your server model, number of disks and volume groups. Since you're on SUSE, you may as well use the "hwinfo" command. I never know how much I'm going to need to know about the system when I first tackle a problem, so I'll generally dump it all into a file and then extract it from there as needed. See our really old post for a script that Lists out hardware information on SUSE Linux in a more pleasant format:

host # hwinfo >/var/tmp/hwinfo.out
host # grep system.product /var/tmp/hwinfo.out
system.product = 'ProLiant DL380 G4'


Now, I know what I'm working with. If this specific of a grep doesn't work for you, try "grep -i product" - you'll get a lot more information than you need, but your machine's model and number will be in there and much easier to find than if you looked through the entire output file.

Then, go ahead and check out /proc/partitions. This will give you the layout of your disk:

host # /proc # cat /proc/partitions
major minor #blocks name

104 0 35561280 cciss/c0d0
104 1 265041 cciss/c0d0p1
104 2 35294805 cciss/c0d0p2
104 16 35561280 cciss/c0d1
104 17 35559846 cciss/c0d1p1
253 0 6291456 dm-0
253 1 6291456 dm-1
253 2 2097152 dm-2
253 3 6291456 dm-3
253 4 10485760 dm-4
253 5 3145728 dm-5
253 6 2097152 dm-6



"cciss/c0d0" and "cciss/c0d1" show you that you have two disks (most probably mirrored, which we can infer from the dm-x output). Depending upon how your local disk is managed, you may see lines that indicate, clearly, that LVM is being used to manage the disk (because the lines contain hints like "lvma," "lvmb" and so forth ;)

58 0 6291456 lvma 0 0 0 0 0 0 0 0 0 0 0
58 1 6291456 lvmb 0 0 0 0 0 0 0 0 0 0 0


3. Check out your local filesystems and fix anything you find that's broken:

Although it's boring, and manual, it's a good idea do take the output of:

host # df -l

and compare that with the contents of your /etc/fstab. This will clear up any obvious errors like mounts that are supposed to be up but aren't or mounts that aren't supposed to up that are, etc... You can whittle down your output from /etc/fstab to show (mostly) only local filesystems by doing a reverse grep on the colon character (:) - This is generally found in remote mounts and almost never found in local filesystem listings.

host # grep -v ":" /etc/fstab

4. Keep hammering away at the obvious:

Check the USED% column in the output of your "df -l" command. If any filesystems are at 100%, some cleanup is in order. It may seem silly, but usually the simplest problems get missed when one too many managers begin breathing down your neck ;) Also, check the inodes column and ensure that those aren't all being used up either.

Mount any filesystems that are supposed to be mounted but aren't, and unmount any filesystems that are mounted but (according to /etc/fstab) shouldn't be). Someone will complain about the latter at some point (almost guaranteed), which will put you in a perfect position to request that it either be put in the /etc/fstab file or not mounted at all.

You're most likely to have an issue here with mounting the unmounted filesystem that's supposed to be mounted. If you try to mount and get an error that indicates the mountpoint can't be found in /etc/fstab or /etc/mnttab, the mount probably isn't listed in /etc/fstab or there is an issue with the syntax of that particular line (could even be a "ghost" control character). You should also check to make sure the mount point being referenced actually exists, although you should get an entirely different (and very self-explanatory) error message in the event that you have that problem.

If you still can't mount, after correcting any of these errors (of course, you could always avoid the previous step and mount from the command line using logical device names instead of paths from /etc/vfstab, but it's always nice to know that what you fix will probably stay fixed for a while ;), you may need to "fix" the disk. This will range in complexity from the very simple to the moderately un-simple ;) The simple (Note: If you're running ReiserFS, use reiserfsck instead of plain fsck for all the following examples. I'm just trying to save myself some typing):

host # umount /uselessFileSystem
host # fsck -y /uselessFileSystem
....
host # mount /


which, you may note, would be impossible to do (or, I should say, I'd highly recommend you DON'T do) on used-and-mounted filesystems or any special filesystems, like root "/" - In cases like that, if you need to fsck the filesystem, you should optimally do it when booted up off of a cdrom or, at the very least, in single user mode (although you still run a risk if you run fsck against a mounted root filesystem).

For the moderately un-simple, we'll assume a "managed file system," like one under LVM control. In this case you could check a volume that refuses to mount (assuming you tried most of the other stuff above and it didn't do you any good) by first scanning all of them (just in case):

host # vgscan
Reading all physical volumes. This may take a while...
Found volume group "usvol" using metadata type lvm2
Found volume group "themvol" using metadata type lvm2


If "usvol" (or any of them) is showing up as inactive, or is completely missing from your output, you can try the following:

host # vgchange –a y

to use the brute-force method of trying to activate all volume groups that are either missing or inactive. If this command gives you errors, or it doesn't and vgscan still gives you errors, you most likely have a hardware related problem. Time to walk over to the server room and check out the situation more closely. Look for amber lights on most servers. I've yet to work on one where "green" meant trouble ;)

If doing the above sorts you out and fixes you up, you just need to scan for logical volumes within the volume group, like so:

host # lvscan
ACTIVE '/dev/usvol/usfs02' [32.00 GB] inherit
....


And (is this starting to sound familiar or am I just repeating myself ;), if this gives you errors, try:

host # lvchange –a y

If the logical volume throws you into an error loop, or it doesn't complain but a repeated run of "lvscan" fails, you've got a problem outside the scope of this post. But, at least you know pretty much where it is!

If you manage to make it through the logical volume scan, and everything seems okay, you just need to remount the filesystem as you normally would. Of course, that could also fail... (Does the misery never end? ;)

At that point, give fsck (or reiserfsck) another shot and, if it doesn't do any good, you'll have to dig deeper and look at possible filesystem corruption so awful you may as well restore the server from a backup or server image (ghost).

And, that's that! Hopefully it wasn't too much or too little, and helps you out in one way or another :)

Cheers,

, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Monday, November 24, 2008

Cabletv.sh For Linux or Unix - Final Blog Update

Hey There,

IMPORTANT NOTE: As noted in the title, the poll we put up last week showed that a majority of folks who participated in the blog's poll (72%) would rather that this script's development be moved to my SourceForge page and become a separate project which would mean that we won't be doing any more follow-up posts to this one. I must say that I whole-heartedly agree. The first benefit will be that we won't be continually putting up the same material every Monday. The second, and perhaps even better, benefit will be that I'll be able to work on the script on an entirely different schedule. Once it's freed from the daily-post deadline, I'll be able to completely overhaul it and make it much better than it is (and a whole lot more quickly). I want everyone who's written in with suggestions for improvement to know that I've read all your emails (so far) and am very appreciative of your constructive criticism and creative approaches toward making the script into a fuller and more robust "program." I apologize if I haven't gotten back to you yet (for those of you whom I haven't gotten back to ;), but I promise you that I will. BTW (This is how goofy I am), I just realized, as I was doing some backtracking, that I've posted an entry every day since October 23rd 2006. It's a miracle I still have a job and the love of my family. I'm just wondering how I manage to spend time with them... oh, yeah. I don't sleep ;)

LAST NOTE ON THIS: At this point, I'll need to setup my SourceForge page to include the project and, as soon as it's up, I'll be sure to put a link to it on the blog (In the upper right hand corner, with the policies and "send me a comment" links). I'll probably note it in that day's blog listing, too, but you'll know it's ready as soon as you see that link appear. It should be up sometime this week.

For this week's Monday Linux/Unix bash shell script continuation, we're finishing up the blog version of our cabletv.sh script from last week. If you liked that one, and want to check out other versions, please revisit last week's cabletv.sh script, and that page will link back to all the other versions of the script hosted on this blog. If you can see the Blogger search bar (wrapped around the site and not really part of it), searching for "cable tv" should bring up all of them on one or two pages. As usual, skip the next paragraph if you've read it before. It lists out all our other "bringing the web to your CLI" scripts we've cranked out to date. Starting next week, there will finally be more original content!

Our previous web-based Bash scripts, in backward chronological order, include our posts on accessing Wikipedia, accessing the Farmer's Almanac, accessing the International Dictionary, checking out the world's weather, spewing out famous quotations on pretty much any subject, doing encyclopedia lookups, accessing the online Thesaurus, translating between different languages and, of course, using the online dictionary.

This week's improvements are limited to one (I guess that would just make it a singular "improvement" ;). I received several suggestions about reliance on awk's strftime, which helped us get over the in-sequential time problem, but added some minutes to the output. For the record, this fix isn't included in this version, as problems with formatting need to be completely reworked, but, to answer the question, the quicker way to do this without awk is with Gnu date (and it "must" be Gnu date, as, for instance, Solaris' version of date doesn't allow for the same type of formatting). So, in a version I'm not putting up here because it isn't ready yet, we've changed:

strftime("%x - %I:%M %p", TheTimeFromZap2It/1000)) }'

to:

date -d @$((TheTimeFromZap2It/1000)) "+%x - %I:%M %p"

which is much faster!

Our only improvement for this week's sendoff is that we've switched from using wget to using straight-up telnet, since more people have telnet on their machines (by default) than wget (If you recall, the original reason we went with wget was because we didn't want folks to have to run lynx and/or html2text for no reason). This also results in a slight performance improvement (about 30 seconds), but I'll take it :) So, our URL calls that used to look like this:

wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}"

are now set up this way, using telnet to port 80 and passing an HTTP/1.1 request (Zap2it bumps you if you use HTTP 1.0!), like so:

(echo "GET /tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80

It seems more convoluted (and, technically, I suppose it is), but it works faster. I'm assuming because telnet is so ingrained in Linux and Unix and it saves us the time of loading up another external program that (although it may be "better suited" to the task) can do much more than we need it to and comes with a bit of extra baggage, as would be expected.

Until next time. I look forward to writing something new for you :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# cabletv.sh - Get your local regular and HD Tv listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# TODO switch to enhanced getopt to deal with variable argument switches
# TODO add config file
# TODO check for bad provider code
# TODO rewrite unix to "pretty" time conversion to speed up returns
# ------------------------------
# DONE switch "wget" to "telnet" for more accessibility and a slight speedup
# DONE End Display Time For Previous day when start time is in zero hour
# DONE check for bad zip code
# DONE added optional time increments - up to 3 hours - for display
# DONE tightened up code using indirect variable references
# DONE date may now be entered in quotes numeric or alpha - e.g. 11/9/08 11/09/2008 "Nov 9 2008" "November 9th 2008" etc
# DONE time may now be entered as 10pm 10 (with am/pm defaulting to system time) 10:00am 23:00 etc
# DONE added error checking for invalid date and time entry - stop before querying online guide
# DONE fixed issue with 00:xx time causing script failure
# DONE added extra error checking
# DONE added better formatting of lineup for easier readability - Thanks to Michael Seeley for the suggestion!
# DONE You have entered an invalid ZIP or postal code
# DONE am to pm or pm to am ends up not in chronological order
# DONE 12 to 1 a/p ends up not in chronological order
#

function usage()
{
echo
echo "Usage: $0 ...all options are optional [-h for help]"
echo "[-z USZipCode] [-p LocalCableOrDishProviderID]"
echo "[-t Time] [-d date] [-n to leave out HD channel info]"
echo "[-s ListingTimeSpan 1 for just now, 2 for an additional half"
echo "hour, 3, 4, 5, 6, in half hour increments, and 7 for 3 hours"
echo "...defaults to 7 since this is the online listing default]"
echo
exit 1
}

while getopts t:z:hs:nd:p: option
do
case $option in
t)
opt_time="$OPTARG"
;;
d)
opt_date="$OPTARG"
;;
z)
opt_zip="$OPTARG"
;;
n)
opt_nohd=1
;;
p)
opt_provider="$OPTARG"
;;
s)
opt_timespan="$OPTARG"
;;
h)
usage
;;
*)
usage
;;
esac
done

## FEEL FREE TO EDIT HERE - If the path's are wrong, or you use less, rather than more, you can change that here.
telnet=/usr/bin/telnet
pager=/usr/bin/more
## OK, THAT'S ENOUGH OF THAT ;)

if [ ! "$opt_date" ]
then
nicedate=`date "+%m/%d/%y"`
else
nicedate=$opt_date
fi

if [ ! $opt_timespan ]
then
opt_timespan=7
elif [ $opt_timespan -lt 1 -o $opt_timespan -gt 7 ]
then
usage
fi

if [ ! $opt_time ]
then
hour=`date "+%I"`
minute=`date "+%M"`
ampm=`date "+%p"`
if [ $hour -lt 12 ]
then
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 AM"
else
nicetime="${hour}:30 AM"
fi
else
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi
if [ $hour == "00" ]
then
cli_time=${hour}:${minute}
else
cli_time=${hour}:${minute}${ampm}
fi
else
cli_time=$opt_time
fi

date_ok=`date -d "$cli_time" +%s 2>&1`
if [[ "$date_ok" =~ "invalid date" ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi

ampm=`echo $opt_time|egrep 'a|p|m' 2>&1`
result2=$?
if [ $result2 -eq 0 ]
then
date_ok2=`echo $opt_time|egrep 'a|p'|grep m 2>&1`
result3=$?
if [[ $result3 -ne 0 ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi
fi

cli_time2=`date -d "$cli_time" +%s`
let fromtime1=$cli_time2*1000
let fromtime2=$fromtime1+1800000
let fromtime3=$fromtime2+1800000
let fromtime4=$fromtime3+1800000
let fromtime5=$fromtime4+1800000
let fromtime6=$fromtime5+1800000
let fromtime7=$fromtime6+1800000
if [ ! $opt_timespan ]
then
opt_timespan=7
fi

timespan_relay="$fromtime1"
timespan_counter=2
timespan_limit=$opt_timespan

while [ $timespan_limit -gt 1 ]
do
nextup_time=$(eval "echo \$$(echo fromtime${timespan_counter})")
timespan_relay="$timespan_relay $nextup_time"
let timespan_counter=$timespan_counter+1
let timespan_limit=$timespan_limit-1
done

from_timespan=$(eval "echo \$$(echo fromtime${opt_timespan})")
cli_time_end=`echo $from_timespan|awk '{printf("%20s",strftime("%I:%M %p - %x", $0 / 1000))}'`

if [ ! $opt_nohd ]
then
opt_nohd=0
fi

if [ ! $opt_zip ]
then
echo -n "Enter Zip Code: "
read zip_code
else
zip_code=$opt_zip
fi

(echo "GET /tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 2>&1|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' 2>&1|grep "You have entered an invalid ZIP or postal code" >/dev/null 2>&1
zip_ok=$?

if [ $zip_ok -eq 0 ]
then
echo
echo "INVALID ZIP CODE ENTERED: $opt_zip"
echo
usage
fi

if [ ! $opt_provider ]
then
(echo "GET /tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 2>&1|sed 's/<.*&lineupId=\([^&]*\)\">/\1 \c/'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Choose Your Provider/{x;$!d;x;q;};x;$G' -e '/document.write/,$d' -e "s/'/'/" -e 's/&/\&/' -e 's/^[ \t]*//;s/[ \t]*$//'|sed '/\([A-Za-z0-9_][A-Za-z0-9_]*:[-X]\)/ {
N
s/ *\n/ \t/g
}'
echo "Enter Provider ID [e.g. \"IL57303:-\" \"4DTV:-\" \"IL12561:X]\""
read tv_provider
else
tv_provider=$opt_provider
fi

echo
echo "Television Listings for $nicedate from $cli_time to $cli_time_end"
echo

last=1

if [ $opt_nohd -eq 1 ]
then
for x in $timespan_relay
do
(echo "GET /tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*$/,+2p'|sed 's/^\([0-9][0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("*** Start - %s", $3);print ""}}'
done|sort -t'-' -k1,1n -k2,2n | uniq|while read one two;do if [ $one -eq $last ]; then echo "$one $two"|awk '{aline=$NF;$NF="";printf("%s %s\n", $0, strftime("%x - %I:%M %p", aline/1000)) }'|while read one two three;do echo " $three";done;last=$one;else echo "$one $two"|awk '{aline=$NF;$NF="";printf("%s %s\n", $0, strftime("%x - %I:%M %p", aline/1000))}'|while read one two three;do echo " $one $two";echo " $three";done;last=$one;fi;done|$pager
else
for x in $timespan_relay
do
(echo "GET /tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider} HTTP/1.1";echo "Host: tvlistings.zap2it.com";echo;sleep 2)|telnet tvlistings.zap2it.com 80 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*\.*[0-9]*$/,+2p'|sed -e 's/^\([0-9][0-9]*\.*[0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*\.*[0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("*** Start - %s", $3);print ""}}'
done|sort -t'-' -k1,1n -k2,2n | uniq|while read one two three;do three_one=`echo "$three"|sed -e 's/.*\*\*\* Start - //' -e 's/^[ \t]*//;s/[ \t]*$//'`; let three_two=$three_one/1000; three_three=`echo "$three"|sed 's/^\(.*\*\*\* Start - \).*/\1/'`;three_four=`echo $three_two|awk '{printf("%s", strftime("%x - %I:%M %p", $0)) }'`;if [ $one -eq $last ];then echo " $three_three $three_four";last=$one;else echo "$one $two";echo " $three_three $three_four";last=$one;fi;done|$pager
fi

exit 0


, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Monday, November 17, 2008

Bash Cable, Dish and Local TV Listings Script For Linux Or Unix

Hey There,

IMPORTANT NOTE: I'm beginning to think that this script should be put on my SourceForge page and maintained there. It's not that I mind doing these weekly updates, but I feel like I'm writing the same thing over and over again, where I could be offering you, the reader, more value by producing different scripts in its place. If you have a few seconds, please answer the short poll on the right hand side of the blog. If public opinion wants this project worked out on the blog, then so be it. If you're all getting sick of these updates, I can move this to my SourceForge page, as a project, and produce more original content for our Monday script posts.

For this week's Monday Linux/Unix bash shell script continuation, we're following up on our script from last week. If you liked that one, please revisit last week's cabletv.sh script, as this one introduces some features which (while nice) introduce a bit of extra run time. This week's improvements include making the script output more readable (Thanks to Michael Seeley for this suggestion) and, finally, fixing the am/pm (or 12 to 1) hump that would throw the results out of chronological order. As usual, please skip the following paragraph, unless you're interested in all the other Monday scripts we've cranked out to date.

Our previous web-based Bash scripts, in backward chronological order, include our posts on accessing Wikipedia, accessing the Farmer's Almanac, accessing the International Dictionary, checking out the world's weather, spewing out famous quotations on pretty much any subject, doing encyclopedia lookups, accessing the online Thesaurus, translating between different languages and, of course, using the online dictionary.

This week's improvements don't involve any actual extra functionality (as the, still in progress, configuration file loading and creation process will), but they do make things look better and read more sensibly, although this comes at a cost (see below).

The first improvement is in the readability of the script. Now, when you get your results, you'll be able to easily differentiate the channels from the program listings. The picture below shows a comparison of the older version (at the top) and the updated version (at the bottom):

Click on the picture below and be sure to have on your 3D specs ready ;)

old and new listings displays

The other improvements gets us over quiet a few errors involving the zero hour, incorrect times being auto-generated and ensuring correct chronological sorting of listings when crossing from 12 to 1 (am/pm or pm/am):

Click on the pictures below and enjoy the sensible passage of time ;)

chronological tv listings

Of course, getting the listings in chronological order has imposed a stiff penalty (although I'm sure it's because of the way in which I implemented it). Leaving everything in Unix time actually speeds up the sort process, but then running awk's strftime() call on each entry as it's printed has (to my way of thinking) disastrous repercussions. Of course, I'll be spending some time working this out. If you run "time" against the old version of the script (assuming my lineup with approximately 8 or 900 channels), the old version ran at this speed to produce all results in the maximum time frame (3 hours):

real 0m27.846s
user 0m6.835s
sys 0m4.725s


With all the extra frills, that number (for the exact same output - although with the fixes in place), runs at this pace:

real 3m30.538s
user 0m44.129s
sys 1m17.421s


a full 3 minutes longer! I've narrowed it down to the repetitive strftime() calls, and you can see that most of the processing time is in "sys," which means I'm forcing my kernel to work harder than it should. The implementation of date conversion in awk is the actual issue, and I'll need to convert that to simpler time-conversion via the Gnu date command, although this causes some issues with formatting that need to be worked out. I figured you'd want something that worked (if even more slowly) at this point, rather than something that ran fast and spewed garbage to your terminal.

And here's something that might make you feel better about the 3 minutes. Check out the time it takes to do every line using sed, grep and date without messing with the basic structure of the output:

real 19m31.859s
user 3m48.153s
sys 7m44.685s


Mamma mia! ;)

Until next time (possibly as a project on my SourceForge page :)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# cabletv.sh - Get your local regular and HD Tv listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# TODO switch to enhanced getopt to deal with variable argument switches
# TODO add config file
# TODO check for bad provider code
# TODO rewrite unix to "pretty" time conversion to speed up returns
# ------------------------------
# DONE End Display Time For Previous day when start time is in zero hour
# DONE check for bad zip code
# DONE added optional time increments - up to 3 hours - for display
# DONE tightened up code using indirect variable references
# DONE date may now be entered in quotes numeric or alpha - e.g. 11/9/08 11/09/2008 "Nov 9 2008" "November 9th 2008" etc
# DONE time may now be entered as 10pm 10 (with am/pm defaulting to system time) 10:00am 23:00 etc
# DONE added error checking for invalid date and time entry - stop before querying online guide
# DONE fixed issue with 00:xx time causing script failure
# DONE added extra error checking
# DONE added better formatting of lineup for easier readability - Thanks to Michael Seeley for the suggestion!
# DONE You have entered an invalid ZIP or postal code
# DONE am to pm or pm to am ends up not in chronological order
# DONE 12 to 1 a/p ends up not in chronological order
#

function usage()
{
echo
echo "Usage: $0 ...all options are optional [-h for help]"
echo "[-z USZipCode] [-p LocalCableOrDishProviderID]"
echo "[-t Time] [-d date] [-n to leave out HD channel info]"
echo "[-s ListingTimeSpan 1 for just now, 2 for an additional half"
echo "hour, 3, 4, 5, 6, in half hour increments, and 7 for 3 hours"
echo "...defaults to 7 since this is the online listing default]"
echo
exit 1
}

while getopts t:z:hs:nd:p: option
do
case $option in
t)
opt_time="$OPTARG"
;;
d)
opt_date="$OPTARG"
;;
z)
opt_zip="$OPTARG"
;;
n)
opt_nohd=1
;;
p)
opt_provider="$OPTARG"
;;
s)
opt_timespan="$OPTARG"
;;
h)
usage
;;
*)
usage
;;
esac
done

wget=/usr/bin/wget
pager=/usr/bin/more

if [ ! "$opt_date" ]
then
nicedate=`date "+%m/%d/%y"`
else
nicedate=$opt_date
fi

if [ ! $opt_timespan ]
then
opt_timespan=7
elif [ $opt_timespan -lt 1 -o $opt_timespan -gt 7 ]
then
usage
fi

if [ ! $opt_time ]
then
hour=`date "+%I"`
minute=`date "+%M"`
ampm=`date "+%p"`
if [ $hour -lt 12 ]
then
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 AM"
else
nicetime="${hour}:30 AM"
fi
else
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi
if [ $hour == "00" ]
then
cli_time=${hour}:${minute}
else
cli_time=${hour}:${minute}${ampm}
fi
else
cli_time=$opt_time
fi

date_ok=`date -d "$cli_time" +%s 2>&1`
if [[ "$date_ok" =~ "invalid date" ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi

ampm=`echo $opt_time|egrep 'a|p|m' 2>&1`
result2=$?
if [ $result2 -eq 0 ]
then
date_ok2=`echo $opt_time|egrep 'a|p'|grep m 2>&1`
result3=$?
if [[ $result3 -ne 0 ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi
fi

cli_time2=`date -d "$cli_time" +%s`
let fromtime1=$cli_time2*1000
let fromtime2=$fromtime1+1800000
let fromtime3=$fromtime2+1800000
let fromtime4=$fromtime3+1800000
let fromtime5=$fromtime4+1800000
let fromtime6=$fromtime5+1800000
let fromtime7=$fromtime6+1800000
if [ ! $opt_timespan ]
then
opt_timespan=7
fi

timespan_relay="$fromtime1"
timespan_counter=2
timespan_limit=$opt_timespan

while [ $timespan_limit -gt 1 ]
do
nextup_time=$(eval "echo \$$(echo fromtime${timespan_counter})")
timespan_relay="$timespan_relay $nextup_time"
let timespan_counter=$timespan_counter+1
let timespan_limit=$timespan_limit-1
done

from_timespan=$(eval "echo \$$(echo fromtime${opt_timespan})")
cli_time_end=`echo $from_timespan|awk '{printf("%20s",strftime("%I:%M %p - %x", $0 / 1000))}'`

if [ ! $opt_nohd ]
then
opt_nohd=0
fi

if [ ! $opt_zip ]
then
echo -n "Enter Zip Code: "
read zip_code
else
zip_code=$opt_zip
fi

$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}" 2>&1|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' 2>&1|grep "You have entered an invalid ZIP or postal code" >/dev/null 2>&1
zip_ok=$?

if [ $zip_ok -eq 0 ]
then
echo
echo "INVALID ZIP CODE ENTERED: $opt_zip"
echo
usage
fi

if [ ! $opt_provider ]
then
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}" 2>&1|sed 's/<.*&lineupId=\([^&]*\)\">/\1 \c/'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Choose Your Provider/{x;$!d;x;q;};x;$G' -e '/document.write/,$d' -e "s/'/'/" -e 's/&/\&/' -e 's/^[ \t]*//;s/[ \t]*$//'|sed '/\([A-Za-z0-9_][A-Za-z0-9_]*:[-X]\)/ {
N
s/ *\n/ \t/g
}'
echo "Enter Provider ID [e.g. \"IL57303:-\" \"4DTV:-\" \"IL12561:X]\""
read tv_provider
else
tv_provider=$opt_provider
fi

echo
echo "Television Listings for $nicedate from $cli_time to $cli_time_end"
echo

last=1

if [ $opt_nohd -eq 1 ]
then
for x in $timespan_relay
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*$/,+2p'|sed 's/^\([0-9][0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("*** Start - %s", $3);print ""}}'
done|sort -t'-' -k1,1n -k2,2n | uniq|while read one two;do if [ $one -eq $last ]; then echo "$one $two"|awk '{aline=$NF;$NF="";printf("%s %s\n", $0, strftime("%x - %I:%M %p", aline/1000)) }'|while read one two three;do echo " $three";done;last=$one;else echo "$one $two"|awk '{aline=$NF;$NF="";printf("%s %s\n", $0, strftime("%x - %I:%M %p", aline/1000))}'|while read one two three;do echo " $one $two";echo " $three";done;last=$one;fi;done|$pager
else
for x in $timespan_relay
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*\.*[0-9]*$/,+2p'|sed -e 's/^\([0-9][0-9]*\.*[0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*\.*[0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("*** Start - %s", $3);print ""}}'
done|sort -t'-' -k1,1n -k2,2n | uniq|while read one two three;do three_one=`echo "$three"|sed -e 's/.*\*\*\* Start - //' -e 's/^[ \t]*//;s/[ \t]*$//'`; let three_two=$three_one/1000; three_three=`echo "$three"|sed 's/^\(.*\*\*\* Start - \).*/\1/'`;three_four=`echo $three_two|awk '{printf("%s", strftime("%x - %I:%M %p", $0)) }'`;if [ $one -eq $last ];then echo " $three_three $three_four";last=$one;else echo "$one $two";echo " $three_three $three_four";last=$one;fi;done|$pager
fi

exit 0


, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Monday, November 10, 2008

Updated Bash Script To Get Cable, Dish and Local TV Listings

Hey There,

For this week's Monday Linux/Unix bash shell script, we're following up on our script from last week, with more user suggested improvements, that makes it so you can get your local TV listings from the command line with bash. This week's script is an update to include a lot of error checking and (from the suggestion I received the most often :) the ability to pick a time span. This script's invocation will allow you to get listings from "right now" (or whatever you set the "time" variable to) to the regular 3 hour time span in half-hour increments. The next paragraph is a blatant homage (read: shameless self promotion) to "other scripts we've done before that follow along the lines of this one." Please skip it and move on to next paragraph unless you enjoy reading lists for the sake of list reading :)

Our previous web-based Bash scripts, in backward chronological order, include our posts on accessing Wikipedia, accessing the Farmer's Almanac, accessing the International Dictionary, checking out the world's weather, spewing out famous quotations on pretty much any subject, doing encyclopedia lookups, accessing the online Thesaurus, translating between different languages and, of course, using the online dictionary.

This script still gets its content from the TV Listings At Zap2It.com and includes a lot of error fixes. Most importantly, though, it allows you to pick your time slot (assuming you don't want the full 3 hour spread), shown in the usage message like this:

host # ./cabletv.sh -h

Usage: ./cabletv.sh ...all options are optional [-h for help]
[-z USZipCode] [-p LocalCableOrDishProviderID]
[-t Time] [-d date] [-n to leave out HD channel info]
[-s ListingTimeSpan 1 for just now, 2 for an additional half
hour, 3, 4, 5, 6, in half hour increments, and 7 for 3 hours
...defaults to 7 since this is the online listing default]


Much of the error checking was done so that the script would capture bad zip codes, invalid time specifications and make it so you can set your date in a more free manner (output below does not include actual listings to make the picture smaller):

Click on the picture below for a graphic depiction of some of the code fixes ;)

cable tv script errors

After that, check out the next few pictures below, which show successful executions and the beginning of the TV listings. The first picture shows the output from a "right now" execution and the other is from an "2 hour" execution. The default time span is 3 hours, since that's the way it is online:

Click on the pictures below and prepare for the mediocrity ;)

tv listings for right now

2 hours of tv listings

I'd like to thank everyone who wrote in with comments and suggestions (which I'll continue to post online, as possible). The script is starting to get better, but it's still far from perfect. Hopefully you find it more helpful now :)

I still need to put out those movie/favorites filters. Soon... Soon... ;)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# cabletv.sh - Get your local regular and HD Tv listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# UPDATE FOR 11/9/2008
# TODO You have entered an invalid ZIP or postal code
# TODO am to pm or pm to am ends up not in chronological order
# TODO 12 to 1 a/p ends up not in chronological order
# TODO switch to enhanced getopt to deal with variable argument switches
# TODO add config file
# TODO check for bad provider code
# DONE check for bad zip code
# DONE added optional time increments - up to 3 hours - for display
# DONE tightened up code using indirect variable references
# DONE date may now be entered in quotes numeric or alpha - e.g. 11/9/08 11/09/2008 "Nov 9 2008" "November 9th 2008" etc
# DONE time may now be entered as 10pm 10 (with am/pm defaulting to system time) 10:00am 23:00 etc
# DONE added error checking for invalid date and time entry - stop before querying online guide
# DONE fixed issue with 00:xx time causing script failure
# DONE added extra error checking
#

function usage()
{
echo
echo "Usage: $0 ...all options are optional [-h for help]"
echo "[-z USZipCode] [-p LocalCableOrDishProviderID]"
echo "[-t Time] [-d date] [-n to leave out HD channel info]"
echo "[-s ListingTimeSpan 1 for just now, 2 for an additional half"
echo "hour, 3, 4, 5, 6, in half hour increments, and 7 for 3 hours"
echo "...defaults to 7 since this is the online listing default]"
echo
exit 1
}

while getopts t:z:hs:nd:p: option
do
case $option in
t)
opt_time="$OPTARG"
;;
d)
opt_date="$OPTARG"
;;
z)
opt_zip="$OPTARG"
;;
n)
opt_nohd=1
;;
p)
opt_provider="$OPTARG"
;;
s)
opt_timespan="$OPTARG"
;;
h)
usage
;;
*)
usage
;;
esac
done

wget=/usr/bin/wget
pager=/usr/bin/more

if [ ! "$opt_date" ]
then
nicedate=`date "+%m/%d/%y"`
else
nicedate=$opt_date
fi

if [ ! $opt_timespan ]
then
opt_timespan=7
elif [ $opt_timespan -lt 1 -o $opt_timespan -gt 7 ]
then
usage
fi

if [ ! $opt_time ]
then
hour=`date "+%I"`
minute=`date "+%M"`
ampm=`date "+%p"`
if [ $hour -lt 12 ]
then
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 AM"
else
nicetime="${hour}:30 AM"
fi
else
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi
if [ $hour == "00" ]
then
cli_time=${hour}:${minute}
else
cli_time=${hour}:${minute}${ampm}
fi
else
cli_time=$opt_time
fi

date_ok=`date -d "$cli_time" +%s 2>&1`
if [[ "$date_ok" =~ "invalid date" ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi

ampm=`echo $opt_time|egrep 'a|p|m' 2>&1`
result2=$?
if [ $result2 -eq 0 ]
then
date_ok2=`echo $opt_time|egrep 'a|p'|grep m 2>&1`
result3=$?
if [[ $result3 -ne 0 ]]
then
echo
echo "INVALID TIME ENTERED: $opt_time"
echo "Be sure to include full am or pm"
echo "and do not add am or pm to 24 clock entries"
echo
usage
fi
fi

cli_time2=`date -d "$cli_time" +%s`
let fromtime1=$cli_time2*1000
let fromtime2=$fromtime1+1800000
let fromtime3=$fromtime2+1800000
let fromtime4=$fromtime3+1800000
let fromtime5=$fromtime4+1800000
let fromtime6=$fromtime5+1800000
let fromtime7=$fromtime6+1800000
if [ ! $opt_timespan ]
then
opt_timespan=7
fi

timespan_relay="$fromtime1"
timespan_counter=2
timespan_limit=$opt_timespan

while [ $timespan_limit -gt 1 ]
do
nextup_time=$(eval "echo \$$(echo fromtime${timespan_counter})")
timespan_relay="$timespan_relay $nextup_time"
let timespan_counter=$timespan_counter+1
let timespan_limit=$timespan_limit-1
done

from_timespan=$(eval "echo \$$(echo fromtime${opt_timespan})")
cli_time_end=`echo $from_timespan|awk '{printf("%20s",strftime("%I:%M %p - %x", $0 / 1000))}'`

if [ ! $opt_nohd ]
then
opt_nohd=0
fi

if [ ! $opt_zip ]
then
echo -n "Enter Zip Code: "
read zip_code
else
zip_code=$opt_zip
fi

$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}" 2>&1|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' 2>&1|grep "You have entered an invalid ZIP or postal code" >/dev/null 2>&1
zip_ok=$?

if [ $zip_ok -eq 0 ]
then
echo
echo "INVALID ZIP CODE ENTERED: $opt_zip"
echo
usage
fi

if [ ! $opt_provider ]
then
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}" 2>&1|sed 's/<.*&lineupId=\([^&]*\)\">/\1 \c/'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Choose Your Provider/{x;$!d;x;q;};x;$G' -e '/document.write/,$d' -e "s/'/'/" -e 's/&/\&/' -e 's/^[ \t]*//;s/[ \t]*$//'|sed '/\([A-Za-z0-9_][A-Za-z0-9_]*:[-X]\)/ {
N
s/ *\n/ \t/g
}'
echo "Enter Provider ID [e.g. \"IL57303:-\" \"4DTV:-\" \"IL12561:X]\""
read tv_provider
else
tv_provider=$opt_provider
fi

echo
echo "Television Listings for $nicedate from $cli_time to $cli_time_end"
echo

last=1

if [ $opt_nohd -eq 1 ]
then
for x in $timespan_relay
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*$/,+2p'|sed 's/^\([0-9][0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("%20s %-20s", "*** Start",strftime("%x - %I:%M %p",$3 / 1000));print ""}}'
done|sort -t'-' -k1,1n -k2,3n | uniq|while read one two three;do if [ $one -eq $last ];then echo " $three";last=$one;else echo "$one $two $three";last=$one;fi;done|$pager
else
for x in $timespan_relay
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*\.*[0-9]*$/,+2p'|sed -e 's/^\([0-9][0-9]*\.*[0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*\.*[0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("%20s %-20s", "*** Start",strftime("%x - %I:%M %p",$3 / 1000));print ""}}'
done|sort -t'-' -k1,1n -k2,3n | uniq|while read one two three;do if [ "$one" == "$last" ];then echo " $three";last=$one;else echo "$one $two $three";last=$one;fi;done|$pager
fi

exit 0


, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Monday, November 3, 2008

Get Cable, Dish and Local TV Listings Using Bash

Hey There,

For this week's Monday Linux/Unix bash shell script, we're following up on what turned out to be a fairly popular script from last week that made it so you could get your local tv listings from the command line with bash. This week's script is an update to include cable and dish TV as well as provide greater coverage of your personal provider's schedule. The next paragraph is another litany of "other scripts we've done before that somewhat resemble this one." Please skip it and move on to paragraph three if you're just interested in today's topic :)

Our previous web-based Bash scripts, in backward chronological order, include our posts on accessing Wikipedia, accessing the Farmer's Almanac, accessing the International Dictionary, checking out the world's weather, spewing out famous quotations on pretty much any subject, doing encyclopedia lookups, accessing the online Thesaurus, translating between different languages and, of course, using the online dictionary.

This script still gets its content from TV Listings At Zap2It.com and accepts a much greater number of arguments than our last attempt. In this version, you can run the entire script manually, like so:

host # ./cabletv.sh

or use any of the flags for the various arguments required at time of execution. Of course we include "-h" for help. See directly below for a picture of your help/usage printout and a manual run up to, but not including, the beginning of the listings:

Click on the picture below and watch the magic happen ;)

cabletv help and manual usage

After that, check out the next picture below, which shows a successful execution and the beginning of the TV listings. Note that the listings now cover a 3 hour time period and include start times for all shows (even those that began before your specified begin time, but happen to end within your time frame):

Click on the picture below and live slightly larger ;)

cabletv usage variations

I'd like to thank everyone who wrote in with comments and suggestions (which have been posted on the original tv script page). You've inspired me to do a better job, but (Lord knows) this script could still use some work. Hopefully you'll find it helpful :)

Of course, later this week, I'll be releasing some wrapper scripts to generate "Favorite Listings," "Only Movie Listings," etc...

SPECIAL NOTE FOR SOME *NIX USERS: If your version of Linux or Unix does not use the Gnu date (available for download as a part of the Gnu coreutils package), the Unix time (seconds since the epoch) extrapolation part of this script won't work correctly.

A few suggestions for getting around having to use Gnu date (available on newer Solaris and some distro's as gdate) include substituting this line of code:

cli_time2=`date -d "$cli_time" +%s`

with either:

1. cli_time2=`perl -e 'print int($cli_time)'` - although this can produce dubious results, or:

2. compiling your own version of gdate with the following code snippet found on The Unix Dummies Question Board:

/* esec.c - display seconds since 00:00:00 UTC, January 1, 1970 */
#include <stdio.h>
#include <time.h>

int main ( void )
{
return ( printf ("%d\n", time (NULL)) );
}


which can be compiled with gcc, like so:

host # gcc -o mygdate esec.c

and used, as a replacement for the above mentioned line of code, like so:

cli_time2=`mygdate "$cli_time"`

Of course, with the understanding that this wouldn't be an issue if the whole program were written in Perl, any suggestions are welcome. If enough interest exists, I'd be happy to rewrite this, in Perl (to make use of the localtime() and TimeLocal() functions), for publication in the near future.

In the meantime, we'll keep it as basic as possible so that anyone with Linux and a shell can find out what's on TV/Cable/Dish without turning on the tube ;)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# cabletv.sh - Get your local regular and HD Tv Cable or Dish listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
#
# TODO You have entered an invalid ZIP or postal code
# TODO am to pm or pm to am ends up not in chronological order
# TODO 12 to 1 a/p ends up not in chronological order
# TODO Deal with special characters
# TODO configuration file
# TODO better error checking
# TODO less hamfisted way of getting 3 hour spread for all channels
#

function usage()
{
echo
echo "Usage: $0 ...all options are optional [-h for help]"
echo "[-z USZipCode] [-p LocalCableOrDishProviderID]"
echo "[-t Time] [-d date] [-n to leave out HD channel info]"
echo
exit 1
}

while getopts t:z:hnd:p: option
do
case $option in
t)
opt_time="$OPTARG"
;;
d)
opt_date="$OPTARG"
;;
z)
opt_zip="$OPTARG"
;;
n)
opt_nohd=1
;;
p)
opt_provider="$OPTARG"
;;
h)
usage
;;
*)
usage
;;
esac
done

wget=/usr/bin/wget
pager=/usr/bin/more

if [ ! $opt_date ]
then
nicedate=`date "+%m/%d/%y"`
else
nicedate=$opt_date
fi


if [ ! $opt_time ]
then
hour=`date "+%H"`
minute=`date "+%M"`
ampm=`date "+%p"`
if [ $hour -lt 12 ]
then
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 AM"
else
nicetime="${hour}:30 AM"
fi
else
let hour=$hour-12
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi
cli_time=${hour}:${minute}${ampm}
else
cli_time=$opt_time
fi

cli_time2=`date -d "$cli_time" +%s`
let fromtime=$cli_time2*1000
let fromtime2=$fromtime+1800000
let fromtime3=$fromtime2+1800000
let fromtime4=$fromtime3+1800000
let fromtime5=$fromtime4+1800000
let fromtime6=$fromtime5+1800000
let fromtime7=$fromtime6+1800000
cli_time_end=`echo $fromtime7|awk '{printf("%20s",strftime("%I:%M %p - %x", $0 / 1000))}'`

if [ ! $opt_nohd ]
then
opt_nohd=0
fi

if [ ! $opt_zip ]
then
echo -n "Enter Zip Code: "
read zip_code
else
zip_code=$opt_zip
fi

if [ ! $opt_provider ]
then
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZBChooseProvider.do?method=getProviders&zipcode=${zip_code}" 2>&1|sed 's/<.*&lineupId=\([^&]*\)\">/\1 \c/'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Choose Your Provider/{x;$!d;x;q;};x;$G' -e '/document.write/,$d' -e "s/'/'/" -e 's/&/\&/' -e 's/^[ \t]*//;s/[ \t]*$//'|sed '/\([A-Za-z0-9_][A-Za-z0-9_]*:[-X]\)/ {
N
s/ *\n/ \t/g
}'
echo "Enter Provider ID [e.g. \"IL57303:-\" \"4DTV:-\" \"IL12561:X]\""
read tv_provider
else
tv_provider=$opt_provider
fi

echo
echo "Television Listings for $nicedate from $cli_time to $cli_time_end"
echo
last=1

if [ $opt_nohd -eq 1 ]
then
for x in $fromtime $fromtime2 $fromtime3 $fromtime3 $fromtime4 $fromtime5 $fromtime6
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*$/,+2p'|sed 's/^\([0-9][0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("%20s %-20s", "*** Start",strftime("%x - %I:%M %p",$3 / 1000));print ""}}'
done|sort -t'-' -k1,1n -k2,3n | uniq|while read one two three;do if [ $one -eq $last ];then echo " $three";last=$one;else echo "$one $two $three";last=$one;fi;done|$pager
else
for x in $fromtime $fromtime2 $fromtime3 $fromtime3 $fromtime4 $fromtime5 $fromtime6
do
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?fromTimeInMillis=${x}&method=decideFwdForLineup&zipcode=${zip_code}&setMyPreference=false&lineupId=${tv_provider}" 2>&1|sed 's/<.*&sch=\([^&]*\)&[^>]*>/\1 /'|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*\.*[0-9]*$/,+2p'|sed -e 's/^\([0-9][0-9]*\.*[0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*\.*[0-9]*$/ {
N
N
s/ *\n/\t/g
}'|awk '{ if ( $1 ~ /^[0-9]/ ) {printf("%-4s %-8s ", $1,$2);for (i=4;i<NF+1;i++) {printf("%s ", $i)};printf("%20s %-20s", "*** Start",strftime("%x - %I:%M %p",$3 / 1000));print ""}}'
done|sort -t'-' -k1,1n -k2,3n | uniq|while read one two three;do if [ "$one" == "$last" ];then echo " $three";last=$one;else echo "$one $two $three";last=$one;fi;done|$pager
fi

exit 0


, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Monday, October 27, 2008

Get Your Local TV Listings From The Bash Command Line

Hey There,

For this week's Monday Linux/Unix bash shell script, we're finally starting to go after online TV listings. If you've checked out all of our other bash CLI scripts aimed at helping you to not have to open your web browser, please skip the following paragraph. It's redundant, to say the least ;)

Our previous web-based Bash scripts, in backward chronological order, include our posts on accessing Wikipedia, accessing the Farmer's Almanac, accessing the International Dictionary, checking out the world's weather, spewing out famous quotations on pretty much any subject, doing encyclopedia lookups, accessing the online Thesaurus, translating between different languages and, of course, using the online dictionary.

This script gets its content from TV Listings At Zap2It.com and accepts a maximum of two arguments. The US Zip Code is a required argument (we haven't checked to see if this works for overseas, but we highly doubt it, at this point) and you can also pass the script a "nohd" argument so that you only get standard TV listings returned. The default action, for this script, is to return all local television results (including HD Channels) for the current time period (Once we crack the dynamic object code, we'll definitely post an updated version of this script that will allow you to pick different "begin times" and also increase the time period your output can include). Also, you'll note that this script, again, includes a "pager" variable (which we've set to /usr/bin/more) since the output you get, if you don't pass the "nohd" option, will be extremely long and repetitive. You can edit that variable to set it to your favorite pager very simply. Otherwise, the script can be run simply, like so (one example command line per general execution mode):

host # ./localtv.sh 60015
host # ./localtv.sh 60015 nohd
<-- Strongly recommended for now, even if you have HD. It seems that most program listings are repetitious, although getting the output with the HD listings (default) can help you figure out what HD channels are broadcasting in your area.

Below are two pictures of the output (from the first two examples above - "60015" and "60015 nohd"). As you can see, we had to clip the HD version, since there are about 5 HD channels for every "regular" channel ;)

Click on the pictures below to be taken away to a place where things are slightly larger ;)

hd tv listings

tv listings without hd

I hope this script (in its infancy) is somewhat helpful or, at least, amusing for you ;) If you question the output, you can verify it by checking the TV Listings At Zap2It.com. It's highly possible that you may get output that we didn't get when running (literally, running ;) this script through its paces. Feel free to remove the 2 or 4 self-nullifying sed expressions in the script, as well ;)

Cheers,


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/bash

#
# localtv.sh - Get your local regular and HD Tv listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

numargs=$#
date=`date`
hour=`date "+%H"`
minute=`date "+%M"`
if [ $hour -lt 12 ]
then
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 AM"
else
nicetime="${hour}:30 AM"
fi
else
let hour=$hour-12
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi

nohd=0

if [ $numargs -gt 2 ]
then
echo "Usage: $0 USZipCode [nohd]"
exit 1
fi
if [ $numargs -eq 2 ]
then
if [ "$2" == "nohd" ]
then
nohd=1
else
echo "Usage: $0 USZipCode [nohd]"
exit 1
fi
fi

args="$1"
wget=/usr/bin/wget
pager=/usr/bin/more
nicedate=`date "+%m/%d/%y"`

echo
echo "Television Listings for $nicedate - $nicetime"
echo

if [ $nohd -eq 1 ]
then
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?method=decideFwdForLineup&zipcode=${args}&setMyPreference=false&lineupId=PC:${args}" 2>&1|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*$/,+2p'|sed 's/^\([0-9][0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*$/ {
N
N
s/ *\n/\t/g
}'|$pager
else
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?method=decideFwdForLineup&zipcode=${args}&setMyPreference=false&lineupId=PC:${args}" 2>&1|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//'| sed -n '/^[0-9][0-9]*\.*[0-9]*$/,+2p'|sed -e 's/^\([0-9][0-9]*\.*[0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*\.*[0-9]*$/ {
N
N
s/ *\n/\t/g
}'|$pager
fi

exit 0


, Mike




MikeS had this to add regarding the script. Some of it has been worked into our updated script and some of it will definitely be added to the to-do list!

Hey I love your local TV bash script. I only noticed one
problem it does not provide other options for example if you
have digital service through a local provider or say DISH. I
am pretty new to using bash and sed gives me a headache. I
noticed on their page that if you click on TV listing and
enter your zip code it gives you a list of options consisting
of "cable", "satellite" and "local." I wonder how hard it
would be to parse that out first then let the user select the
best option. I hard coded my option in to your code and it
works great so I can't image it not working well. If you
you're not interested in working on it that's cool. I'll just
have to delve into bash and sed to get it figured out maybe
throw in a little zenity. Anyway it's great. If i do make any
changes I'll bounce it back to you. oh there are some text
issues too. like how the & and " are displayed.

htmlspecialchars can be a pain to handle.

EDITOR'S NOTE: I'm leaving this part out and redirecting to our post on posting code on Blogger since trying to pull this off again makes me nuts ;)

Later,

Mike


Russ had this to add, which ended up being another major contributing factor in the update of this script!

I just took a look at the zap2it page; I see that the GET parameter in the URL for a selected time of 20:00 PDT today is: ?fromTimeInMillis=1225249200000. That appears to be Unix milleseconds from the epoch. (The page source shows a number of day/time values for that variable embedded in JavaScript.) I tried plugging it into the URL in the script and it ran just fine, giving me the 8:00 listing. So the object would be to obtain the proper millisecond value for the desired time from a script date argument. I know how to do that in Python, but not in the shell.

Regarding the 3-hour time block, it appears that all the data is on the page. Each program is in an HTML anchor, with the time in milliseconds given as a tag's "sch" attribute and the channel in a "chn" attribute. So it seems it would be possible to loop through each channel for the time periods and display each one with its clock time. That's quite a bit more complicated than the current script, though, and I don't think I'll tackle it. On the other hand, perhaps someday I'll give it a try in Python, which is my language.

Best,
-Russ


Russ also submitted these script add-ons:

Code for "clean.py"

#! /usr/bin/python
""" Converts HTML entities to their corresponding characters.
"""

import sys, htmlentitydefs, re

pattern = re.compile("&#?(\w+?);")

def descape_entity(m, defs=htmlentitydefs.name2codepoint):
# callback: translate one entity to its ISO Latin value
try:
return unichr(int(defs[m.group(1).strip('&#;')]))
except KeyError:
return unichr(int(m.group(0).strip('&#;')))

def descape(string):
""" Processes the string for HTML entities. """
return pattern.sub(descape_entity, string)

txt = sys.stdin.read() # Read the incoming text from the pipe
sys.stdout.write(descape(txt)) # Process and write to standard out


and code for "tube.sh" - Note that some settings are hard-coded

#!/bin/bash

#
# localtv.sh - Get your local regular and HD Tv listings
#
# 2008 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

numargs=$#
date=`date`
hour=`date "+%H"`
minute=`date "+%M"`
if [ $hour -lt 12 ]
then
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 AM"
else
nicetime="${hour}:30 AM"
fi
else
let hour=$hour-12
if [ $minute -lt 30 ]
then
nicetime="${hour}:00 PM"
else
nicetime="${hour}:30 PM"
fi
fi

args="97045"
wget=/usr/bin/wget
pager=/bin/more
nicedate=`date "+%m/%d/%y"`

echo
echo "Television Listings for $nicedate - $nicetime"
echo

# Get the HTML from the Web site and run through the common filters
$wget -nv -O - "http://tvlistings.zap2it.com/tvlistings/ZCGrid.do?method=decideFwdForLineup&zipcode=${args}&setMyPreference=false&lineupId=PC:${args}" 2>&1|sed -e :a -e 's/<[^>]*>/ /g;/</N;//ba' |sed -e '/^[ \t]*$/d' |sed -e '1,/Forgotten password/d' -e '/isFavoritesAvailable/,$d'|sed -e '/[ECMP][SD]T/,+6d' -e "s/'/'/" -e 's/&/\&/' -e '/zc.getAdFrame/d' -e 's/^[ \t]*//;s/[ \t]*$//' > fil

# Filter channels with no HD listings and direct to the display file (disp)
cat fil | sed -n '/^[0-9][0-9]*$/,+2p'|sed 's/^\([0-9][0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*$/ {
N
N
s/ *\n/\t/g
}'|egrep -Ew '^2|^6|^8|12|22|32|49' > disp
##else
# Filter channels with HD listings and append to the display file
cat fil | sed -n '/^[0-9][0-9]*\.*[0-9]*$/,+2p'|sed -e 's/^\([0-9][0-9]*\.*[0-9]*\)$/\n\1/'|sed '/^[0-9][0-9]*\.*[0-9]*$/ {
N
N
s/ *\n/\t/g
}'|egrep -Ew '^10' >> disp
cat disp |./clean.py |$pager
# Delete the files
rm fil disp
exit 0



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Friday, October 24, 2008

Creating And Deleting Local Zones On Solaris 10 Unix

Hey There,

Today's post is the final post in our quick series on dealing with local zones on Solaris 10. If you want to check the previous entries out, finish this paragraph. If you don't (or already have), just skip past this one :) The previous posts in this series have dealt with already-created local zones and how to create new file systems in a local zone on Solaris 10, modify filesystems in existing local zones and remove file systems in a local zone.

Today's "how to" is going to cover the bookends of this mini-series; creating local zones in Solaris 10 and destroying them.

NOTE: This notation is fairly obvious in this post (it was more appropriate in the following three), but I'll include it for completeness' sake. You have to be in the global zone to create or destroy a new local zone. See what I mean? ;) Also, resource/storage pools, etc, are outside the scope of this series of posts. If you're curious to learn a bit more about that aspect, check out our older series of posts dealing with using storage pools in Solaris 10. The link is to the final post, but all the preceding posts are linked to on that page; much like they are on this one.

Now, we'll go, step by step, through creating a local zone (up through the final "installation" phase) and, subsequently, undoing all of our hard work by destroying it ;)

1. First of all, fire up your old friend zonecfg and we'll get the party started. For our examples, we'll call our new local zone "DING" to maintain consistency with our other posts and we'll call our pool "CLOCK" (ding dong, yeah, it's sappy ;) You'll notice that you define the zone name when you invoke zonecfg, although just doing that doesn't actually "create" it, like so:

host # zonecfg -z DING <-- This is going to give you an error, but you can safely ignore it. Basically, the error just indicates that you're trying to configure a zone that hasn't been created yet. That's o.k., because we're just about to :)
zonecfg:DING> create

2. Next, we'll set up all of the minimally necessary parts of this local zone (note that we've already checked that enough resources (disk, IP, etc) exist in order for us to be able to install our new zone):

zonecfg:DING> set zonepath=/zones/DING/
zonecfg:DING> set autoboot=true
<-- I would recommend leaving this as "false" until you know you're good, but the worst that can happen isn't really all that bad, since a local zone caught in an auto-boot-loop won't cause you the same headache a straight-up box with the same issue would.
zonecfg:DING> add net <-- Notice how this puts you in a sub-menu, which shows up in the prompt. This happens for most device configuration and is pretty helpful if you have to walk away from your build for a while and come back to a screen where someone's hit enter 500 times ;)
zonecfg:DING:net> set address=99.99.99.1
zonecfg:DING:net> set physical=bge0
zonecfg:DING:net> end
<-- and this, universally, takes us back down (or up, depending on how you look at it) a menu, so we end up back at
zonecfg:DING>

3. Then, with the process just about completed, we'll assign the zone to the "CLOCK" storage pool (outside the scope of these posts, but you can checkout our older 4-part post on working with storage pools). Once that's complete, we'll verify our zone and commit the configuration, like this:

zonecfg:DING> set pool=CLOCK
zonecfg:DING> verify
zonecfg:DING> commit
<-- This writes the configuration, which, up until this point, is held in-memory.
zonecfg:DING> exit (or ^D [Ctrl d])

4. The last step in creating a zone isn't entirely obvious. The first time I did this, I thought I was done when I completed step three. The zone was created, I'd assigned it all of the resources it required (and tweaked all of those) and then verified and committed the zone. Alas, I was wrong, there was still one last thing left to do.

The final act, in creating and enabling your new local zone is to "install" the zone with the zoneadm command. This will not only re-verify all of your zone's resources, check and see if your new zone will run on your system (or any other, for that matter), but also installs all the necessary files in the local zone's root filesystem and creates all mount points as necessary. It's very simple to run and very easy to deal with (Although it may take a little bit more time, even if your setup is good :) - Just run it, simply, like this:

host # zoneadm -z DING install
Preparing to install zone email-zone
...
Zone DING is initialized.


And that's, basically, all you need to do to set up a local zone :) Of course, you should run "zoneadm -z DING boot," zlogin, etc, to boot the zone up, login and run your own tests, just to be sure everything is the way you like it. If you need to make any adjustments, you can still use zonecfg to modify your zone's configuration (theoretically ad infinitum).

Now let's get to work ruining it all, by destroying our local zone ;) It's actually very simple and only includes one "real" step. We'll do the first part, just do to be polite, and shutdown the zone before we destroy it, like so:

host # zoneadm -z DING halt <-- Totally unnecessary if you're going to utterly destroy your zone anyway

Then, all we need to do is run one command to end it all:

host # zonecfg -z DING delete <-- You can also use the -F flag if it won't go away. Note, also, that no commit is necessary, as it was when we created the local zone.

And there we have it; the poignant story a local zone from birth to death ;)

Cheers,

, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Thursday, October 23, 2008

Removing Local Zone File Systems On Solaris 10 Unix

Hey There,

Today's post is a follow-up to our very recent posts on modifying existing local zone filesystems and creating new file systems in a local zone on Solaris 10. Today, we're moving to completion with this simple how-to on removing filesystems on local zones. We'll follow up with another simple post on how to create and destroy (all in one) local zones on Solaris 10. Even though I can write 500 hundred words on why I can't write less than 500 words, that post should be short ;) It almost "has" to be!

NOTE: This note is the same as in the last two posts. Nothing has changed ;) For those of you who haven't read those (or find this post all on its lonesome ;) here it is: When removing existing filesystems on a Solaris 10 local zone, all changes must be made to the filesystem from the global zone (assuming that you originally mounted it from there)!

1. First things first; you can't unmount a filesystem from within a local zone if it was created in the global zone. As per our notation in the post on creating filesystems on local zones, this does not apply to NFS, NAS or any other remote-or-network-mounted filesystems. These are mounted directly from the local zone and, as such, can be unmounted directly from the local zone.

One interesting thing, if you have your filesystem set up under the local zone's root, is that it won't show up on the global zone's "df -k" or "df -h," etc output. If you want to see the mount from the global zone (assuming you forgot where you put it or you just want to make sure), you can find out what filesystems are mounted in your local zone, from the global zone, (at least, this is one way) by running:

host # zoneadm list -vc <-- To list out the zones on your system from the global zone (DING is our example zone).
host # zonecfg -z DING info <-- This will list out more information than you need. Basically, look for the line at the top titled "zonepath:" You can then look under that zone path to see your local zone's root filesystem mounts. Note that you could just use "grep" to find all the lines in /etc/vfstab that include your local zone's name.

2. Back to cold harsh (but mercifully swift ;) reality. From the global zone, simply unmount your local zone filesystem, like so:

host # umount /zones/DING/root/DONG

3. Then, all that's left to do is to remove the local zone from the overall zone configuration (we're assuming that you've unmounted all of "your" local zone's filesystems before continuing and executing the steps here :) Again, this can all be done very simply by using the zonecfg command:

zonecfg –z DING
zonecfg:DING> remove fs dir=/DONG
zonecfg:DING:fs> commit
zonecfg:DING:fs> quit


4. Okay; step 3 included a white lie ;) If you don't want to get error messages or, worse, cause your system not to boot properly, it's recommended that you remove the local zone filesystem entries from /etc/vfstab in the global zone, as well.

And now (I kind of promise ;) you should be all set :) We'll post that creation/destruction post very soon, if not tomorrow. After that, we'll get back to Linux, AIX or any Operation System other than Solaris 10. We try to keep a fair spread given the sheer amount of distro's the term "Linux and Unix" actually encompasses. On the bright side, this blog's subject matter could easily be much more vague ;)

Cheers,

, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.