Bash tips: if -e wildcard file check => [: too many arguments
Here is a quick bash tip that might be useful if you need to use inside a bash script a check to see if a wildcard expression of files/folders exists or not. For example:
if [ -e /tmp/*.cache ]
then
echo "Cache files exist: do something with them"
else
echo "No cache files..."
fi
This is using -e (existing file check) that is working fine on individual files. Also the above code might seem to work fine if the result of the expression if one file; but if you have more files returned by the expression this will fail with the following error:
line x: [: too many arguments
This is unfortunately normal as '-e' can take only one parameter, so it will not work with multiple results. This means we have to find a walkaround for this issue... There are probably many solutions for this problem; my idea is to run ls and count the results and then feed that to if. Something like:
files=$(ls /tmp/*.cache)
if [ $files ]
then
echo "Cache files exist: do something with them"
else
echo "No cache files..."
fi
Now this is obviously wrong as if there are no files in the directory we will get a result like this:
ls: cannot access /tmp/*.cache: No such file or directory
and we don’t want to count that. We need to ignore such errors when files are not there, and we will use:
files=$(ls /tmp/*.cache 2> /dev/null)
this way ls errors printed to STDERR now go to /dev/null and we don’t have to worry about them anymore.
This is still not good enough, as if will feed it with more values than it will still fail (when we have more files):
line x: [: /tmp/1.cache: unary operator expected
Obviously the proper way to do this would be to count the number of files and for this we just add "wc -l" to count the result of ls. This should look like this:
files=$(ls /tmp/*.cache 2> /dev/null | wc -l)
if [ "$files" != "0" ]
then
echo "Cache files exist: do something with them"
else
echo "No cache files..."
fi
and if needed we can even use the $files variable that now has the number of file. I hope you will find this useful if you need to do something similar; obviously the file check was just an example, and you would use this based on your needs and inside a script that does something useful
.
>
Tags: bash, tips, Tips & Tricks

6th March 2009, 01:19
To get a correct number of files you may want to use wc -l instead of -w, because:
- filenames might contain spaces
- ls outputs one file per line when not to a terminal (so ls -1 would be redundant here)
Also, != is the string comparison operator, you should use -ne or -gt at least in example code to teach good practices to budding shell scripters
6th March 2009, 02:50
I think you could also do a for loop, which would not trigger an error if no files existed:
for FILE in /tmp/*.cache ; do
# these commands get executed once per file
# with $FILE as the current filename
echo “My file is ${FILE}”
done
6th March 2009, 08:35
@rpetre: thanks! You are right about wc -l, and I just edited the post with the correct information. The final check was intended to be “$files” != “0″, but somehow I put it without quotes. I changed that now to -gt as you suggested as it is cleaner that way. Thanks again for spotting these so fast
@Matt: you are right, and depending on the situation this might be good. If you need to perform an action on the resulted files, when the files don’t exist this will fail when there are no files matching the for expression. Actually in my usage for this, inside the if i had a for just like that, performing an action on all exisitng files, while the else (no files) was performing another action. Thanks for your comment.
9th March 2009, 17:02
You can also enable the bash nullglob option to prevent ls from erroring out when there’s no files:
shopt -s nullglob
To disable: shopt -u nullglob
13th November 2009, 07:51
thx very much
if [ -e *.log ] ;
then
mv *.log ${workDir}
get errors….haha
15th March 2010, 12:37
This tests for the existence of the first file (which suffices: there is AT LEAST 1 file), sets the exit status in $EXISTENCE and then breaks the loop:
for i in *.log; do test -e $i; EXISTENCE=$?; break; done
if [ $EXISTENCE=0 ];
then
echo “Cache files exist: do something with them”
else
echo “No cache files…”
fi
The actual test is only 1 line.
9th April 2010, 13:59
#!/bin/sh
# Scrub Firefox lock file to clean up crashed browsers
#
# list, count and delete target files
#
#
# EH, 08APR10
#
DEST=/home/*/.mozilla/firefox/*/lock
nozfile=$(ls $DEST 2> /dev/null | wc -l) #list files matching wildcards, and count them
if [ "$nozfile" != "0" ] #if there are 1 or more files then…
then
for FILE in $DEST ; do
if ( rm ${FILE} ) #loop through files and if delete succesful print message
then
echo ” ${FILE} deleted by cron script.”
fi
done
fi
exit 0 #end script
2nd September 2010, 12:18
Harakiri almost got it right. If there are no files, $EXISTENCE will not be set, resulting in the test to evaluate to TRUE. This is because $EXISTENCE will evaluate to nothing causing the if statement to expand to “if [ =0 ]“. This test evaluates to TRUE (I tried it), which is obviously wrong.
Try this instead:
NOTEXISTS=1 ; for FILE in /tmp/*.cache ; do test -e $FILE ; NOTEXISTS=$? ; break ; done
if [ $NOTEXISTS -eq 0 ] ; then
echo “Cache files exist: do something with them”
else
echo “No cache files…”
fi
15th December 2010, 00:08
[...] Gefunden habe ich die Loesung bei MDLog:/sysadmin [...]
17th February 2011, 23:04
[...] Bash tips: if -e wildcard file check => [: too many arguments | MDLog:/sysadmin – February 17th ( tags: bash linux howto programming reference shell unix tips ) [...]
18th May 2011, 13:25
I found all of the above to be too inefficient.. Try this instead.
for i in filename*; do FOUND=$i;break;done
if [ $FOUND == 'filename*' ]; then
echo “No files found matching wildcard.”
else
echo “Files found matching wildcard.”
fi
15th June 2011, 09:17
#!/bin/bash
set -x
j=0
cd /Path/of/the/file
Today=`date ‘+%b %e’`
ls -lrt a*access*| grep “$Today” |awk -F” ” ‘{print $9}’ > temp
for i in `cat temp`;do
grep “500″ $i |awk -F” ” ‘{print $9}’ > fivehund_errorfile
File_time=`ls -lrt fivehund_errorfile | awk -F” ” ‘{print $6 ” ” $7 ” ” $8}’`
Time_now=`date ‘+%b %e %H:%M’`
#if [ "$File_time" == "$Time_now" ];
#if [ "$File_time" == "$Time_now" -a test -s fivehund_errorfile ];
#if [ $File_time == $Time_now ] -a [ test -s fivehund_errorfile ];
#if [ $File_time == $Time_now -a test -s fivehund_errorfile ];
#if [ [$File_time == $Time_now] -a [test -s fivehund_errorfile] ];
if [ $File_time == $Time_now ] && [ test -s fivehund_errorfile ];
then
j=1
echo “500 found in $i file”
else
echo “not found in $i file”
fi
done
getting the messg as
++ ‘[' Jun 15 16:54 == Jun 15 16:54 ']‘
./five_check.sh: [: too many arguments
can some one help me to fix this error/warning
22nd August 2011, 23:02
My little bash script for confirming that a file exists for a non linux user:
clear
echo “Enter a file to see if it exists:”
read file
echo “searching system……”
sleep 1
locate $file > file.finder
if [ $file != "1" ];then
echo “$file EXISTS, IN THESE PLACES:”
cat file.finder
sleep 4
rm file.finder
else
echo “$file doesn’t exist!”
fi
21st September 2011, 06:59
files=$(find /tmp -type f -name “*.cache” | wc -l)
if [ "$files" != "0" ]
then
echo “Cache files exist: do something with them”
else
echo “No cache files…”
fi
21st September 2011, 07:19
if ls /tmp | grep -sq *.cache
then
echo “Cache files exist: do something with them”
else
echo “No cache files…”
fi
9th October 2011, 21:34
WAYYYYYY too complicated, all of you.
If what you want is reasonably terse script that works entirely with bash builtins and doesn’t fail unless bash can’t expand the glob without blowing out its command line buffer, use nullglob in a subshell:
if test -n “$(shopt -s nullglob; cd /tmp; echo *.cache)”
then
echo Cache files exist
else
echo No cache files
fi
If you’d rather know that your script will always do the right thing, you don’t mind launching one external process to do the test and you want portability to non-bash shells, use find:
if test -n “$(find /tmp -maxdepth 1 -name ‘*.cache’ -print -quit)”
then
echo Cache files exist
else
echo No cache files
fi
20th December 2011, 10:48
Thanks Matt Simmons! the for loop made the most sense and worked for me.
All of the above was useful information, but I can’t see how anything beats the for loop, I was doing for files in `ls filename.0*` and I just needed to modify it so it didn’t use ls to get the file list. Not sure why I started out using ls, but I’m guessing I had a syntax issue until I used your example.
24th January 2012, 08:45
Just do it this way:
testpattern=whatever*
ok=0
for tmp in $testpattern
do
if [ -e $tmp ] ; then ok=1 ; fi
break
done
if [ $ok -eq 1 ]
then
echo “Matches”
else
echo “Does not match”
fi
24th January 2012, 08:47
Please note that my solution is somewhat similar to the one of Trevor. His solution however has a bug which occurs if the testpattern is for example “bla*” and a file literally named “bla*” (which is legal) exists. My solution handles this fine.