Bash Script : Print lines of a file in reverse order

The task is to print the lines of a file in the reverse order in which it appears in the file. A bash script and some other tricks would be presented below to do this. We can do this with tac and sed , but here we will do it without any of these tools and use only the bash inbuilt, bash arrays, and redirection.

Idea


The process is to read each line of the input file in an array with each array element containing a line, and after reading is finished we will print the array from last to first, reverse order, which will print the file in reverse order.
We directly present the script below and then describe.

The Script

#!/bin/bash
#This code is a part of http://phoxis.wordpress.com
#

file_name="$1"

#Check if file name is given, and it exists
if [ "$#" -eq 0 ]
  then
   echo "Syntax: $0 filename"
   exit
elif [ -f "$file_name" ]
  then
     :
  else
     echo "File \""$file_name"\" does not exist"
     exit
fi

#Set the IFS variable to \n this enables reading one \n separated
# line per read
IFS=$'\n'
arr=();

#Read from file_name and store each line into next array location.
while read -r line
do
  arr+=("$line");
done < "$file_name"

#If last line is not \n terminated read returns false, body of while
# is not executed. Save last line into array manually
arr+=("$line")

#Count number of lines, and adjust index
i="${#arr[*]}"
i=$(($i-2))

#Print the lines in reverse order
while [ $i -ge 0 ]
do
   echo "${arr[$i]}"
   i=$((i-1))
done

Save the above script as “my_tac.sh” . The syntax to use this script is my_tac.sh file_name

At first we get the passed argument into variable file_name . We check if the number of parameter passed is zero, that is , if nothing is passed. If nothing is passed show error. Else check if the passed file name exists. If it does not exist exit, else do nothing.

We will treat each line of the file as one field when reading into an array, and two lines are separated by a newline character a \n. We set the IFS that is the Input Field Separator to \n . Note: $’\n’ quoting will expand the \n into its corresponding newline character.
Next we declare an array.
In the while loop the while read -r line reads from the file whose name is in “file_name”. The contents of the file is redirected into the while loop, and the read gets them from there. In each iteration it reads one line at a time and stores into variable line. It is then assigned to the next element of the array in the body of the loop.
The -r option in read -r tells the command to treat the backslash \ as a normal character as a part of the file and not an escape character.
When the file ends the loop terminates.

There is two things to note here. First, Note the double quotes around "$line" in the array expressions. This will ensure that the blank lines with only a \n is read and also the blankspace formatting in the file is preserved.
Second, consider a whose last line is not a blank line. That is the last line contains some text and ends without a new line and simply terminates with NUL. In this case at the last iteration read command will read in the last line but return false as it is not terminated with any field terminated chars, and while loop’s body will not be entered, and thus the last line is not stored in the array. We manually store the read in last line into the array in arr+=("$line")

Next we calculate the length of the array, which is the number of lines the file contains, and store into a variable i, and it is iterated from the value of i to 0 and the contents of the array is printed in reverse order.

And thus the file is printed in reverse line sequence.

This shell script will process only the first file passed to it. This can be easily expanded by writing the code in a function, and then calling the function with each new argument one by one. shift can be used to traverse through the parameters. Read : http://phoxis.org/2010/03/14/read-multiple-arg-bash-script/

The tac

There is a program that you can use in bash which comes with coreutilities is tac . This acts just reverse as the program cat .

tac file_name

The above command would simply print the contents of the file in reverse line order. I would prefer using this tool to make reverse of files instead.

sed

There are other ways too to print the file in reverse line order like the cool example with sed below

sed -n '1!G;h;$p' file_name

Work it out yourself, i am not an sedder!


About phoxis

Computer Science Undergraduate Student
This entry was posted in Computer Science, Linux / Unix Shell and tagged , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <pre> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>