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
signature.asc
Description: Digital signature
_______________________________________________ luv-main mailing list [email protected] http://lists.luv.asn.au/listinfo/luv-main
