[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: -d option not working. . .?

From: Bob Proulx
Subject: Re: -d option not working. . .?
Date: Wed, 12 Sep 2007 10:32:35 -0600
User-agent: Mutt/1.5.9i

Eric Blake wrote:
> According to Michael Williams on 9/12/2007 4:06 AM:
> > Being that I'm not a bash (or any other shell for that matter) guru, is
> > there any reason that parsing occurs this way?  Why is it not more like
> > other programming languages?
> Two words: history and POSIX.  It's been done that way for more than 20
> years, so it was standardized that way.  Changing it would break too many
> existing scripts.

The shell is first and foremost a way to launch other commands.  The
syntax is simply "if" followed by a command-list, (e.g. if /some/foo;
or even if cmd1; cmd2; cmd3; then).  Plus the '( ... )' syntax is
already taken by the use of starting a subshell.

As I recall in the original shell language the file test operator was
not built-in.  It was provided by the standalone '/bin/test' command.
The result was effectively this:

  if /bin/test -d somedir

Although the full path /bin/test was never used.  I showed it that way
here for emphasis that following the 'if' statement is a command list.
Normally it would simply have been:

  if test -d somedir

Of course that is fine and for the best portability that style is
still the recommended way today to use the test command.  But many
people find that it looks different from other programming languages.
To make the test operator (note I mention the test operator and not
the shell language, this is a localized change not affecting the
language as a whole) look more like other programming languages the
'test' program was coded to ignore the last argument if it was a ']'.
Then a copy of the test program could be used as the '[' program.

  ...modify /bin/test to ignore ']' as last argument...
  cp /bin/test /bin/[

This allows:

  if [ -d somedir ]

Doesn't that look more normal?  People liked it and it caught on.  It
was so popular that both 'test' and '[' are now shell built-ins.  They
don't launch an external '/bin/test' program anymore.  But they *used*
to launch external programs.  Therefore argument parsing is the same
as if they still did launch an external program.  This affects
argument parsing.

  it test -f *.txt
  test: too many arguments

Oops.  I have twenty .txt files and so test got one -f followed by the
first file followed by the remaining files.  (e.g. test -f 1.txt 2.txt
3.txt 4.txt)

  if test -d $file
  test: argument expected

Oops.  I meant to set file.

  if test -d $file

If variables such as that are not set then they wlll be expanded by
the shell before passing them to the (possibly external) command and
disappear entirely.  This is why test arguments should always be quoted.

  if test -d "$file"
  if [ -d "$file" ]

Actually today test is defined that if only one argument is given as
in this case "test FOO" then then test returns true if the argument is
non-zero in text length.  Because "-d" is non-zero length "test -d" is
true.  The number of arguments affects how test parses the args.  This
avoids a case where depending upon the data may look like a test

  if test "$DATA"         # true, $DATA is non-zero length

  if test "$DATA"         # false, $DATA is zero length

But the problem case is how should test handle an argument that looks
like an operator?  This used to generate errors but now because it is
only one argument is defined to be the same as test -n $DATA.

  if test "$DATA"         # true, $DATA is non-zero length
  if test -d              # true, same as previous case.

Because test and [ are possibly external commands all of the parts of
them are chosen to avoid shell metacharacters.  The Fortran operator
naming was well known at the time (e.g. .gt., .eq., etc.) and was
pressed into service for the shell test operator too.  Comming from
Fortran using -gt, -eq, etc. looked very normal.

Incorrect use generating unlikely to be intended results:

  if test 5 > 2    # true, "5" is non-zero length, creates file named "2"

Intended use:

  if test 5 -gt 2  # true (and no shell meta characters needing quoting)

Then much later, sometime in the mid 1980's, the Korn sh decided to
improve upon this situation.  A new test operator was introduced.
This one was always a shell built-in and therefore could act upon the
shell arguments directly.  This is '[[' which is a shell keyword.
(Keyword, metacharacters, builtins, all are different.)  Because the
shell processes [[ internally all arguments are known and do not need
to be quoted.

  if [[ -d $file ]]  # okay
  if [[ 5 > 2 ]]     # okay

I am sure that I am remembering a detail wrong but hopefully this is
useful as a gentle introduction and interesting anyway.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]