Solutions
Solutions
(block course)
Michael F. Herbst
[email protected]
http://blog.mfhs.eu
where the first one should be preferred, since it does the counting already
in grep. This means that we need to call one program less ⇒ Usually
better for performance.
• The options do the following:
-A n Add n lines of input after each matching line
-B n Add n lines of input before each matching line
-n Print line numbers next to each matching input line as
well
-H Print file name next to each matching input line as well
-w A line only is displayed if exactly the keyword exists. Usu-
ally it is sufficient if the search string is contained in the
line only.
2
• We can use the -v flag of grep in order to invert the result, i.e. now all
non-matching lines are printed:
1 < resources / matrices /3. mtx grep -v %
• Use cut to extract the third field and sort -u to get a sorted list of the
values with all duplicates removed. Now piping this to wc -l gives the
number of lines in the output of sort -u, i.e. the number of distinct
values:
1 < resources / matrices /3. mtx grep -v % | cut -d " " -f .
,→3 | sort -u | wc -l
• Now we need sort without the -u. We get the smallest as the first in the
sorted output:
1 < resources / matrices /3. mtx grep -v % | cut -d " " -f 3.
,→ | sort | head - n1
gives the result 0. Looking at the file we realise, that there is actually
another, negative value, which should be displayed here. The problem is
that sed does lexicographic ordering by default. To force it into numeric
ordering, we need the flag -n. The correct result is displayed with
1 < resources / matrices / bcsstm01 . mtx grep -v % | cut -d ".
,→ " -f 3 | sort -n | head - n1
• Running
1 < resources / matrices / lund_b . mtx grep -v % | cut -d " ".
,→ -f 3 | sort -n | head - n1
gives an empty output. This happens since the file contains lines like
3
1 9 8 5.5952377000000 e +01
where there are two spaces used between 2nd and 3rd column. The prob-
lem is that cut splits data at each of the delimiter characters — <space>
in the case. In other words it considers the third field to be empty and will
take the data 5.5952377000000e+01 to be in field 4. For us this means
that there are empty lines present in the output of cut, which sort first
and are printed by head.
• Using awk, we would run
1 < resources / matrices / lund_b . mtx grep -v % | awk '{.
,→print $3 } ' | sort -n | head - n1
Solution 2.2 Using the same kind of diagrams as in the notes, we get
0
keyboard ls
2→ 0
1→ 0
1→0 1→0
grep test grep blue awk ...
2
2 2 1
terminal outfile
here both stdin and stderr of some_program reach tee and get subse-
quently filtered.
• Each time the program executes, both tee as well as the normal output
redirector > will cause the logfiles to be filled from scratch with the output
from the current program run. In other words all logging from the previous
executions is lost.
We can prevent this from happening using the -a (append) flag for tee
and the redirector >>. Hence we should run
1 some_program |& tee -a log . full | grep keyword >> log ..
,→summary
• Running < in cat > out is exactly like copying the file in to out as
mentioned before.
• Running < in cat > in gives rise to the in file to be empty.
This is because the shell actually opens the file handles to read/write
data before calling the program for which input or output redirection was
requested. This means that in fact the file handle to write the output to
in is already opened before cat is called and hence in is already at the
time cat looks at it (because the non-appending output file handle deletes
everything). Overall therefore no data can be read from in and thus the
in file is empty after execution.
• stdin is connected to the keyboard, stdout and stderr are connected to the
terminal. Therefore everything we type(stdin of cat) is copied verbatim
to the terminal (stdout of cat). The shell just seems to “hang” because
cat waits for our input via the keyboard and thus blocks the execution of
further commands.
Solution 2.5
• Since the return code of the commands to the left of && or || determine if
the command to the right is executed, we best look at the command list
left-to-right as well:
– The directory 3/3 does not exist and hence the first cd gives return
code 1.
– Therefore cd 4/2 is executed (|| executes following command if pre-
ceding command has non-zero return code)
– The command list to the left of the first &&, i.e. cd 3/3 || cd 4/2
has return code 0 (the last command executed was cd 4/2, which
succeeded)
– Hence cd ../4 is executed which fails since the directory 4/4 does
not exist below resources/directories
– In other words the list cd 3/3 || cd 4/2 && cd ../4 has return
code 1 and thus cd ../3 gets executed
5
– This succeeds and thus the command to the right of && is executed,
i.e. we cat the file
• We need to suppress the error messages of the failing cd commands. These
are cd 3/3 and cd ../4. In other words the shortest commandline would
be
1 cd 3/3 2 >/ dev / null || cd 4/2 && cd ../4 >/ dev / null || .
,→cd ../3 && cat file
• We now first make the directory 3/3. So the execution changes slightly:
– cd 3/3 now succeeds and thus cd 4/2 is not executed; we are in
directory resources/directories/3/3.
– The last command executed was the succeeding cd 3/3, such that
the return code of the command list cd 3/3 || cd 4/2 is zero.
– We attempt to execute cd ../4, which fails as the dir resources/
directories/3/4 does not exist.
– Hence we execute cd ../3, which is successful and “changes” the
directory to resources/directories/3/3.
– Finally the pwd command is also executed, since cd ../3 was suc-
cessful.
Solution 2.6
• true is a program that — without producing any output — always ter-
minates with return code 0.
• false is a program that produces no output and always terminates with
return code 1.
For the first set of commands the exit code is
• 0, since false returns 1 and hence true is executed, which returns 0.
• 0, since true triggers the execution of false, which in turn triggers the
execution of true
• 1, since false returns 1, so nothing else is executed and the return code
is 1.
• 0, since false causes true to be executed, which returns 0. So the final
false is not executed and the return code is 0.
Running the commandlines in the shell, we get
• 0
• 0
• 1
• 1
• 0
In a pipe sequence, the return code is solely determined by the last command
executed. In other words the return code of all other commands in the pipe is
lost.
6
This code executes a lot quicker, since grep can use a different algorithm
for the search: Once it found a single match, it can quit the search and
return 0.
word output
Holmes 460
a 2287
Baker 42
it 1209
room 168
• A usual pipe: The output of echo test on stdout, i.e. “test” gets piped
into grep test, which filters for the string “test”. Since this string is
contained in echo’s output, we see it on the terminal and the return code
is 0
• Recall that & sends the command to its LHS into the background. So
the echo happens, which we see on the screen. At the same time grep
test is executed, which does not have its stdin connected to a file or
another program’s stdout. In other words it has its stdin connected to the
keyboard and it waits for the user to input data (The terminal “hangs”.).
Depending on what we type (or if we type anything at all) the return code
of grep is different.
• A pipe where both the stdout as well as the stderr are piped to grep. The
effect is the same as in the first example, since echo produces no output
on stderr. I.e. we get “test” on the terminal and return code 0.
7
• We print once again “test” onto the terminal by executing echo test
. Since this is successful (zero return code) grep test is also executed.
Similar to the second case, stdin of grep is connected to the keyboard
and waits for user input. The exit code of grep — and hence the whole
commandline — depends on what is typed.
• The echo test prints “test” onto the terminal and since this is successful,
nothing else happens.
Solution 3.4 Since cat takes data on stdin and copies it to stdout without
modification, we can cache all data a script gets on stdin in a variable CACHE
using the simple line
1 CACHE = $ ( cat )
Once this has been achieved we just use echo to access this data again and grep
inside it:
1 # !/ bin / bash
2
Solution 3.8 Just applying the guidelines introduced in the script we can
identify the following problems:
• Line 5, cd Top Dir: The directory Top Dir is not quoted, which is why
cd will enter the directory Dir instead of what was intended.
• Line 6, grep $1: The positional parameter may contain spaces. Therefore
$1 should be quoted.
• Line 9, grep -H $1 and grep -H $2: Again both positional parameters
should be quoted.
• Line 10, echo '$FILE': Use double quotes in order to get parameter
substitution working.
• Line 10, wc -l: Better use grep -c ^ (see next exercise)
• Line 14: For the same reason single quotes should be used.
• Line 16, echo $FILE: FILE may contain newline characters, which we
want to keep when printing it. onto the terminal. Therefore this variable
should be quoted as well, i.e. it is better to use echo "$FILE".
The corrected script is
1 # !/ bin / bash
2 # script to extract some information from directories
3 # $1 : additional keyword to search for
4 #
5 cd " Top Dir "
6 ADDITIONAL = $ ( < output grep " $1 " )
7 IMPORTANT = $ ( < output grep -i important )
8 cd Lower
9 FILE = $ ( < out1 grep -H " $1 " ; < out2 grep -H " $2 " )
10 COUNT = $ ( echo " $FILE " | grep -c ^)
11
12 echo results :
13 echo " important messages : " $IMPORTANT
14 echo " other messages : $ADDITIONAL "
15 echo we found $COUNT more findings in
16 echo " $FILE "
3 simple scripts/sol/ex quoting.sh
9
Solution 3.11 The solution makes use of the fact that grep -n separates
the line number and the text of the matching line by a “:”. So by sending
the output of grep to a cut -d: -f1 we can just extract the numbers of the
matching lines.
1 # !/ bin / bash
2 # $1 : filename
3 # $2 : keyword
4 # $3 : keyword
5 # $4 : keyword
6
Solution 4.1 Here we use the positional parameters $1 to $3 and the [ com-
mand in order to achieve our goal:
1 # !/ bin / bash
2
11
Solution 4.2 We use test to determine the file type and take appropriate
action:
1 # !/ bin / bash
2
9 # $1 is a directory :
10 [ -d " $1 " ] && cd " $1 "
4 control io/sol/test file.sh
Solution 4.4 The solution requires two nested while loops. We use ((ICNT
++)) and ((JCNT++)) to increase the counter variables ICNT and JCNT by one
in each iteration.
1 # !/ bin / bash
2
23 while [ $ICNT - le $I ]; do
24 # create directory ICNT
25 mkdir $ICNT
26
18 fi
19
27 N = $FROMNUMBER
28 while [ $N - lt $TONUMBER ]; do
29 echo $N
30 (( N ++) )
31 done
32 echo $N
4 control io/sol/seq2.sh
If the 3-argument version should be supported as well we arrive at:
1 # !/ bin / bash
2
10 # - - - -- - - - -- - -
11 # checking :
12
37 STEP =1
38 fi
39
53 # -------------
54 # do the seq :
55
56 N = $FROMNUMBER
57 while [ $N - lt $TONUMBER ]; do
58 echo $N
59
16 # -------------------------------------------------------
17
Solution 4.7 One way to achieve the required substitutions is to exploit word
splitting. Recall that word splitting takes place at all <tab>, <newline> or
<space> characters.
• For the first part we need a commandline that performs word splitting on
the content of VAR and inserts a <newline> between each word. This can
be done e.g. by executing
1 for i in $VAR ;
2 echo $i
3 done
• For the second part we need to insert a <space> between all the words
after word splitting. This is the effect of
1 echo $VAR
10 exit 0
11 ;;
12 -q | - - quiet )
13 QUIET =1
14 ;;
15 -f )
16 shift
17 FILE = " $1 "
18 ;;
19 *)
20 echo " Unknown argument : $1 " >&2
21 exit 1
22 esac
23 shift
24 done
25
Solution 4.12 We use a few calls to read in order to read single lines from
the script’s stdin. Then we output the relevant lines to stdout or stderr.
1 # !/ bin / bash
2 # read first line of stdin
3 read line
4
Solution 4.13 We alter the previous script in order to get the following:
1 # !/ bin / bash
2
The bottom line is: If you want to be able to use the stdin of your script as
a means to pass data to it, do not ask the user for parameters with read. Use
commandline arguments instead.
Solution 4.14 The final version of the script could look like this
1 # !/ bin / bash
2
31 if [ " $TO " != " end " ] && [ " $TO " - le " $FROM " ]; then
32 echo " The -- to line (= $TO ) needs to be larger that the .
,→-- from line (= $FROM ) . " >&2
33 exit 1
34 fi
35
36 # line count
37 LC =0
38
64 elif [ " $TO " != " end " ] && [ $LC - eq $TO ]; then
65 # check first if we have something in the cache :
66
107 exit 0
4 control io/sol/swap lines general.sh
we realise that it can deal with the multiple spaces which are used in some lines
to separate the columns. In other words, compared to cut it gives the correct
result when the third column of the mtx files is to be extracted.
Solution 4.17 We can achieve exactly what is asked for in a bash three-liner:
1 # !/ bin / bash
2
Solution 4.18 Since the directories are separated by a “:” in PATH, a good
IFS to use is :.
21
1 # !/ bin / bash
2
If we look at the 3 fields of the C-like for loop, we see that A is initialised
to 99 and C to 100. After each iteration C gets increased by one and A gets
decreased by one. The iteration stops if C%A-3 is equal to 0 (C-false), i.e.
if
C%A = 3
This is the case after the first iteration, since this gives C equal to 101 and
A equal to 99.
Now we know that the loop body ((B=(B+1)%2)) is only executed once.
Since B has not been set, it defaults to zero. Executing the statement
under arithmetic evaluation hence assigns B with
(B + 1)%2 = 1%2 = 1
which is not C-false. Therefore the final ((B)) returns 0, which is also the
return code of the whole expression.
• 1: ((B=1001%10)) gives rise to no output, such that the first statement
1 (( B =1001%10) ) | grep 4
Solution 5.2 If one runs the code provided here on the shell, one realises,
that for proper integer numbers the result of echo $((A+0)) and the result of
echo $A is identical. Exactly this behaviour was used in the following script:
1 # !/ bin / bash
2
31 # if C is a prime , print it
32 (( isprime ==1 ) ) && echo $C
33 done
5 variables/sol/primes.sh
24
Solution 5.4 The first version making use of a temporary file can be achieved
like this
1 # !/ bin / bash
2
If we want to omit the reading and writing to/from disk, we have to do every-
thing in one pipe. One solution for this could be
1 # !/ bin / bash
2
Solution 5.5 We first parse the arguments and check whether there is any-
thing to do (if there are no numbers supplied, we are done). Then we build up
the expression for bc in BCLINE and echo it to bc to get the result.
1 # !/ bin / bash
2
50 # calculate it with bc
51 # and print result to stdout
52 echo " $BCLINE " | bc -l
53 exit $ ?
5 variables/sol/sum mean.sh
Solution 5.6 We have to take care to exclude both the first comment line as
well as the first non-comment line from being manipulated at all. Apart from
these lines all other, however, have to be touched. This script uses a so-called
firstrun flag and as well as the while read line paradigm to achieve this:
1 # !/ bin / bash
2
3 NUM = $1
4
3 # initial note :
4 # this script is deliberately made cumbersome
28
7 # keyword
8 KEYWORD = $ {1: -0000}
9
10 ERROR =0
11 [ ! -f " bash_course . pdf " ] && {
12 echo " Please run at the top of the bash_course repository.
,→" >&2
13 ERROR =1
14 }
15
23 [ $ERROR - eq 1 ] && (
24 echo " A fatal error occurred "
25 exit 1
26 )
27
46 if [ $COUNT - gt 0 ]; then
47 echo " We found $COUNT matches ! "
48 exit 0
49 else
50 echo " No match " >&2
51 exit 1
52 fi
6 functions subshells/sol/subshell exercise corrected.sh
29
Solution 6.2 The key point here is to use a subshell in order to keep track of
the temporary change of the IFS variable
1 # !/ bin / bash
2
Solution 6.3 We use the function list_files that deals with a directory
and all subdirectories recursively. A little care has to be taken when printing
the paths such that the “/” appears at the right places.
1 # !/ bin / bash
2
3 list_files () {
4 # $1 : prefix to append when listing the files
5
Solution 6.4 Instead of using one single line with all commands, we use func-
tions to split the tasks up into logical parts and name these parts sensibly.
1 # !/ bin / bash
2
9 calculate_xi () {
10 # analyse the output from book_parse . sh
11 # calculate the xi values and print a table
12 # of xi values followed by a tab and the filename to .
,→stdout
13
26 filter_3_largest () {
27 # filter the output of calculate_xi such that only the 3
28 # books with the largest xi values are passed from stdin
29 # to stdout
30
35 print_results () {
36 # Take a table in the format produced by calculate_xi and
31
44 # ---------------------------------------------------------
45
Solution 6.5 After the subtract operation has been implemented as well, we
arrive at
1 # !/ bin / bash
2
7 # --------------------------------------------------
8
9 add () {
10 # add two numbers
11 # $1 : first number
12 # $2 : second number
13 # echos result on stdout
14 echo $ (( $1 + $2 ) )
15 }
16
17 multiply () {
18 # multiply two numbers
19 # $1 : first number
20 # $2 : second number
21 # echos result on stdout
22 echo $ (( $1 * $2 ) )
23 }
24
25 subtract () {
26 # Subtract two numbers
27 # $1 : first number
28 # $2 : number subtracted from first number
29 # echos result on stdout
30 echo $ (( $1 - $2 ) )
31 }
32
33 operation () {
34 # selects for add or multiply depending on
35 # SEL
36 # $1 : first operand for operator ( add or multiply )
37 # $2 : second operand for operator ( add or multiply )
32
48 calculate3 () {
49 # it calls operation with 3 and $1
50 # such that we either add , subtract or multiply (.
,→depending on SEL ) 3 and $1
51 # echos the result on stdout
52
53 operation $1 3
54 }
55
56 map () {
57 # $1 : a command
58
59 local COMMAND = $1
60 shift
61
70 usage () {
71 echo " $0 [ -h | -- help | -- add3 | -- multiply3 ] <.
,→arguments > "
72 echo " Script to do some operation to all arguments "
73 echo
74 echo " Options : "
75 echo " -- add3 adds 3 to all arguments "
76 echo " -- multiply3 multiplies 3 to all arguments "
77 echo " -- subtract3 subtracts 3 from all arguments "
78 }
79
80 # --------------------------------------------------
81
82 # $1 selects method
83
90 SEL = add
91 ;;
92 -- multiply3 )
93 SEL = multiply
94 ;;
95 -- subtract3 )
96 SEL = subtract
97 ;;
98 *)
99 echo " Unknown argument : \ " $1 \ " " >&2
100 echo " Usage : " >&2
101 usage >&2
102 exit 1
103 esac
104
It takes very little effort to add extra operators, since the script only needs to
be changed at two places: We need to add the function and we need to add an
extra case in order to get SEL set accordingly.
One could go even further: The functions add, multiply and subtract are
very similar. So one could use the tool eval in order to write a generating
function which automatically defines these aforementioned functions. Then we
arrive at
1 # !/ bin / bash
2
7 # --------------------------------------------------
8
9 generator () {
10 # function to generate a function that takes
11 # two numbers and echos the result of applying
12 # an operation to these numbers on stdout
13 #
14 # $1 : name of the function to generate
15 # $2 : operator to use in the operation
16
17 eval " $1 () {
18 echo \ $ ((\ $1$2 \ $2 ) )
19
20 }"
21 }
22
34
27 operation () {
28 # selects for add or multiply depending on
29 # SEL
30 # $1 : first operand for operator ( add or multiply )
31 # $2 : second operand for operator ( add or multiply )
32 # echos the result on stdout
33
42 calculate3 () {
43 # it calls operation with 3 and $1
44 # such that we either add , subtract or multiply (.
,→depending on SEL ) 3 and $1
45 # echos the result on stdout
46
47 operation $1 3
48 }
49
50 map () {
51 # $1 : a command
52
53 local COMMAND = $1
54 shift
55
64 usage () {
65 echo " $0 [ -h | -- help | -- add3 | -- multiply3 ] <.
,→arguments > "
66 echo " Script to do some operation to all arguments "
67 echo
68 echo " Options : "
69 echo " -- add3 adds 3 to all arguments "
70 echo " -- multiply3 multiplies 3 to all arguments "
71 echo " -- subtract3 subtracts 3 from all arguments "
72 }
73
74 # --------------------------------------------------
35
75
76 # $1 selects method
77
Note, however, that eval is a dangerous command and should never be used
on anything that contains data, which the user of your script can set. In other
words: Only use it if you know what it does and how it works!
Solution 6.6 In order to make the script from the other exercise sourcable,
we just need to insert the code
1 return 0 & >/ dev / null
before the case statement, e.g. in line 80 (of the version not using the generator
). The script, which is sourcable, can be found in 6_functions_subshells/sol
/functional_sourcable.sh. Note that it still can be executed normally and
runs as expected.
5 echo " This script only works if executed from the top .
,→directory " >&2
6 echo " of the tarball containing the solution scripts . " .
,→ >&2
7 echo " Please change the working directory accordingly and.
,→" >&2
8 echo " execute again . " >&2
9 exit 1
10 fi
11
Due to the relative path to the sourced script we used in this modified ver-
sion of 6_functions_subshells/source_exercise.sh, the script only works
if executed from the top directory of the tarball, which contains the solution
scripts.
• .. matches any string that contains any two character substring, i.e. any
string with two or more letters. This is everything except g and the empty
string.
• ^..$ matches a string with exactly two characters, i.e. ab and 67.
• [a-e] matches any string that contains at least one of the characters a to
e, i.e. ab and 7b7.
• ^.7*$ matches any string which starts with an arbitrary character and
then has zero or more 7s following. This is g, 67, 67777, 7777 and 77777.
• ^(.7)*$ matches any string which has zero or more consecutive substrings
consisting of an arbitrary character and a 7. This is 67, o7x7g7, 7777 and
the empty string. Note that e.g. 77777 does not match: If we “use” the
pattern .7 three times we get ^.7.7.7$ and 77777 has one character too
little to be a match for this.
Solution 7.3
a) ab*c or c$
b) ab+c or bc$
c) ^a.*c or c$
d) ^ *q or q..
e) ^a|w or ....
Solution 7.4
is just the list of all digits which are contained in the file resources/
digitfile in exactly the order they occur.
• We can run
1 < resources / digitfile grep -o '[0 -9] ' | sort -n | uniq.
,→ -c
we can easily verify that the proposed pattern gives indeed the values in the
third column. As usually we get the largest of these values by piping the result
to sort -r -n | head -n1:
1 < resources / matrices / bcsstm01 . mtx grep -o -E -e .
,→ ' -?[0 -9]\.[0 -9]* e [+ -][0 -9][0 -9] ' | sort -r -n | head .
,→- n1
38
Solution 7.5 The whole problem can be solved using the command lines
1 < resources / chem_output / qchem . out head - n48 > file
2 < file sed -r '/Q - Chem / d ; s /[ A - Z ]\. -?// g ; s / ,/\ n /g ' | sed '.
,→s /^[[: space :]]*//; /^ $ /d ' | sort
sed processes all rules for each line going from top to bottom, right to left.
So even though we introduce new line breaks by the substitution, sed considers
the resulting string still as a logical line and all regexes are applied to the logical
line instead of the actual lines. Using such the procedure, which was suggested
by the exercise, we cannot deal with this in any other way but piping it to
another sed, which now honours the new line breaks.
Note that a careful inspection of the problem reveals that the one-liner
1 < file sed -r '/Q - Chem |^ $ / d ; s /( *[ A - Z ]\. -? ?| , * $ ) // g ; .
,→s / ,/\ n /g '
does the trick as well, just using a single sed.
8 < " $1 " awk '{ print $2 " " $3 " " $2 + $3 } '
8 awk/sol/print add.sh
39
it prints the correct results, thus dealing well with the multiple separators in
resources/matrices/lund_b.mtx.
Solution 8.3 We use echo in order to transfer the numbers to awk, let awk
do the computation and print the result on stdout (straight from awk itself):
1 # !/ bin / bash
2
7 # --------------------------------------------------
8
9 add () {
10 # add two numbers
11 # $1 : first number
12 # $2 : second number
13 # echos result on stdout
14 echo " $1 $2 " | awk '{ print $1 + $2 } '
15 }
16
17 multiply () {
18 # multiply two numbers
19 # $1 : first number
20 # $2 : second number
21 # echos result on stdout
22 echo " $1 $2 " | awk '{ print $1 * $2 } '
23 }
24
25 subtract () {
26 # Subtract two numbers
27 # $1 : first number
28 # $2 : number subtracted from first number
29 # echos result on stdout
30 echo " $1 $2 " | awk '{ print $1 - $2 } '
31 }
32
33 operation () {
34 # selects for add or multiply depending on
35 # SEL
36 # $1 : first operand for operator ( add or multiply )
37 # $2 : second operand for operator ( add or multiply )
38 # echos the result on stdout
39
43 local FIRST = $1
44 local SECOND = $2
45 $SEL $FIRST $SECOND
46 }
47
48 calculate3 () {
49 # it calls operation with 3 and $1
50 # such that we either add , subtract or multiply (.
,→depending on SEL ) 3 and $1
51 # echos the result on stdout
52
53 operation $1 3
54 }
55
56 map () {
57 # $1 : a command
58
59 local COMMAND = $1
60 shift
61
70 usage () {
71 echo " $0 [ -h | -- help | -- add3 | -- multiply3 ] <.
,→arguments > "
72 echo " Script to do some operation to all arguments "
73 echo
74 echo " Options : "
75 echo " -- add3 adds 3 to all arguments "
76 echo " -- multiply3 multiplies 3 to all arguments "
77 echo " -- subtract3 subtracts 3 from all arguments "
78 }
79
80 # --------------------------------------------------
81 # make script sourcable :
82 return 0 & >/ dev / null
83 # --------------------------------------------------
84
85 # $1 selects method
86
95 -- multiply3 )
96 SEL = multiply
97 ;;
98 -- subtract3 )
99 SEL = subtract
100 ;;
101 *)
102 echo " Unknown argument : \ " $1 \ " " >&2
103 echo " Usage : " >&2
104 usage >&2
105 exit 1
106 esac
107
Solution 8.4 The BEGIN rule initialises the variable c to zero, which is the
default anyway. Therefore it can be omitted.
1 # !/ bin / bash
2 awk '
3 # BEGIN { lines =0 }
4 { lines = lines +1 }
5 END { print lines }
6 '
8 awk/sol/awk wc.sh
20 '
8 awk/sol/davidson extract.sh
13 # if we find the " Term symbol " line inside the block , we .
,→store
14 # the term symbol which sits in $3 $4 and $5
15 inside_block ==1 && /^ * Term symbol / { term_symbol = $3 " " .
,→$4 " " $5 }
16
26 inside_block =0
27 }
28 '
8 awk/sol/exstates extract.sh
44