-
Book Overview & Buying
-
Table Of Contents
-
Feedback & Rating

Linux System Programming Techniques
By :

In this recipe, we will learn how to write a program that is pipe-friendly. It will take input from standard input and output the result on standard output. Any error messages are going to be printed on standard error.
We'll need the GCC compiler, GNU Make, and preferably the Bash shell for this recipe.
In this recipe, we are going to write a program that converts miles per hour into kilometers per hour. As a test, we are going to pipe data to it from a text file that contains measurements from a car trial run with average speeds. The text file is in miles per hour (mph), but we want them in kilometers per hour (kph) instead. Let's get started:
avg.txt
. This text will be used as the input for a program we will write. The text simulates measurement values from a car trial run:10-minute average: 61 mph 30-minute average: 55 mph 45-minute average: 54 mph 60-minute average: 52 mph 90-minute average: 52 mph 99-minute average: nn mph
mph-to-kph.c
, or download it from GitHub from https://github.com/PacktPublishing/Linux-System-Programming-Techniques/blob/master/ch2/mph-to-kph.c. This program will convert miles per hour into kilometers per hour. This conversion is performed in the printf()
statement:#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { char mph[10] = { 0 }; while(fgets(mph, sizeof(mph), stdin) != NULL) { /* Check if mph is numeric * (and do conversion) */ if( strspn(mph, "0123456789.-\n") == strlen(mph) ) { printf("%.1f\n", (atof(mph)*1.60934) ); } /* If mph is NOT numeric, print error * and return */ else { fprintf(stderr, "Found non-numeric" " value\n"); return 1; } } return 0; }
$> gcc mph-to-kph.c -o mph-to-kph
$> ./mph-to-kph 50 80.5 60 96.6 100 160.9 hello Found non-numeric value $> echo $? 1 $> ./mph-to-kph 50 80.5 Ctrl+D $> echo $? 0
awk
:$> cat avg.txt | awk '{ print $3 }' 61 55 54 52 52 nn
mph-to-kph
program at the end to convert the values:$> cat avg.txt | awk '{ print $3 }' | ./mph-to-kph 98.2 88.5 86.9 83.7 83.7 Found non-numeric value
nn
, a non-numeric value, which is an error in the measurement, we don't want to show the error message in the output. Therefore, we redirect stderr to /dev/null
. Note the parenthesis around the expression, before the redirect:$> (cat avg.txt | awk '{ print $3 }' | \ > ./mph-to-kph) 2> /dev/null 98.2 88.5 86.9 83.7 83.7
sed
to accomplish this:$> (cat avg.txt | awk '{ print $3 }' | \ > ./mph-to-kph) 2> /dev/null | sed 's/$/ km\/h/' 98.2 km/h 88.5 km/h 86.9 km/h 83.7 km/h 83.7 km/h
This program is similar to the one from the previous recipe. The features we added here check if the input data is numeric or not, and if it isn't, the program aborts with an error message that is printed to stderr. The regular output is still printed to stdout, as far as it goes without an error.
The program is only printing the numeric values, no other information. This makes it better as a filter, since the km/h text can be added by the user with other programs. That way, the program can be useful for many more scenarios that we haven't thought about.
The line where we check for numeric input might require some explanation:
if( strspn(mph, "0123456789.-\n") == strlen(mph) )
The strspn()
function only reads the characters that we specified in the second argument to the function and then returns the number of read characters. We can then compare the number of characters read by strspn()
with the entire length of the string, which we get with strlen()
. If those match, we know that every character is either numeric, a dot, a minus, or a newline. If they don't match, this means an illegal character was found in the string.
For strspn()
and strlen()
to work, we included string.h
. For atof()
to work, we included stdlib.h
.
In Step 5, we selected only the third field—the mph value—using the awk
program. The awk $3
variable means field number 3. Each field is a new word, separated by a space.
In Step 6, we redirected the output from the awk
program—the mph values—into our mph-to-kph
program. As a result, our program printed the km/h values on the screen.
In Step 7, we redirected the error messages to /dev/null
so that the output from the program is clean.
Finally, in Step 8, we added the text km/h after the kph values in the output. We did this by using the sed
program. The sed
program can look a bit cryptic, so let's break it down:
sed 's/$/ km\/h/'
This sed
script is similar to the previous ones we have seen. But this time, we substituted the end of the line with a $
sign instead of the beginning with ^
. So, what we did here is substitute the end of the line with the text "km/h". Note, though, that we needed to escape the slash in "km/h" with a backslash.
There's a lot of useful information about strlen()
and strspn()
in the respective manual pages. You can read them with man 3 strlen
and man 3 strspn
.