CSCI 344
Intermediate Bash Scripts


Write the following bash scripts.  You may use the Internet as a reference for bash script syntax and as a reference for UNIX/Linux commands (such as wc and tail), but you may not search for solutions to these problems--that would completely defeat the purpose of doing these exercises.

Here is a link to the O'Reilly's Learning Bash.  Note: this link only works from computers in the csuchico.edu domain and only works sometimes (maybe it has to do with the phase of the moon).

Unlike the previous Bash assignment, you may use any UNIX/Linux command or utility to solve these problems.



1)  (weight 1) Write a script that looks through all the directories in $PATH and finds all matches to the given command.  Then prompt the user using a select statement for which one of these commands should be run:

$ run ls
1) ./ls
2) /bin/ls
run command? 2
lines  ls  prompt  run  src
$

Hint:
Use your answer for question 1 in the basic_bash_scripts assignment as a starting point.  Instead of printing all the matches, put them into a string and pass this string to the select statement.

When checking to see if a variable is null [ -z $cmds ]  if $cmds has the form "/bin/ls:/usr/bin/ls" and the IFS=:, the [ ] will complain that you have too many arguments.  You can fix this by quoting the $cmds: [ -z "$cmds" ].


2) (weight 1) Write a version of cat using the bash read command.  If the user specifies a valid file on the command line (it is a regular file and you can read it) use that file.  If not, prompt the user for a valid file (echo -n does not print the newline).  Keep prompting the user until a valid file is entered.

$ mycat
enter valid file? asdf
enter valid file? qwer
enter valid file? z
one - first line
two
three
four
five - last line
$


Hints:

A string can be read into the variable filename using the following syntax:

read filename
Assuming that the variable filename contains the name of a valid file, the entire file can be read one line at a time using the following syntax:

while read line; do
    echo $line
done < $filename

The read line reads the next line of the file into the variable line.  The "< $filename" redirects the input of the while loop to be the file named by the variable filename.



3) (weight 4) Write a script that counts the lines of all files in a given directory and reports the total by extension.  

$ lines ~/src/gpl
cpp: 4196
h: 3959
l: 192
y: 1561
$


Ignore files that do not have an extension (that is, they don't have a .).  

If a file has the form   <string1>.<string2>. ... .<stringN> assume that stringN is the extension.

If the argument is not a directory or if no argument is given, issue the following message and exit with an error status

$ lines foo
Usage: lines directory
$


Hints:

The command wc counts the lines in the given files.  When passing multiple files to wc the last line contains a total.  When passing one file to wc, the last line can also be thought of as the "total."  The command tail can help you with this.

In my solution I first find all the different extensions in the directory.  Once you know all the extensions you can use wc $dir/*.$cur_extension to count all the files.

When checking to see if a variable is null [ -z $extensions ]  if $extensions has the form "cpp:h:y" and the IFS=:, the [ ] will complain that you have too many arguments.  You can fix this by quoting the $expressions: [ -z "$extensions" ].

In lab I suggested you could use grep to find if you already have seen the pattern.  Since egrep recognizes the "|" for or, it is a better choice.  However, it is difficult to get it to work.  After an hour I gave up.  It is much easier to use a loop and loop through the extensions you have already seen than to use egrep.  Don't agree?  try a directory with a.cpp and b.pp and c.p and d.c.