Hi Allan,

> I want to check for the existence of a file that can have a variable 
> tail portion of its name.
>      if[-f <file> ] doesn't seem to have the ability to do wild cards.
> Can anyone suggest how to achieve this?

[I also saw Matthew Cengia's reply. And I agree with him that
the answer depends on what you really want to do.  But here are
just some thoughts of mine based on my guesses about that.]

I'd tend to use something like:

   test -n "$(ls -1d prefix* 2>/dev/null)"

You can't just do

   test -n prefix*

because then, if the file doesn't exist, you'll get the glob
passed through literally unchanged, and so still get a false
positive.  There's a bash setting so that non-matching globs
become the empty string, but that tends to be more trouble than
it's worth.

So, in my suggestion, the glob gets expanded in the arguments to
the 'ls' command, whose output is captured by the backticks,
$(...) (better behaved than the older `...`).  If no matching
file exists, then the stdout of 'ls' will be a single empty
line, whose trailing newline will get stripped off by the
backticks, leaving an empty string for 'test'.  The -d option is
in case one of the expanded files is a directory, so you just
get its name, not its contents.  And the -1 option is to force
one filename per line.  Neither really matters here, but they're
often handy for other uses of this idiom.  The redirection of
stderr doesn't affect the behavior, but just avoids the clutter
of the error message from 'ls' when it can't find the file
'prefix*'.  And the double quotes are to make sure the -n gets
just a single argument even if multiple files match the glob (or
if the name has spaces, but I don't want to go there), or if the
match is empty.

You can use 'test -z ...' for the opposite sense.

This only gives you a yes/no test.  If you need more than that,
you could use something like this code fragment

   test $(ls -1d prefix* 2>/dev/null | wc -l) -eq 1

to ensure that there is only one match; or even something like

   ls -1d prefix* 2>/dev/null >tmp
   if test $(wc -l tmp) -eq 1; then
     the_one=$(cat tmp)
     ...
   fi

where you could probably do a better job of consing up a good
temporary filename, like /tmp/foo$$, and then doing some trap
stuff to remove it later.

Or maybe

   the_one="$(ls -1d prefix* 2>/dev/null | head -n 1)"

with a subsequent 'test -n "$the_one"'.  That is if you're sure
there'll only ever be one match or if there are multiple matches
you're happy to silently take just the first one.

The thing you have to remember is that glob (wild card)
expansion happens quite early, so if you have (say) files foo.c
and foo.o then in

   test -f foo.*

the foo.* will get expanded and it will be as if you'd written

   test -f foo.c foo.o

for which I get "bash: test: too many arguments".

Sorry, I'm terribly old-fashioned and use 'test' directly, not
'[' or even '[[' (which I didn't know about -- thanks, Matthew),
mostly because it just makes the semantics simpler and more
regular and more decomposable.  'if' uses the exit status of a
command for true or false, and 'test' is just a utility command
that can get you an exit status out of various conditions.
Maybe it's an old Lisp-ism, but I even tend to write things like

   test -e foo.c && echo foo

avoiding the 'if' altogether.

You can also use the 'if' on any command, not just on a 'test'.
In fact, it just occurred to me that for a lot of what you might
want to do, you could just have:

   if ls -1d prefix* &>/dev/null; then
      ...
   fi

That is, rely directly on the exit status of the 'ls' command,
throwing away all its output, both stdout and stderr.

In shell programming in general, it's a good idea to pay
attention to the exit status of commands.

My only other tip is to take care with your test file-testing
options.  'test -f' tests just for a regular file, so will still
give false if the thing exists but is a symlink or a directory
or a fifo.  It depends on what you really want to do.  Sometimes
'test -e' (exist) or 'test -r' (readable) are closer to what
you need.  My above code, with the 'ls' and globbing, is
essentially testing just for existence.  Often, for practical
purposes you need need a combination of test options, like
exists and is readable and isn't a directory.

Hope this is useful.

-- Smiles, Les.

P.S. I'm contemplating switching to fastmail -- but that's a
topic for luv-talk, I guess.

P.P.S.  I should note in passing that Perl's behavior with its
backticks is different -- newlines don't get special treatment.
Same for Ruby.  Not sure about Python and other languages, but I
suspect they're more Perl-like than shell-like too.  Just keep
this in mind if ever you want to translate shell code with
backticks into a different scripting language.
_______________________________________________
luv-main mailing list
[email protected]
http://lists.luv.asn.au/listinfo/luv-main

Reply via email to