32

How can I use the test command for an arbitrary number of files, passed in using an argument with a wildcard?

For example:

test -f /var/log/apache2/access.log.* && echo "exists one or more files"

Currently, it prints

error: bash: test: too many arguments
1

12 Answers 12

57

This solution seems to me more intuitive:

if [ `ls -1 /var/log/apache2/access.log.* 2>/dev/null | wc -l ` -gt 0 ];
then
    echo "ok"
else
    echo "ko"
fi
Sign up to request clarification or add additional context in comments.

4 Comments

To me this was the most relevant answer to the question.
This is exactly what I was looking for.
good Idea but you can do it simpler: ˋif ls -/var/log/apache2/access.log.* 2>/dev/null 1>&2ˋ works also, test [ ], backticks and wc -l is not needed
For ones who don't know, you have to redirect stderr to a null file using 2>/dev/null since if no match found, the error of ls wouldn't be printed out.
12

To avoid "too many arguments error", you need xargs. Unfortunately, test -f doesn't support multiple files. The following one-liner should work:

for i in /var/log/apache2/access.log.*; do test -f "$i" && echo "exists one or more files" && break; done

By the way, /var/log/apache2/access.log.* is called shell-globbing, not regexp. Please see Confusion with shell-globbing wildcards and Regex for more information.

Comments

11

First, store files in the directory as an array:

logfiles=(/var/log/apache2/access.log.*)

Then perform a test on the count of the array:

if [[ ${#logfiles[@]} -gt 0 ]]; then
  echo 'At least one file found'
fi

4 Comments

Good idea. But when no file exists, the array content will be ['/var/log/apache2/access.log.*'] (one element containing the '*'). Suggestion: [[ ${#logfiles[@]} -gt 1 || -e ${logfiles[0] ]] or simply [[ -e ${logfiles[0] ]] (as dmaticzka does)
I would suggest adding shopt -s nullglob to avoid having an element in the array content if no file exists. In that case, the variable will be empty and the following test easier to make.
@simohe @skupjoe what about adding test for regular file? if [[ ${#logfiles[@]} -gt 0 ]] && [[ -f ${#logfiles[@]} ]] ; then . Sorry for being late for the party, so the shopt -s nullglob will not be necessary?
@zero0cool good catch! How could I miss that? :) And @sunta3iouxos good suggestion. The test should be: if [[ ${#logfiles[@]} -gt 0 ]] && [[ -e ${logfiles[@]} ]]; then to catch the condition for 0 files! Or use -f or -d to test for just files/directories ;).
6

This one is suitable for use with the Unofficial Bash Strict Mode, no has non-zero exit status when no files are found.

The array logfiles=(/var/log/apache2/access.log.*) will always contain at least the unexpanded glob, so one can simply test for existence of the first element:

logfiles=(/var/log/apache2/access.log.*)

if [[ -f ${logfiles[0]} ]]
then 
  echo 'At least one file found'
else
  echo 'No file found'
fi

1 Comment

Great idea to test just [0] of array.
5

If you wanted a list of files to process as a batch, as opposed to doing a separate action for each file, you could use find, store the results in a variable, and then check if the variable was not empty. For example, I use the following to compile all the .java files in a source directory.

SRC=`find src -name "*.java"`
if [ ! -z $SRC ]; then
    javac -classpath $CLASSPATH -d obj $SRC
    # stop if compilation fails
    if [ $? != 0 ]; then exit; fi
fi

Comments

3

You just need to test if ls has something to list:

ls /var/log/apache2/access.log.* >/dev/null 2>&1 && echo "exists one or more files"

1 Comment

Why is this one downvoted? Redirection is ugly? Better to use explicit if statement? Shouldn't use ls for scripting?
2

Variation on a theme:

if ls /var/log/apache2/access.log.* >/dev/null 2>&1
then 
  echo 'At least one file found'
else
  echo 'No file found'
fi

Comments

1
ls -1 /var/log/apache2/access.log.* | grep . && echo "One or more files exist."

Comments

1

Or using find

if [ $(find /var/log/apache2/ -type f -name "access.log.*" | wc -l) -gt 0 ]; then
  echo "ok"
else
  echo "ko"
fi

1 Comment

I recommend to add -maxdepth 1 before -type f to not go into subdirectories. For speedup, use this: find /var/log/apache2/ -maxdepth 1 -type f -name "access.log.*" -print -quit (quit after first match)
1

This condition below doesn't produce stderr. the condition's blackhole (/dev/null) doesn't prevent the stderr in cmd.

if [[ $(ls -1 /var/log/apache2/access.log.* | wc -l ) -gt 0 ]] 2> /dev/null

therefore I suggests this code.

if [[ $(ls -1 /var/log/apache2/access.log.* | wc -l ) -gt 0 ]] 2> /dev/null 
then
    echo "exists one or more files."
fi

4 Comments

it work also with broken symlinks?
If the symlink's target changed, you could also change the broken symlinks to connect the changed target. let's say link pair { target : symlink }. I change the target name with the suffix 1. So there is no target anymore but target1. your symlink is broken. but if you "ln -s -f target1 symlink" (-f is force write on existing symlink file option), your symlink will be alive.
Also, I changed the condition on the script above to a symlink, which works great.
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
0

more simplyfied:

if ls /var/log/apache2/access.log.* 2>/dev/null 1>&2; then
   echo "ok"
else
   echo "ko"
fi

Comments

0

Faced the same issue and I put together one-liner using test, without error if no such file exists:

[ $(ls /var/log/apache2/access.log.* 2>/dev/null | wc -l) -gt 0 ] && echo "exists one or more files" || echo "no log files"

1 Comment

Thank you for your interest in contributing to the Stack Overflow community. This question already has quite a few answers—including one that has been extensively validated by the community. Are you certain your approach hasn’t been given previously? If so, it would be useful to explain how your approach is different, under what circumstances your approach might be preferred, and/or why you think the previous answers aren’t sufficient. Can you kindly edit your answer to offer an explanation?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.