Tips 2

Lessons Learned

From grading lab 2, I saw a couple things that worried me. Since this is the first lab, I didn’t take off points - but from next lab onwards please do keep this in mind.

But of course you were using my vim configuration, so you already had this on, didn’t you… If you are using emacs, a guide to soft tabs.

The next time I open a file and see:

Lolol some code lolol
   such stupidity trolol
   all in the same block heheh
       but is not indented properly
because lol no to teh soft tabs

I will lose my mind, and you will lose your grade. So we both lose. Use “teh” soft tabs.

Solutions to Lab 2

Let’s talk birthdays:

if [ `date --date="$1" +%a` = `date --date="$2" +%a` ]; then
    echo "Same day!"
else
    echo "Not same day."
fi

The neat +%a operator converts dates to days of the week, which saves you the necessity of having to do the | cut -d' ' -f1'. To me, this solution was cuter. Others may disagree.

Counting the lines in a file.

This one was exceedingly simple if you know the proper linux commands.

find . -type f | xargs wc -l

The’s actually quite a bit going on here, so let’s dive into it.

The find command is invaluable. Given a directory, it will find whatever you need it to. For example, if you want to find a file named chanderTheBeast.truth and you’re in your cs50 directory, but you want to search from your home (~) directory, simply type find ~ -type f -name "chanderTheBeast.truth" and voila! It searches starting from the ~ directory. The type parameter is simply the type of thing it’s looking for. So you can put -d for directory, or -f for file.

Great, so we understand the find command. It gives us a list of all files starting in the current directory - which is what the problem asks of us. The next portion is the mysterious xargs command. This is incredibly useful.

What would happen if I had infinite recursion (not tail recursion, just plain old recursion, but infinite)? Well, I would get a Stack Overflow error, right? Well, you may remember from your architecture classes that parameters / arguments live in the stack, too! So what would happen if I gave this wc -l function one trillion file names as parameters!? Another stack overflow error, or depending on your operating systems, an error. Some OS’s are smart enough to detect this sort of thing and impose a hard limit on maximum argument count.

The incredibly useful xargs function feeds whatever you’ve piped to it, one at a time to the function that follows. If it’s an incredibly long list, it will actually chop it up into pieces to avoid the issue we just discussed above!

And the last piece, wc -l is something we already know about. It gives the number of words for a given parameter.

So altogether, the command finds all the files in a directory (and its subdirectories,) passes them one by one to the wc -l function which gives the number of words for each of the files. In other words, this does what the problem asks!

Pretty neat, eh? The tell-tale sign of a good bash script is brevity.

Leap Years

This one was rather simple - all you had to do was implement Wikipedia’s algorithm.

if (( $1 % 400 == 0 || ($1 % 4 == 0 && $1 % 100 != 0) )); then
    echo "$1 is a leap year."
else
    echo "$1 is not a leap year."
fi

Not much more to say on that point. The key here understanding short-circuiting.

Searching URL’s

This is one of the few areas where I disagree with Professor Campbell. In my opinion, the command curl is far superior to wget. But then again, this is up for debate.

The main differences are the following

Choose what you like, but I prefer curl. Also reminds me of lifting, and we all need some swole acceptance.

for word in "${@:2}"; do
    echo $word
    while read line; do
        echo "$line `curl -Ss $line | grep -c $word`"
    done < $1
done

So what’s going on with this funky outer loop? Well "${@:2}" is basically the easy bash way of skipping the first parameter because that’s the filename - we don’t care about searching for the filename, we only want the key words. So the outer for loop just iterates over all the given key words.

Great. What about the inner loop? The while loop with the funky done < $1 is the bash way of reading lines from a file. Here, $1 is the filename - so we read every line in the given file.

Lastly, the curl -Ss $line | grep -c $word does two things:

  1. curl: as mentioned, it downloads a URL, much like wget. The -S option is how to tell curl you want to download something. Unfortunately, curl gives you extraneous information, like how many bytes it downloaded and how fast (it likes to brag about being faster than wget). To silence curl (silence, infidel!) we use the -s option.
  2. The second thing is searching for how many times in this URL the key word occurs. Well, luckily for us grep has a nice -c option which doesn’t give anything but the count for how many times that occurred.

Together, we have the complete program.

Mission Impossible 4: Section Leader Protocol

Probably still a better movie than Ghost Protocol. Yeah, I know - I’m in the minority. Meh. So sue me.

The first challenge in this program is to convert the given name Chandrasekar Ramesh or whoever your section leader is to their username, chander. This is easily done enough by examining the file /etc/passwd.

Here’s how I got the username from the name:

cat /etc/passwd | grep $name | awk -F: '{print $1}'

Pretty nifty. awk and sed are extremely useful commands - you can think of awk as a super saiyan version of cut; it does a lot more than just this, but that’s a basic explanation for now. If you need to do any find and replace operations, sed is your gal. Learn both of these tools - they are ubiquitous throughout computer science.

Next, all we need to do is determine if the user is online or not. That’s accomplished through the who command.

if [ -n "`who | grep $name`" ]; then
    echo "Gotcha!" | mail -s "Gotcha!" $name
fi

The test function has many, many options. It’s well worth your time to memorize some of them. -n checks if the resulting string is not empty. grep would only return a non-empty string if it found the name. Thus, if it found the name, we simply send them an email.

Lastly, getting the date for right now is done with date -d now.

You have many options for how to store the information, but given that the best design will scale to more than just two users, it’s better to store this information in an array.

It’s soon evident that we need 3 arrays: the first is the array of arguments; the second is the Unix usernames associated with each name; and the third is the array of statuses maintaining whether the person is online or not.

The seq command is really useful in being able to produce a range of numbers to iterate over - this helps iterate through array indices, and using the index, you can index into all three of the arrays.

One alternative method you may want to consider, though, is the last command. It shows the log of all logins and their durations and which username it belongs to over the past couple days. Very useful stuff - and could be an alternative to who (or used for the extra credit, eh?).

Ultimately, my implementation wasn’t so different than the solution, so it’s not worth presenting at length here.