Prerequisites: Bash Scripting, Shell Function Library
HEAD and TAIL command print a specified number of lines of a file from the beginning and from the end respectively. If we don’t specify a number, the commands print the entire file. We will make a bash script display.sh which takes two to three arguments.
- –h or -t option to specify whether to print from head or tail
-
NUM to specify the number of lines to be printed
- If NUM is not specified, the script shall print the whole file.
- If NUM exceeds the total number of lines in a file, the script shall print the whole file as usual.
- FILE address
Creating Shell Script
The name of the script will be display.sh. Make it an executable file.
$ touch display.sh $ chmod +x display.sh
Step 1: Handling Arguments and Errors
$ ./display.sh #invalid syntax (no arguments provided)
$ ./display.sh FILE #invalid syntax (options not specified)
$ ./display.sh -option NUM FILE #correct syntax
$ ./display.sh -option FILE #correct syntax
Open the file and add the following script :
#!/bin/bash # if the number of args is less than 2, # then show the correct syntax to the user if [ $# -lt 2 ] ; then echo "Invalid Syntax!" echo "The valid syntax is ./$(basename $0) [-h|t] NUM [FILE]" exit 1 fi # storing the option (-h or -t) option=$1 # if second argument is the file to be displayed # then by default we will print all the lines # otherwise we will store the number of lines # to be printed if [[ -f $2 ]]; then NUM=$(wc -l < "$2") #wc -l returns number of lines in a file FILE="$2" #store the file address else NUM=$2 FILE="$3" fi
- We will store the arguments in variable
- It works even if the file name contains spaces. Make sure to add double quotes. For example : ./display.sh -h 10 “file name”
- wc -l command returns the total number of lines in a file.
Step 2: Create Main Function
case $option in "-h") print_head $NUM $FILE exit;; "-t") print_tail $NUM $FILE exit;; *) echo "Invalid Option" exit 1 exit;; esac exit 0
- If the option is -h, the print_head function will be called.
- If the option is -t, the print_tail function will be called.
- If the option is invalid, the script will exit 1
Step 3: Create print_head function
function print_head () { #print first NUM lines NUM=$1 ; FILE=$2 ; COUNTER=0 ; while IFS= read -r line #will keep reading the file till the end do echo "$line" (( COUNTER++ )) if [ $COUNTER = $NUM ] ; then #if we have printed NUM lines; break break fi done < "$FILE" }
- read command trims white spaces. To avoid that, include IFS= in your script.
- -r option enables us to print escape sequences in the file as well.
- COUNTER variable keeps the count of lines that have been printed.
Step 4: Create print_tail function
function print_tail () { #print last part of file NUM=$1 ; FILE="$2" ; total_lines=$(wc -l < "$FILE") #number of lines to be ignored if [ $NUM -ge $total_lines ] ; then IGNORE=0 else IGNORE=$(( $total_lines - $NUM )) fi while IFS= read -r line do if [ $IGNORE -gt 0 ] ; then (( IGNORE-- )) continue fi echo "$line" done < "$FILE" }
- We start reading the file line by line from the beginning.
- IGNORE variable will keep a track of how many lines at the beginning we have to ignore, and thus not print them.
- The rest of the lines will be printed once IGNORE becomes equal to 0.
Step 5: Putting together everything
#!/bin/bash function print_head () { #print first NUM lines NUM=$1 ; FILE=$2 ; COUNTER=0 ; while IFS= read -r line #will keep reading the file till the end do echo "$line" (( COUNTER++ )) if [ $COUNTER = $NUM ] ; then #if we have printed NUM lines; break break fi done < "$FILE" } function print_tail () { #print last part of file NUM=$1 ; FILE="$2" ; total_lines=$(wc -l < "$FILE") #number of lines to be ignored if [ $NUM -ge $total_lines ] ; then IGNORE=0 else IGNORE=$(( $total_lines - $NUM )) fi while IFS= read -r line do if [ $IGNORE -gt 0 ] ; then (( IGNORE-- )) continue fi echo "$line" done < "$FILE" } # if the number of args is less than 2, # then show the correct syntax to the user if [ $# -lt 2 ] ; then echo "Invalid Syntax!" echo "The valid syntax is ./$(basename $0) [-h|t] NUM [FILE]" exit 1 fi #storing the option (-h or -t) option=$1 #if second argument is the file to be displayed #then by default we will print all the lines #otherwise we will store the number of lines to be printed if [[ -f $2 ]]; then NUM=$(wc -l < "$2") #wc -l returns number of lines in a file FILE="$2" #store the file address else NUM=$2 FILE="$3" fi case $option in "-h") print_head $NUM $FILE exit;; "-t") print_tail $NUM $FILE exit;; *) echo "Invalid Option!" exit 1 exit;; esac exit 0
Run the Script
$ ./display.sh -h FILE
$ ./display.sh -h 4 FILE
$ ./display.sh -t 6 file.txt $ ./display.sh -t 1000 file.txt