6

How do I do this?

if [ -f town-*.swf ]
then
mkdir towns
fi

This checks if town-*.swf exists, but I also need it to look for city-*.swf among others, so I need something like this:

if [ -f town-*.swf, city-*.swf ]
then
mkdir towns
fi
DisplayName
  • 11,688
  • Wouldn't it be easier to build a switch-case for this? – ryekayo Oct 24 '14 at 16:48
  • Whats a "switch-case"? – DisplayName Oct 24 '14 at 16:49
  • See here: http://www.shellhacks.com/en/CASE-Statement-in-Bash-Example – ryekayo Oct 24 '14 at 16:50
  • @ryekayo case can (and often should) be used for testing equality or matching, but how do you want to use case to check whether a file exists? – Uwe Oct 24 '14 at 16:57
  • 2
    @DisplayName Just for clarification: Do you assume that there is at most one file matching each of the patterns town-*.swf and city-*.swf, or could there be several ones? – Uwe Oct 24 '14 at 17:03
  • @DisplayName Still the question is whether there can be several files, say town-1.swf and town-2.swf? (If it's impossible, there's an easy solution; if it's possible, you have to work harder.) – Uwe Oct 24 '14 at 17:14
  • There could be several files too. – DisplayName Oct 24 '14 at 17:18
  • @DisplayName, please see the updated answer and let me know if it works. – Ramesh Oct 24 '14 at 17:28

5 Answers5

6

POSIXly, you can use ls

if ls town-*.swf >/dev/null 2>&1 &&
   ls city-*.swf >/dev/null 2>&1 
then
  mkdir towns
fi

or shorter if condition:

if ls town-*.swf city-*.swf >/dev/null 2>&1

even if your shell supports brace expansion:

if ls {town,city}-*.swf >/dev/null 2>&1
cuonglm
  • 153,898
5
if stat -t city-*.swf >/dev/null 2>&1
then
    if stat -t town-*.swf >/dev/null 2>&1
    then
       mkdir towns
    fi
fi

As user uwe pointed out in the comments, my previous command would prevent the wild card from being expanded. However, this new version should work.

If you need it with a single if loop, you could modify the script as,

if stat -t city-*.swf >/dev/null 2>&1 && stat -t town-*.swf >/dev/null 2>&1
then
     mkdir towns
fi

If you need to specify an or condition instead of the and condition, you could replace && with ||.

Testing

ls
city-1.swf  city-2.swf  city-3.swf  city-4.swf  city-5.swf  sc.sh  
town-1.swf  town-2.swf  town-3.swf  town-4.swf  town-5.swf

Now, I execute the script which is named as sc.sh and then I could see that the towns directory is getting created successfully.

References

https://stackoverflow.com/questions/2937407/test-whether-a-glob-has-any-matches-in-bash

Ramesh
  • 39,297
2

In most cases command test ( [ ) offer operators -a and -o

EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.

But in the case of possible multi-lines you should use command which can operate in the condition (for example ls)

ls {town,city}-*.swf &>/dev/null && mkdir town

You can use the fact that globbing when can't to find the substitution remain the string as is (in the case with *). So we just need to check is it somewhere in the line:

set -- {city,town}-*.swf 
[[ ! "$*" =~ \* ]] && mkdir town

Or use case (as offered in comments above)

case $(ls) in
  *city-*.swf*town-*.swf*) mkdir town ;;
esac
Costas
  • 14,916
  • find was better. And you can always -exec sh -c 'script'. In there you might kill find and so only ever do one -exec. (plus ls has been done already...) – mikeserv Oct 25 '14 at 12:42
  • 1
    @mikeserv I'd like find too but in the case we need to check two conditions the command string can be rather difficult – Costas Oct 25 '14 at 12:48
1

POSIX defines the -p option to the pathchk utility so...

  • -p Instead of performing checks based on the underlying file system, write a diagnostic for each pathname operand that:

    • Is longer than {_POSIX_PATH_MAX} bytes (see Minimum Values in the Base Definitions volume of IEEE Std 1003.1-2001, Chapter 13, Headers, )

    • Contains any component longer than {_POSIX_NAME_MAX} bytes

    • Contains any character in any component that is not in the portable filename character set

If the filenames you are looking for should match only characters in the portable filename character set - which is comprised of ASCII letters and digits and ._-/ - then the following command will work as well as any without having to do any more stat() calls than the shell does to glob them:

if    pathchk -p town-*.swf city-*.swf
then  mkdir towns
fi

Another way is to glob the pathname twice - once for an already known value and once also for the unknown. It the known value glob is not stripped then it is because the unknown value did not resolve.

Consider the following:

mkdir test
touch test/testfile1
echo tes[t]/test*1 tes[t]/test*2

OUTPUT

test/testfile1 tes[t]/test*2

Here we know the test path exists and so we can test for its resolution when attempting to match the unknown path's resolution.

For example, let's assume your current working directory is comprised entirely of pathnames that do not end with a ? and there are no one-off paths that do either. This is not such a big assumption to make either - it can even be assured like:

cd /tmp/no_question_mark\? && {
    $_handle_it || exit
};  ln -s "$PWD" /tmp/no_question_marks
    cd /tmp/no_question_marks

That assumption made, you can then do something like:

set "${PWD%?}"?/town-*.swf "${PWD%?}"?/city-*.swf
case "$*" in (*\?/*) ! echo 'missed at least one...';;
(*) echo 'got em all!'; mkdir towns;;
esac
mikeserv
  • 58,310
0

Here's a solution that's lower impact than running the external command ls, as well as being more portable than a solution based on stat (which differs from OS to OS).

#!/bin/bash

found=false
for _ in town-*.swf, city-*.swf do
  found=true; break
done

if $found; then
  echo "Yup!"
fi

The underscore is a throwaway variable. The for loop is an easy way to expand your list of files, and the break insures that you won't waste cycles looping through a long file list. The executables true and false can be found in /bin/ or /usr/bin/, and could be replaced with bash builtins or functions if you would prefer that optimization.

Note that this also should work in a /bin/sh. (At least, it did for me in a couple of other operating systems.)

ghoti
  • 6,602