Note that there should be a 'then' statement after each 'elif' too.
Anyway, use
#bash -n file.sh
to debug your script. -n means "no execution". So in a situation where an error free script should be run, this will help a lot.
Or you can use
#bash -o xtrace file.sh
to debug your script and trace variable evaluation during execution of script.

The elif statement line provides another command to evaluate, similarly to the original if state-
ment line. If the exit status code from the elif command is zero, bash executes the commands
in the second then statement section.
You can continue to string elif statements together, creating one huge if-then-elif
conglomeration:

about file :-d file Check if file exists and is a directory.-e file Checks if file exists.-f file Checks if file exists and is a file.-r file Checks if file exists and is readable.-s file Checks if file exists and is not empty.-w file Checks if file exists and is writable.-x file Checks if file exists and is executable.-O file Checks if file exists and is owned by the current user.-G file Checks if file exists and the default group is the same as the current user.file1 -nt file2 Checks if file1 is newer than file2.

The elif statement line provides another command to evaluate, similarly to the original if state-
ment line. If the exit status code from the elif command is zero, bash executes the commands
in the second then statement section.
You can continue to string elif statements together, creating one huge if-then-elif
conglomeration:

if command1
then
command set 1
elif command2
then
command set 2
elif command3
then
command set 3
elif command4
then
command set 4
fi

Each block of commands is executed depending on which command returns the zero exit status
code. Remember, the bash shell will execute the if statements in order, and only the first one
that returns a zero exit status will result in the then section being executed. Later on in ‘‘The
case Command’’ section you’ll see how to use the case command instead of having to nest lots
of if-then statements.

-d file Check if file exists and is a directory.
-e file Checks if file exists.
-f file Checks if file exists and is a file.
-r file Checks if file exists and is readable.
-s file Checks if file exists and is not empty.
-w file Checks if file exists and is writable.
-x file Checks if file exists and is executable.
-O file Checks if file exists and is owned by the current user.
-G file Checks if file exists and the default group is the same as the current
user.
file1 -nt file2 Checks if file1 is newer than file2.

Anybody want to explain how it helps to get a long listing from ls and
then clip out everything except the filename with awk? Or to run a
tail and two tr and an awk to pull out the active part of the file
name?

Try:

Using ls to give 1 column of all the filenames, in reverse text order,
so the last file is the first name given.

Using a pattern as a file separator in awk, so it splits fields at
both special chars; and having awk stop at the first name.

zlast="$( ls -1r 1_*.dbf.gz | awk '-F[_.]' '{ print $2; exit; }' )"

In fact, a couple of shell substitutions using ## and \%% would be
better.

What value does: x=`expr $last` :add ?

What is this for?

while [ $x = $y ]
do
sleep 10
break
done

If x != y it does nothing. If x == y it sleeps once and breaks. No
loop necessary in either condition. Try:

[[ $x == $y ]] && sleep 10

I don't even see what this is for. If the names are the same, what
makes you think 10 seconds is an adequate time for whatever you expect
to change, to make any difference?

And, being as we do not get new values for x and y at this point, why
would we test "while [ $x != $y ]" in the next line? The outcome must
still be the reverse of what the previous test did.

I hate: gzip 1_$y*.dbf. We already got x and y using 1_*.dbf. So what
does the * add in the gzip? It just risks matching a longer path that
we have not identified previously.

I hate the loop that hopes that incrementing y will eventually match
x. More likely is that some file will get missed or removed, and you
will get a couple of million messages like:
"gzip: 1_411645276*.dbf: not found" while you count y upwards.

I don't see why you use: last=`ls -l 1_*.dbf* but you gzip: .dbf

If the file is packed, it doesn't match .dbf.gz so there is no point
in seeing if xx.dbf* and xx.dbf.gz are actually different.

echo "NO need to be zipped" >> $log

Would it be nice to add a $( date) and a script name to that message,
maybe? Even a pwd and hostname and maybe even an echo for each name
you DID gzip?

From Jalal's post, if [ ! -f FILE_NAME ] will indeed check if a
specified file name exists. But it interacts very badly with
wildcards. Should it tell you if all the names exist, or any one or
more, or what ? In fact, we had a recent thread on this that
established that different shells do bad things on almost all cases.
They all check only the first name generated by the wildcard, but they
do different things with any other names generated.

It really is not clear what this script is meant to do overall. I
assume it is something like:

Given a directory containing files with names like 1_3456.dbf,
where 3456 represents a unique serial number which increments on each
new file created by some autonomous process:

Write a script to gzip any file that has not already been gzipped,
except the latest (because it is still being appended to). In which
case:

I repeatedly see shell-script here with line
script :
which is uncommented and I guess this is not the intention.

All using this shell-script should be aware that 'script' is a system command which is invoked here. It could be dangerous under some conditions - filling the smaller file system, for example. I recommend making this line commented!

This beast is getting complicated enough am lost. I happen to agree with Karel about commenting. If this is supposed to help the original poster, then should commenting not be done to actually help? Just saying

I suspect Avigdor is confusing structure with indentation (which has again got lost in the posting).

Functional decomposition is the key to structure. If you break out small, well-defined, testable parameterised code units as functions (in any language), you will simplify the global code down to the point where the overall process will be blindingly obvious.

Plus, you get to give meaningful names to all the functions and arguments, which can be chosen to illuminate the purpose of each section. And you get good localisation of many variables, which saves you mixing up your usage of global variables. And you get reusability at the function level.