Open In App

Shell Script which Works Similar to the Unix Command HEAD TAIL

Improve
Improve
Like Article
Like
Save
Share
Report

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.

Error Handling. Prompts the user to use proper syntax

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

Printing the entire gfg.txt file

$ ./display.sh -h 4 FILE

Printing first 4 lines of gfg.txt

$ ./display.sh -t 6 file.txt
$ ./display.sh -t 1000 file.txt

Printing 6 lines from the last part of the file. If NUM exceeds total number of lines, it prints whole file

Handles invalid cases as well

Rejects invalid options, invalid arguments


Last Updated : 09 Aug, 2022
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads