Saturday, April 14, 2012

A Linux Preview Script

Mac OS X comes with a program called Preview, which displays PDF files as well as images (PNG, GIF, Bitmap, etc.). Linux doesn't have anything quite like it. OK, if you use the Gnome desktop there's something called Document Viewer, usually linked to evince, which reads Postscript, PDF, Djvu, DVI, etc. In fact, it displays Documents better than Preview does.

Unfortunately, evince doesn't display images. For that we have programs like Eye of Gnome, qiv (my favorite), and for image manipulation ImageMagick or the GIMP.

And then there are plain old ASCII files, such as you'd use for source code or LaTeX. For that we could use The One True Editor, or gedit, or some other sort of pop-up display window that lists the contents of a file.

What we want, then, is a program, preferably a command line script, which is given a file name, determines what the file is, and then opens the file using the appropriate viewer/editor. How do we do that, pray tell?

Enter the file command, inherited from Unix. file is pretty useful. For example, suppose I have a JPEG file, but for some reason it has been named x1, without the extension. file figures it out pretty fast:


$ file x1
x1: JPEG image data, JFIF standard 1.02

And it will work on most standard Linux file types.

Given this, it's fairly easy to construct a script which takes a look at one or more files, determines the file type, and picks out the approriate file viewer. My script is below. I've called it gnuview. You invoke it from the command line:


$ gnuview file1.txt file2.png file3.dvi file4.bmp

and all of these files pop up on your screen, each launched using the appropriate program.

A few notes:

  • The list of file types is not by any means complete, though I think I hit most of the major ones. If the script doesn't know how to handle a specific file type, it prints out a message on standard error. It's fairly obvious how to add different file types.
  • I dumped my favorite programs (qiv and emacs), in favor of programs that are installed by default with most Linux distributions (eye of gnome, gedit). (qiv doesn't read bitmap files, anyway.) Feel free to change the defaults to your favorite.
  • Error messages? We don't need no stinkin' error messages!
  • Finally, the cascading tree structure of if statements is just annoying. This should probably be rewritten using some kind of case construction, but I didn't bother to figure it out.

No warranty for any of this, of course, and not even a license except for the Creative Commons Disclaimer down at the bottom of this web page. If you come up with a version of this code, put it in the comments or put a link to it there.


#! /bin/bash

# This is an attempt to mimic the behavior of Apple's OS X Preview
#  program.  We determine the type of the file and then use the
#  appropriate command to open it.

# Some defaults.  Change as you see fit:

# ASCII files.  Note that gedit must be invoked in standalone mode

GNTXT="/usr/bin/gedit -s"

# Picture viewer (eog reads more files than qiv, so we'll use that)

GNPIC=/usr/bin/eog

# Postscript/PDF/DjVu/DVI viewer:

GNPDF=/usr/bin/evince

# Read the command line, and scroll through each file type:

for thisfile
do

#   If the string says ASCII anywhere, do this, which should get
#     LaTeX source, source code, etc.

    file $thisfile | grep 'ASCII' 2>&1 > /dev/null
    if [ $? -eq 0 ]
    then

        $GNTXT $thisfile &

    else

#   Maybe it's a document (Postscript/PDF/DjVu/DVI):
#   (I wan't sure about the "or" construct, see the first comment in
#   http://www.cyberciti.biz/faq/searching-multiple-words-string-using-grep/)
        file $thisfile | grep 'document\|DVI' 2>&1 > /dev/null
        if [ $? -eq 0 ]
        then
            $GNPDF $thisfile &
        else

#   Or maybe it's an image
            file $thisfile | grep 'image\|bitmap' 2>&1 > /dev/null
            if [ $? -eq 0 ]
            then
                $GNPIC $thisfile &
            else

#               And if we haven't figured out the type yet,
#                leave a nice note:
                FTYPE=`file $thisfile | awk '{print $2}'`
                echo Cannot handle file $thisfile, type $FTYPE  > /dev/stderr
            fi
        fi
    fi

done