Skip to content

Instantly share code, notes, and snippets.

@stephennaicken
Last active February 14, 2019 07:44
Show Gist options
  • Save stephennaicken/cc0debbd18b2ebc2471227828d894946 to your computer and use it in GitHub Desktop.
Save stephennaicken/cc0debbd18b2ebc2471227828d894946 to your computer and use it in GitHub Desktop.
.TH FILEVIEWER 1
.SH NAME
fileViewer \- view files
.SH SYNOPSIS
.B fileViewer
[\fIOPTION\fR]... \fIFILE\fR
.SH DESCRIPTION
.B fileViewer
is a file viewer that will display the selected file to standard output or a if a directory, it will operate interactively. In interactive mode, for each file in the directory, the user will be asked if the file is to be displayed to standard output.
.SS OPTIONS
.TP
\-f \fIfile\fR
Output file to standard output
.TP
\-h
displays helps and exit
.SH EXAMPLES
.TP
View a file called index.html
.B fileViewer
\-f index.html
.TP
Enter interactive mode for a directory named test_dire
.B fileViewer
test_dir
.SH AUTHOR
Written by Stephen Naicken (snaicken at alueducation dot com)
.SH SEE ALSO
.BR cat (1)
#!/usr/bin/env bash
# Author: Stephen Naicken
# With the help of CS15!
# Student ID: Not a student! :D
# Declare a global variable NAME and assign it the String value
# Stephen. To access the value we use $NAME. No spaces around the =.
NAME="Stephen"
# The usage function display information as to how to use the script
usage(){
echo "Usage: fileViewer -[f file | h]"
}
# We didn't cover trap in class. Let's do that here. Whilst a Unix
# command (or rather its process) is running in the foreground, you
# can use Ctrl-C (hold Ctrl and press C) to send an interrupt to it.
# We usually use it to kill the process of the application. Here we
# are going to trap it
#
# For more on trap, checkout
# https://linuxconfig.org/how-to-modify-scripts-behavior-on-signals-using-bash-traps
trap trapCtrlC SIGINT
# Here we trap when the script ends regardless of the cause
trap trapEndScript EXIT
trapCtrlC(){
# You could do some tidying up, as we do with exception handling
# in Java
echo -e "\r\nYou hit Ctrl-C. What did I do to deserve this?!"
# exit and return status code 130 (Script terminated by Ctrl-C)
# https://www.tldp.org/LDP/abs/html/exitcodes.html
exit 130
}
trapEndScript(){
echo -e "\r\nGoodbye $NAME!"
}
# There is no equivalent to public static void main(String[] args)
# from Java in Bash. We write a function called main, which will be
# called when the script runs.
#
# Unlike in class, you'll notice I'm using inverted commas around
# variable $1. If value of $1 contains a space, by using "$1" we
# preserve whitespace. See the first example at
# https://www.tldp.org/LDP/abs/html/quotingvar.html.
#
# This method handles one argument, $1. Any additional argument is
# dropped.
main(){
# if $1 does not exist - careful with the spaces
if [[ ! -e "$1" ]]; then
echo "The argument does not exist. Please check it and try again."
# else if $1 is a directory
elif [[ -d "$1" ]]; then
handle_dirs "$1"
# else if $1 is a file and it is readable
elif [[ -f "$1" && -r "$1" ]]; then
read_file "$1"
fi # end the if-statement
}
# Our handle_dirs function is interactive. It will loop through all
# files in the directory given by the value of $1. At each iteration,
# we prompt the user to ask if she would like to display the file. If
# the user types n or N, then the file is skipped, if the user enters
# anything else, the file is displayed to the screen.
#
# Again, we only handle one argument, hence we only refer to $1 in the
# function.
handle_dirs(){
# First, we'll consider the "$1"/*. This will again preserve any
# special characters in the value of $1 (except $). The /* is a
# wildcard and will give us a list of entries in the directory. So
# the result of "$1"/* is a list.
#
# The for-loop is a for-each loop, similar to the for-each loop in
# Java, but it does not use strict types. We can read this as for
# each item in the list "$1"/*, assign the item to the variable f.
# For a simple example of this, see the first code sample at
# https://www.tldp.org/LDP/abs/html/quotingvar.html
#
# TODO: Can you modify this method to skip over directories?
# TODO: Bonus points: Can you modify this method to ask the use if
# they would like to display files in sub-directories?
# TODO: Modify to ignore binary files?
for f in "$1"/*; do
file "$f" # Run file command on the value of $f
# -n option to echo will not print out a new line
echo -n "Would you like to display this file (Y/n): "
# read input from the user and store in variable ans
read ans
# The following a switch-case statement. If the value of $ans
# is n or N, we will skip the file, but if it is Y or anything
# else, we will display it.
case "$ans" in
n | N)
echo "Skipped $f"
;;
Y | *)
read_file "$f"
;;
esac # end the switch case
done # end the for loop
}
# The read_file function which is responsible for the reading and
# output to stout of the file
read_file(){
# local variable are declared using the local keyword, local to
# this function only
#
# Here we are using command substitution. Here we use the output
# redirector (<) to redirect the value of $1 (the file name) to
# the wc command (man wc to learn more!). wc < "$1" is wrapped in
# matching parentheses and preceded by a $. So, wc < "$1" will
# run in a sub-shell and the output of this commend from the
# subshell will be stored in the word_count variable. At its
# simplest, command substitution will allow us to the store the
# output of a command in a variable.
local word_count="$(wc -w < "$1")"
local char_count="$(wc -m < "$1")"
local line_count="$(wc -l < "$1")"
# Here is a while loop that will read lines from the file, whose
# filename is the value of $1. At each iteration, we print out the line.
#
# We use the output redirector to send $1 to the while, which
# iterates over its contents.
while read line; do
echo $line
done < "$1"
# Lets use those local variables, we used earlier. Use the manual
# page of the echo command to discover what the -e option does.
echo -e "\r\nWord Count: $word_count\r\nChar Count: $char_count\r\nLine Count: $line_count"
}
# This script is simple, so our use of options is contrived.
#
# We will use the bash built-in, getopts, to get the options specified
# by the user when executing our script in the terminal. 'f:h'
# translates to our expectation of two options, f and h. The colon
# after the f signifies that f is used with an argument, e.g. -f
# filename.
#
# The while loop will iterate over the options and storing each in
# turn in the variable c. At each iteration, we use the switch-case
# statement to test the option, if it is h, the usage function is
# called. If it is f we run the read_file function and pass to the
# function the option's argument:
#
# fileViewer.sh -f test.txt well call read_file test.txt.
# (OPTARG="test.txt")
while getopts 'f:h' c
do
case $c in
f)
read_file $OPTARG
;;
h)
usage
;;
esac
done
# Our first example of bash arithmetic in this script. OPTIND is a
# variable containing the index of the next argument to process. shift
# will allow us to skip over parsed options
shift $(($OPTIND-1))
# If the number of args greater than one, we have an argument to
# process, else we do nothing. $# will give us the number of
# arguments. Note the style of the if-statement is different for
# arithmetic.
if (( $# >= 1 ))
then
main $1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment