Session 7 · Computer Systems
Practice tasks: from input to output file
Six tasks in the style of the practical test, from reading a number to finding the most frequent visitor in a CSV-like file, each with a hint and an expected result.
This session is a practice set in the style of the practical test: six tasks, from a two-line warm-up to a small data-processing script. Nothing new is introduced – everything here was in the previous three sessions. The point is to get fast and reliable at the routine: argument checks, the safe loop, a pattern, an output.
Work through them in order and time yourself. Open a hint only after you have a first version that runs; every task has an expected result so you can check yourself.
Read the statement twice. "Input value, not a command-line argument" and "the file name comes as an argument" are different scripts. Most lost points in a practical test come from solving a slightly different task.
The routine
For every task from 2 on, the same steps, in this order:
- Check the number of arguments; print a usage line to standard error and
exit 1. - Check that the input file exists (
-f); if not, an error message andexit 1. - If there is an output file, empty it once before the loop.
- Paste the universal loop:
IFS= read -r line || [ -n "$line" ], strip\r, skip empty lines. - Write only the processing step – the part that differs between tasks.
- Run it, compare with the expected result, look at the input with
cat -Aif they differ.
steps 1–2
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <input_file> <output_file>" >&2
exit 1
fi
if [ ! -f "$1" ]; then
echo "Error: file '$1' does not exist" >&2
exit 1
fiThe tasks
Task 1
Square of a number
Read a number as input – with read, not as a command-line argument – and print its square. You may assume the input is valid.
Number: 2
2^2 = 2 × 2 = 4Approach
read -p prints the prompt and reads in one step. The arithmetic is a single $(( … )); build the output line around it.
read -r -p "Number: " n # from the keyboard – not $1Task 2
Copy the lines that contain a word
Copy every line containing ZH from one file to another. Both file names are command-line arguments: the source, then the file to create. If the source doesn't exist, tell the user. Input: 2.txt; expected: 2 lines in the new file.
Approach
A glob test is enough – no regex needed for "contains". Empty the output before the loop, append inside it. One-line alternative: grep ZH "$1" > "$2".
> "$output_file"
...
if [[ $line == *ZH* ]]; then
echo "$line" >> "$output_file"
fiTask 3 looks like a one-liner, but the line's real last character is not always the one you see. Depending on where the file was made, there can be spaces or a \r after the last digit (Fig. 2).
sp = a space.Task 3
Lines that end in a number
Print the lines of a file (argument) that end in a digit. Check that the file exists. Input: 3.txt; expected: 2 lines.
Approach
Delete every whitespace character from a copy of the line and test that copy with an end-anchored pattern. tr -d '[:space:]' also removes the spaces between words – harmless here, since only the last character matters, but a reason not to print the copy.
clean=$(echo -e "$line" | tr -d '[:space:]')
if [[ $clean =~ [0-9]$ ]]; then
echo "$line" # print the original, not the cleaned copy
fiTask 4
Count the numeric lines
Count the lines of a file (argument) that contain only a number – think of real numbers too: sign, decimal point. Print the count. Input: 4.txt; expected: 5.
Approach
The decimal pattern from the text processing session, anchored on both ends. 420, 1222, 123, 12.2 and -12.3 count; asd2, dsa3 and a sentence that merely contains 23 do not.
if [[ $line =~ ^[+-]?[0-9]*\.?[0-9]+$ ]]; then
count=$(( count + 1 ))
fiTask 5 sends each number one of two ways and produces two results (Fig. 3).
Task 5
Even sum, odd numbers to a file
From a file (argument), add up the even numbers and print the sum (expected: 12). Copy the odd numbers to a new file, OddNumbers.txt, add them up too, and write the sum as the last line: Sum of copied odd numbers: 24. The 24 must come from a variable, not be typed in. Input: 5.txt.
Approach
Two sums, one if on n % 2. The odd-sum line is appended after the loop, when the sum is final.
> OddNumbers.txt
...
if (( n % 2 == 0 )); then
even_sum=$(( even_sum + n ))
else
echo "$n" >> OddNumbers.txt
odd_sum=$(( odd_sum + n ))
fi
...
echo "Sum of copied odd numbers: $odd_sum" >> OddNumbers.txtTask 6
The most frequent visitor
A GP practice keeps its patient register as plain text: Name, visits, date of last visit. Print the name of the person who visited most often, and how many times. Check that the file exists. Input: 6.txt; expected: Kiss Lajos – 12 visits.
Approach
Split each line at the commas with a here string. The number has a space in front of it – clean it before comparing, or -gt complains. Keep the best name and count so far.
IFS=',' read -r name visits date <<< "$line"
visits=$(echo "$visits" | tr -d '[:space:]')
if [ "$visits" -gt "$max" ]; then
max=$visits
best=$name
fiCheck yourself
| Task | Input | Expected |
|---|---|---|
| 1 | keyboard: 2 | 2^2 = 2 × 2 = 4 |
| 2 | 2.txt | 2 lines in the new file |
| 3 | 3.txt | 2 lines printed |
| 4 | 4.txt | 5 |
| 5 | 5.txt | 12 on the terminal; OddNumbers.txt: 3, 5, 7, 9 and the sum 24 |
| 6 | 6.txt | Kiss Lajos – 12 |
Summary
- Input (
read) and argument ($1) are different – the statement says which. - Argument count, file exists, empty the output – before any processing.
- Test a cleaned copy of the line; print the original.
- One loop can feed several results: a sum to the terminal and lines to a file.
- Check every script on a bad input too, not only on the sample file.