Code Maven Programming Exercises
Moved here from Code Maven
See the pages on the side.
Some simple exercises and several project ideas to practice programming.
When learning a programming language, the most important thing is to practice. Here are some exercises you can do. For each exercise, as time permits, I am going to add instructions specific to some programming languages, but these exercises can be solved in almost any other programming language.
Once you feel comfortable with the exercises you can pick one of the more complex projects as well.
Introduction
Scalars
- Calculate Area of Rectangular (Standard I/O, if)
- Number guessing game (random, prompt, if)
- Create calculator
Files
- Sum of numbers in a file (open, loop +=)
- Analyze Apache log file (count localhost vs others, index, substr)
- Add more statistics
- Write report to file
Lists and Arrays
- Color selector
- Add numbers taken from CSV file
- Improve the color selector
- Improve number guessing game
- Count digits
- Sort based on secondary condition
- Display unique rows of a file
- Sort mixed strings
- Queue at the Doctor
- Check if number is prime
- Reverse Polish Calculator
Hashes, Dictionaries
- Count words in a file
- Parse HTTP GET parameters
- Sort scores
- Analyze Apache log file (count all the hits from all the clients)
- Parse file with variable width fields
Functions and Subroutines
Regular Expressions, Regexes
- Regex Exercises - part 1
- Match numbers with regex
- Match hexa, octal, binary numbers
- Match Roman numbers
- Split file path with regex
- Sort SNMP numbers
- Parse hours log file and create time report
- Parse INI or Config files.
Linux
Shell
3rd party libraries
- Install Module from CPAN/PyPi/Gems/etc.
Simple applications
- Counter
- Exercise: Implement the wc command of Linux/Unix (word count)
- Traverse directory implementing du
- One dimensional spacefight (aka. The spaceship operator)
- MasterMind
Mix
- Send plain text e-mail
- Send HTML e-mail
- Send e-mail with attachments
- Compare Wikipedia translations
- RSS/Atom to e-mail
Web development
Web crawlers
Exercise: Hello World
- ruby
- python
- javascript
- php
First exercise - checking your environment.
Try your environment:
-
Make sure you have access to the right version of your language. Pick the language you are currently learning from the below list:
-
Perl 5: As of this writing version 5.30 is already out, but most of the material will work on 5.10 and even 5.8, if you cannot upgrade. (See the most recent release of Perl 5.)
-
Raku (formerly known as Perl 6): See the most recent version of Raku (that was 2019.03 when I updated this).
-
Ruby: 2.5.x will be fine. Check out the most recent version of Ruby.
-
Python: In case you have Python 2 (e.g. 2.7.x) you should strongly consider switching to Python 3. Even though if you need to maintain legacy code then you might need to know Python 2 as well.
-
In the Python 3 series any of 3.8.x is the most recent, but most of the examples will work on older versions of Python 3 as well. See the most recent version of Python.
-
Go version go1.12.9 looks fine. The you could get the most recent version of Go.
Check you can read the documentation.
- Check if you have a good editor with syntax highlighting.
- Write a simple script that prints "Hello world!".
- Add comments to your code.
- Add user documentation to your code.
Solutions
Calculate the area of a rectangular
Write a program that computes the area of a rectangular.
- Start by having two hard coded values. Multiply them and print the result. The result should be 42:
$ perl example.pl
42
- Then change the code to prompt the user for two numbers. Multiply them and print the result:
$ perl example.pl
First number: 7
Second number: 6
42
# ruby example.rb
First number: 3
Second number: 11
33
- Further modify the program to warn if one of the numbers given was negative.
$ python example.py
First number: 3
Second number: -1
Warning: Invalid input. The side of a rectangular cannot be negative.
Number guessing game
In this exercise we are starting to build a game, called the number guessing game.
The game is that the computer "thinks" about a number and we have to guess it. On every guess, the computer will tell us if our guess was smaller or bigger than the hidden number. The game ends when we find the number.
This exercise is the first step of this game.
Create a simple command line game guessing a whole number
- Let the computer "think" about a whole number between 1-200.
- Let the user guess a number.
- Tell the user if his guess was bigger or smaller than the number the computer "thought" of or tell them if they hit the right number.
- At this point the user only has one guess, though as a bounus you can allow the user to guess several times.
Solutions
- Number guessing in Ruby
- Number guessing in Python
- Number guessing in Perl 5
- Number guessing in Perl 6
- Number guessing in Groovy
Create calculator
Create a command line calucaltor that will prompt for two numbers and an operator
and will calculate the result. Handle the following operators: +
, -
, *
, and /
.
An interaction should look like this:
$ mycalc
First number: 23
Second number: 19
Operator: +
Result: 42
where the numbers 23, 19 and the operator + are typed in by the user.
Solutions
Sum of numbers in a file
Let's implement something slightly useful.
In this exercise we have a file called numbers.txt
that has numbers in it.
Each line is a number.
Our aim is to add the numbers and print the result.
3
7
23
0
-17
98
12
Expected result is 126.
Tools
Some of the tools you might need to solve this exercise.
-
Perl:
-
Ruby:
-
Convert string to number in Ruby
-
For the advanced version:
-
map in Ruby
-
reduce in Ruby
-
Python:
-
Open file in Python
Solutions
- Python: sum of numbers
- Perl 5: sum of numbers in a file
- Ruby: sum of numbers in a file
- Groovy: sum of numbers
Analyze Apache log file - count localhost
In this exercise we take a log file generated by a web server, and do some simple analysis on it.
The file looks like this:
127.0.0.1 - - [10/Apr/2007:10:39:11 +0300] "GET / HTTP/1.1" 500 606 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:39:11 +0300] "GET /favicon.ico HTTP/1.1" 200 766 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
139.12.0.2 - - [10/Apr/2007:10:40:54 +0300] "GET / HTTP/1.1" 500 612 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
139.12.0.2 - - [10/Apr/2007:10:40:54 +0300] "GET /favicon.ico HTTP/1.1" 200 766 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:53:10 +0300] "GET / HTTP/1.1" 500 612 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET / HTTP/1.0" 200 3700 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET /style.css HTTP/1.1" 200 614 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET /img/machine-round.jpg HTTP/1.1" 200 17524 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.11 - - [10/Apr/2007:10:54:21 +0300] "GET /unix_sysadmin.html HTTP/1.1" 200 3880 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:54:51 +0300] "GET / HTTP/1.1" 200 34 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:54:51 +0300] "GET /favicon.ico HTTP/1.1" 200 11514 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:54:53 +0300] "GET /cgi/machine.pl HTTP/1.1" 500 617 "http://contact.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
127.0.0.1 - - [10/Apr/2007:10:54:08 +0300] "GET / HTTP/0.9" 200 3700 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:58:27 +0300] "GET / HTTP/1.1" 200 3700 "-" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:58:34 +0300] "GET /unix_sysadmin.html HTTP/1.1" 200 3880 "http://machine.local/" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
217.0.22.3 - - [10/Apr/2007:10:58:45 +0300] "GET /talks/Fundamentals/read-excel-file.html HTTP/1.1" 404 311 "http://machine.local/unix_sysadmin.html" "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.3) Gecko/20061201 Firefox/2.0.0.3 (Ubuntu-feisty)"
Every row is a request on a web server. Some are pages, some are images, some are JavaScript files. That's not the important part now. Currently we are going to focus on the first field of every line which is the IP address of the requester. We go with a simple exercise.
As you might know, every device uses the IP address 127.0.0.1 to refer to itself. So visitors arriving from that IP address are based on the same machine.
The task is to write a scrip that will count how many requests came from 127.0.0.1 and how many from elsewhere?
Tools
- Perl 5: open the file for reading or die
- Perl 5: chomp remove trailing newlines
- Perl 5: index
- Perl 5: substr
- Ruby: ARGV command line parameters.
- Ruby: open a file for reading and iterate over it.
Solutions
Add more statistics
Take the solution from the count sum exercise where we added up the numbers in each row of a file, and add additional statistical values:
- minumum
- maximum
- average
If you are following the exercises then caluclating median and standard deviation might too difficult at this point, but you can give them a try.
Solutions
Write report to file
Take your solution from the Add more statistics exercise, and write the result into a file.
Tools
Solutions
Color selector
Given a list of colors (Blue, Yellow, Brown, White) create a script that will display a menu and ask the user to select one of the colors: For example an interaction would look like this:
$ perl color_selector.pl
Please select a number:
0) Blue
1) Yellow
2) Brown
3) White
2
The selected color is Brown
or like this:
$ perl color_selector.pl
Please select a number:
0) Blue
1) Yellow
2) Brown
3) White
7
Bad selection
Tools
Solutions
Add numbers taken from a CSV file
Given a CSV file like this:
Tudor;Vidor;10;Hapci
Szundi;Morgo;7;Szende
Kuka;Hofeherke;100;Kiralyno
Boszorkany;Herceg;9;Meselo
Write a script that will take the values of the 3rd in this Comma Separated Values file and add them together.
Can your code also handle this file?
Budapest,Bukarest,1200,km
Tel Aviv,Beirut,500,km
London,Dublin,300,km
New York,"Moscow, East",6000,km
Local,"Remote
Location",10,km
Tools
Solutions in Perl 5
- Perl 5 Process CSV file
- Perl 5 Process CSV file short version
- Perl 5 One-liner sum of column in CSV
- Process CSV file using Text::CSV_XS
Solution Ruby
Solution in Groovy
Improve the color selector
Improve the color selector
There are several parts of this exercise:
- The color selector created previously shows menu items numbered from 0. Change it so the displayed numbers will start at 1 but that it will still work correctly.
- Currently the user can give any value on the command line. Incluing "nonsense". Check that the given value is indeed one of the possible values hard-coded in the script. Report an error and quit if it isn't.
- Allow the user to supply a flag called --force If this flag is present allow any value as a color. Even "nonsense".
- Read the names of the valid colors from the colors.txt file.
- Allow the user to supply a --file flag as in "--file mycolor.txt" and take the name of the file with the colors from there.
Improve Number guessing game
This is an extension of the Number guessing game exercise.
This exercise has several steps.
First of all, let the user guess several times (with responses each time), until she finds the hidden number.
Allow various special keys too:
- n - skip the rest of this game (give up) and start a new game with new hidden number.
- s - show the hidden value (cheat).
- m - toggle: turn on/off move mode: When the move mode is on, after every guess the number can change by 2 in either direction.
- d - toggle: turn on / off debug mode. In debug mode show the hidden number every time before you are asked to supply the guess.
- x - exit
Now I can tell you that this is actually a 1 dimensional space fight. The number is your distance from the enemy space ship.
For training purposes limit the outer space to be between 0-200 so even when the move mode is on the space-ship won't fly off this area.
Finaly, keep track of the minimum and maximum score (number of guesses till hit) in a file.
Tools
Solutions
- Perl 5: Improve the number guessing game - multiple guesses
- Perl 5: Improve the number guessing game - exit
- Perl 5: Improve the number guessing game - s for show
- Perl 5: Improve the number guessing game - n for next game
- Perl 5: Improve the number guessing game - d for debug mode
- Perl 5: Improve the number guessing game - m for move mode
Count digits
Exercise: count digits in a file
Given a file like this, that has rows of numbers in it without anything, but a single space in between the numbers. Our task is to count how many times each digit appears.
23 34 9512341
3 34 2452345 5353 67 22
42136357013412
42 5 65 64
Our expected output looks like this:
0 1
1 5
2 9
3 11
4 9
5 8
6 4
7 2
8 0
9 1
Tools for Perl 5
- <a href-"https://perlmaven.com/perl-tutorial">The Perl 5 tutorial in general.
- Perl 5: shift from @ARGV
- Perl 5: open a file for reading or die.
- Perl 5: Arrays
- Perl 5: chomp, remove trailing newlines.
- Perl 5: split, to cut a string into pieces.
Tools for Ruby
- Ruby: ARGV for command line parameters
- Ruby: Open file and read content
- Ruby: Arrays
- Ruby: Iterate over elements of a an array
Solutions
Sort based on secondary condition
We have a file with strings in it. We would like to sort then according to length. The short strings first. The result is that within the strings of the same length we see them in random order. How can we sort them according to abc? So first come the short strings, but within each length we have abc order.
Foo
Foobar
Bar
Baz
Moo
Another
Reallylong
Shorter
Solutions
Display unique rows of a file
Given a the following file:
A 1
B 2
A 2
C 3
B 2
D 4
C 1
Create another file in which the first character is unique:
A 1
B 2
C 3
D 4
Tools
Solutions
Sort mixed string
In a file we have the following strings: A1 B1 A27 A3 ... each string has a single letter at the beginning and then a number.
Sort them first based on the first letter and among values with the same leading letter sort them according to the numbers so the above values would be in the following order:
A1 A3 A27 B1
File:
A1 A27 C1 B1 B12 A38 B3 A3
Queue
The application should manage a queue of people.
- It will prompt the user for a new name by printing
:
. - The user can type in a name and press ENTER.
- The app will add the name to the queue.
- If the user types in "n" then the application will print the first name in the queue.
A sample interaction will look like this:
: Foo
: Bar
: Moo
: n
next is Foo
: n
next is Bar
: Peter
: n
next is Moo
: n
next is Peter
: n
the queue is empty
Check if number is prime
Write a program that gets a number on the commnad line a prints "True" if the number is a prime number or "False" if it isn't.
$ is_prime 42
False
$ is_prime 19
True
Implement Reverse Polish Calculator
Implement Reverse Polish Calculator
We are used to write calculations using the infix notation where the operator is between the two operands. e.g. 3 + 4 * 7. In order to implement a calculator that knows how to calculate this one needs to implement the order of operations.
In the Reverse Polish Notation the operator comes after the two operands. e.g. 3 4 + instead of 3 + 4.
In this notiation there are no preferences between operators.
The above expression can be written in RPN as:
3
4
7
*
+
=
The task is to implement RPN in your favorite language.
Python
In order to make it easer for you I've prepared a module that implements the low-level calculations.
def calc(a, op, b):
'''
from calc import calc
calc(2, '+', 3)
'''
if op == '+':
return a + b
if op == '*':
return a * b
if op == '-':
return a - b
if op == '/':
return a / b
raise Exception("Operator '{}' not supported".format(op))
def test_calc():
assert calc(2, '+', 3) == 5
assert calc(2, '*', 3) == 6
assert calc(8, '-', 3) == 5
assert calc(8, '/', 2) == 4
import pytest
with pytest.raises(Exception) as exinfo:
calc(2, '**', 3)
assert exinfo.type == Exception
assert str(exinfo.value) == "Operator '**' not supported"
# To test this module, run pytest calc.py
Solutions
Count words in a file
Similar to the exercise in which we counted digits, this time we would like to count words.
Give a file that contains lots of line, each line containig words separated by a space, we would like to count how many times each word appear. For the sake of simplicty in parsing, we can assume that there are no special pnctuations that we might need to get around. Just words separated by a space.
A small input file might look like this:
one word another word
a new sentence and a new word
Tools
Solutions
Parse HTTP GET parameters
When you visit a web-site, fill in a form and submit it, in some cases you will see the URL containing a question mark ?
followed by a long list of key-value pairs. These are the HTTP parameters of this GET request.
Every programming language has plenty of libraries handling these values properly, but we can also use them as an exercise.
So given a string like this: fname=Foo&lname=Bar&email=foo@bar.com
or a string like this ip=127.0.0.1&machine=foobar
create a hash/dictionary/associative array that will hold the key-value
pairs found in the string.
Given fname=Foo&lname=Bar&email=foo@bar.com
the data structure should look like this:
{
'fname' : 'Foo',
'lname' : 'Bar',
'email' : 'foo@bar.com'
}
Given this string ip=127.0.0.1&machine=foobar
the data structure should look like this:
{
'ip' : '127.0.0.1',
'machine' : 'foobar'
}
For extra bonus try to handle this case as well:
Given fname=Foo&lname=Bar&email=foo@bar.com&email=here@there.or
the data structure should look like this:
{
'fname' : 'Foo',
'lname' : 'Bar',
'email' : [ 'foo@bar.com', 'here@there.or' ]
}
sort scores
In this exercise you a file with scores. Each line has a name and a number (a score) separated by a comma.
Foo,23
Bar,70
Baz,92
Bozo,17
Gozo,52
Dardon,20
Mekodra,23
The first task is to read in the file and print out the names with their scores ordered by name givin the following result:
Bar 70
Baz 92
Bozo 17
Dardon 20
Foo 23
Gozo 52
Mekodra 23
The second task is to print them ordered by scores resulting in:
Baz 92
Bar 70
Gozo 52
Foo 23
Mekodra 23
Dardon 20
Bozo 17
Tools
Solutions
Analyze Apache log file - count every host
In this exercise we take a log file generated by a web server, and extend the previous exercise analyzing Apache log files.
The task is to write a scrip that will count how many requests came from each different IP address and then print them in descending order.
Solutions
Parse variable width fields
In this log file I got from some company, there are rows in which the first 16 and last 16 characters describe some kind of an addresses while everything in between describes several commands.
Each command is built up by a leading character (A, B, C, D, etc) and a number of digits. The number of digits depend on the leading character.
In this exercise we need to split up the data to commands and count how many times each command type was given.
1234567890123456A001B0002D00004C0000051234567890123456
1234567890123456A001A002D00004C0000051234567890123456
Tools
Solutions
ROT13
Implement ROT13: given a string return the rot13 of it. Given a file, replace with the rot13 of it.
Regexes part 1
Pick up a file with some text in it. Write a script (one for each item) that prints out every line from the file that matches the requirement. You can use the script at the end of the page as a starting point but you will have to change it!
- has a 'q'
- starts with a 'q'
- has 'th'
- has an 'q' or a 'Q'
- has a '*' in it
- starts with an 'q' or an 'Q'
- has both 'a' and 'e' in it
- has an 'a' and somewhere later an 'e'
- does not have an 'a'
- does not have an 'a' nor 'e'
- has an 'a' but not 'e'
- has at least 2 consequtive vowels (a,e,i,o,u) like in the word "bear"
- has at least 3 vowels
- has at least 6 characters
- has at exactly 6 characters
- all the words with either 'Bar' or 'Baz' in them
- all the rows with either 'apple pie' or 'banana pie' in them
- for each row print if it was apple or banana pie?
- Bonus: Print if the same word appears twice in the same line
- Bonus: has a double character (e.g. 'oo')
Ruby
if ARGV.length != 1
puts "Usage: Needs filename"
exit
end
fh = open ARGV[0]
fh.each do |line|
if /REGEX/.match(line)
print line
end
end
Python
#!/usr/bin/env python
from __future__ import print_function
import sys
import re
if len(sys.argv) != 2:
print("Usage:", sys.argv[0], "FILE")
exit()
filename = sys.argv[1]
f = open(filename, 'r')
for line in f:
print(line, end=" ")
match = re.search(r'REGEX1', line)
if match:
print(" Matching 1", match.group(0))
match = re.search(r'REGEX2', line)
if match:
print(" Matching 2", match.group(0))
Perl
For Perl check out the respective regex exercise page on the Perl Maven site.
Solution - Perl 5
Match numbers with regex
Write a function that given a string returns "true" if the string is a number and "false" if it is not a number. As there might be several definitions of what a number is, you need to create several solutions one for each definition:
- Non negative integer.
- Integer. (Will you also allow + in front of the number or only - ?
- Real number. (Do you allow .3 ? What about 2. ?
- In scientific notation. (something like this: 2.123e4 )
Tools
Solution
Match hexa, octal, binary numbers
Write 3 functions that return "true" if the given value is a
- Hexadecimal number
- Octal number
- Binary number
Tools
Solution
Match Roman numbers
Exercise: Match Roman numbers
I usually give this as a bonus exercise for people who finish all the other exercises early. Let's see if you can do this?
Write a function that return "true" if the given value is a Roman number. Can you even write a Roman to "normal" (also known as Arabic) number converter?
Roman numbers are:
I = 1
II = 2
III = 3
IV = 4
V = 5
VI = 6
VII = 7
VIII = 8
IX = 9
X = 10
...
I = 1
V = 5
X = 10
L = 50
C = 100
D = 500
M = 1000
Tools
Solution
Split file path using regex
Write two functions.
Given a path like /home/foo/.mozilla/cache/data.txt, one of the functions will return the filename (data.txt), the other function will return the full-path directory name (/home/foo/.mozilla/cache).
Handle also the case of c:\home\foo.mozilla\cache\data.txt.
Sort SNMP numbers
Given a file with SNMP numbers (one number on every line) print them in sorted order comparing the first number of each SNMP number first. If they are equal then comparing the second number, etc...
Actually the format is called ASN 1.
1.2.7.6
4.5.7.23
1.2.7
1.12.23
2.3.5.7.10.8.9
Tools
Solutions
Parse hours log file and create time report
When I was running class-room Perl training courses I've was logging how my course progresses and created log files like this:
09:20 Introduction
11:00 Exercises
11:15 Break
11:35 Scalars
12:30 Lunch Break
13:30 Exercises
14:10 Solutions
14:30 Break
14:40 Arrays
15:40 Exercises
17:00 Solutions
17:30 End
09:30 Advanced Arrays
10:30 Break
10:50 Exercises
12:00 Solutions
12:30 Hash fuction introduction
12:45 Lunch Break
14:15 Exercises
16:00 Solutions
16:15 Break
16:30 Subroutines
17:00 Exercises
17:30 End
Every row starts with the timestamp when the specific activity started and then the name of the activity. Empty rows separate the dates.
Some of the activities are the names of the lectures, such as Introduction and Scalars. Others are the names of the names of the activities: Exercises, Solutions, Break, Lunch Break, and End marks the end of the training day.
A course lasts several days. In our sample file we have two days.
Once I had these log file I wanted to generate two reports.
One of them was to display the same data, but showing from-to timestamps and removing the End entry. It would look like this:
09:20-11:00 Introduction
11:00-11:15 Exercises
11:15-11:35 Break
...
The other one would show the accumulated time spent on various activities and the lectures detailed.
Lectures: 210 minutes 22%
Solutions: 95 minutes 9%
Break: 65 minutes 6%
...
Lectures:
Introduction: 23 minutes 2%
...
This is the exercise, to implement the code that will take the log file and generate the reports.
Tools
Solutions
Parse INI file
An INI or Config file looks like this:
# comment
[alpha]
base = moon
ship= alpha 3
[earth]
# ?
base=London
ship= x-wing
It has sections that start with a section name in square brackets and inside section it has key-value pairs separated by an equal sign surrounded
by 0 or more spaces. Empty rows are disregarded. Any line starting with a #
is considered a comment and is also disregarded.
In some extended cases we can have key-value pairs before any section was declared and they will belong to an implicit seciton called _
.
In other extended cases multi-line values are also allowed.
For our purpuse we can go with simple ini files.
The task is to create a script that can parse such input file and create a hash-of-hashes (dictionary of dictionaries in Python) where the primary key is the section name, the secondary key is the 'key' and the value is the value in each line.
The Perl dump of the above INI file would look like this:
$VAR1 = {
'earth' => {
'base' => 'London',
'ship' => 'x-wing'
},
'alpha' => {
'base' => 'moon',
'ship' => 'alpha 3'
}
};
Tools
Solutions
Linux as a Virtual Environment - install + nginx
- VirtualBox
- Ubuntu
- Linux
- Nginx
These exercises were given to the participants of the DevOps Workshop on using Linux as a Virtual OS on Windows (or Mac or another Linux).
A few exercises that you could do to practice what we have learned during the workshop. Some will go way beyond it and make you learn a lot more about using Linux on the command line.
- Set up another Virtual Box and Install Ubuntu. This time use a different IP address.
- Make sure you can ping the machine from your host machine (Your Windows or OSX machine)
- Install the ssh server and check that you can connect to the guest machine using Putty or some other ssh client.
- Install Nginx on this new machine
- Check if you can visit the page using your browser on your host machine
- Edit the default html file to show your name instead, or in addition to the default text
- Check out what kind of options does the "ls" command have, experiment with it.
- Is "ls" already an "alias" in your computer?
- Read the manual of ls by typing in "man ls" You can use space to advanced in the manual. You can use "q" to quit.
- Once you find a flag combination that you like create an alias so whenever you type a single "l" it will execute "ls" with your favorite flags.
- Make the above alias permanent in your shell. (Try by logging in in another putty session and check if the new l alias is available there too!)
- Create a file called workshop.txt add some notes to it.
So far is basically the same as we had during the workshop. Use the slides as your guide.
Additional tasks
- Create a new file called a.txt with "hello" in it.
- Create a new file called b.txt with "world" in it.
- Rename a.txt to be b.txt (using mv) observe that Linux does not ask for confirmation when you overwrite a file.
- Find out how to convince mv to ask for permission before overwriting a file.
- Try the above with "cp" that stand for copy.
- Create a sub-directory in your home directory called "web".
- Inside the "web" directory create a file called "index.html" with some content.
- Find out where where is the default configuration file of nginx (hint: it was in the slides) and change it so that the default directory will be the "web" sub-directory of home directory. (how do you know the full path to the "web" directory?
- reload the configuration file of Nginx by running
sudo service nginx reload
. - Reload your browser and check if the new page is from the "web" directory.
- If you managed to do this, you can now create additional files in this directory without root privileges.
Implement the wc command of Linux/Unix (word count)
A sample execution of wc
looks like this:
$ wc *
11 34 249 README.pod
2 4 128 authors.txt
37 110 773 check_examples.pl
wc: examples: read: Is a directory
2737 2738 27627 python_weekly.pickle
wc: sites: read: Is a directory
9 15 149 sites.yml
wc: static: read: Is a directory
2796 2901 28926 total
That is. Given a list of things on the command line it counts the number of "lines", "words", and "characters" for each file printing them in 3 columns (in that order) followed by the name of the file.
At the end it will print the totals of each column.
If it encounters something that is not a file (e.g. a directory) it prints a warning and goes on.
In our case there were 3 directories 'examples', 'sites', and 'static'.
Optionally allow the user to supply any of the 3 flags: -l
to print the line count, -w
to print the word count, or -c
to print the charater count.
By default it prints all 3.
If no input file is provided, wc
will work on the content arriving on the Standard Input. This means we can write this:
$ find . | wc -l
and get back the number of file in the directory tree starting in our current working directory.
Suggestion
Count words in a file is a simpler exercise. Solve that first!
Tools - Perl 5
- Perl 5: Open file and read content
- Perl 5: @ARGV
- Perl 5: warn
- Printing to STDERR
- Create functions
- Check file type (file, directory, etc)
Tools - Ruby
- Ruby: Open file and read content in Ruby
- Ruby: ARGV command line parameters
- Printing to Standard Error
Solutions
next
next
Send plain text e-mail
Create a function that will get one or more addresses where To
send the message,
0 or more address to Cc
the message. A single addres to pretend as the From
field.
Get a string to be the Subject
line and some plain text to be the body of the e-mail.
By default this should assume to be running on a Linux/Unix system with a properly configured sendmail program and use that as the underlying transporting system.
Then also add an optional parameter SMTP
that holds the name or IP address of an
SMTP server.
When this parameter is given, use the SMTP server to send the e-mail.
Send HTML e-mail
Extend your solution for sending plain text e-mail by allowing the user to provide both a plain text version of the e-mail and an HTML` version of it and send the e-mail that will contain both.
Send e-mail with attachment
Extend your solution for sending plain text e-mail by allowing the user to provide a list of filename and send the e-mail with the given files included as attachments.
next
Hello World for Web applications
The first exercise for building web applications.
Write a "web application" that will display Hello World. The first one should be a plain HTML file without any code behind it.
Before going on do the above and then see the plain HTML solution.
Then pick your favorite back-end web development environment and write some code that will return the same HTML created earlier. Each soultion should have several versions:
- Just return the string "Hello World!".
- Return the HTML 5 version of "Hello World!"
- Write version in which there is a template with a placeholder for "text" and the code fills that placeholder with the string "Hello World!".
Tools
Solutions
- Hello World! in plain HTML
- Node.js: Getting Started with Node.js
- Python: Hello world with Flask and Python
- Perl 6: Getting started with Bailador
- Perl 5: Hello World with plain CGI
- Perl 5: Getting started with PSGI
- Perl 5: Hello World with Plack in CGI mode
- Perl 5: Hello World with Dancer2
- Ruby: Hello World using CGI
Echo Web application
Display a form and a button. After the user types in some text and clicks on the button, show "You said: " and the text typed in by the user. Two solutions: one with GET, the other one with POST.
Solutions
- Plain HTML
- Plain JavaScript
- Node.js (part 1)
- Python: Flask
- Perl 6: Bailador
- Perl 5: Echo with plain CGI
- Perl 5: Getting started with Perl Dancer - Creating an Echo application
- Perl 5: Echo with Plack in CGI mode
- Ruby: echo with CGI