On 2013-11-12 11:27, Les Kitchen wrote:
> 
> 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)"

No, never parse the output of 'ls'. In this case it's unlikely to be an
issue, but best never to do it anway:
http://mywiki.wooledge.org/ParsingLs

> 
> You can't just do
> 
>    test -n prefix*

That's what 'test -f prefix*' (or perhaps -e) is for.

> 
> 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.
> 
[...]
> 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

Again, parsing ls is bad; this is worse than your previous example
because if, (however unlikely) you have a file with a newline in it,
it'll get counted twice.

For bash:

  array=(*); test "${#array[@]}" -eq 1

Or for sh:

  set -- *; test "$#" -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".

This is true; so to amend my previous suggestion of test -f or test -e:

  for f in foo.*; do test -f "$f" && do_stuff; break; done

Use break to exit after the first match, otherwise remove it.

> 
> Sorry, I'm terribly old-fashioned and use 'test' directly, not
> '[' or even '[[' (which I didn't know about -- thanks, Matthew),

Also worth looking at ((, which is designed specifically for arithmetic
and numeric comparisons.

> 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.

Less bad, but I'd still try to avoid calling out to an exernal command
when a shell loop will do.

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

Agreed.

> 
[...]
-- 
Regards,
Matthew Cengia

Attachment: signature.asc
Description: Digital signature

_______________________________________________
luv-main mailing list
[email protected]
http://lists.luv.asn.au/listinfo/luv-main

Reply via email to