Last active
February 14, 2019 07:44
-
-
Save stephennaicken/cc0debbd18b2ebc2471227828d894946 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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