views:

391

answers:

5

I want to check whether a directory has files or not in bash. My code is here.

for d in {,/usr/local}/etc/bash_completion.d ~/.bash/completion.d do
[ -d "$d" ] && [ -n "${d}/*" ] &&

        for f in $d/*; do                                                                                                           
            [ -f "$f" ] && echo "$f" && . "$f"                        

        done                                                                                                                        
    done

The problem is that "~/.bash/completion.d" has no file. So, $d/* is regarded as simple string "~/.bash/completion.d/*", not empty string which is result of filename expansion. As a result of that code, bash tries to run

. "~/.bash/completion.d/*"

and of course, it generates error message.

Can anybody help me?

+4  A: 

If you set the nullglob bash option, through

shopt -s nullglob

then globbing will drop patterns that don't match any file.

Martin v. Löwis
kjshim's code seems to work for me, as is, without setting `nullglob` (`shopt nullglob` returns "off") . Any ideas why that might be?
Dennis Williamson
Perhaps the directories you have been trying were not empty?
Martin v. Löwis
`ls -la test` shows `.` and `..`
Dennis Williamson
A: 

You could use find directly in the following way:

for f in $(find {,/usr/local}/etc/bash_completion.d ~/.bash/completion.d -maxdepth 1 -type f);
do echo $f; . $f;
done

But find will print a warning if some of the directory isn't found, you can either put a 2> /dev/null or put the find call after testing if the directories exist (like in your code).

tonfa
A: 
find() {
 for files in "$1"/*;do
    if [ -d "$files" ];then
        numfile=$(ls $files|wc -l)
        if [ "$numfile" -eq 0 ];then
            echo "dir: $files has no files"
            continue
        fi
        recurse "$files"
    elif [ -f "$files" ];then
         echo "file: $files";
        :
    fi
 done
}
find /path
A: 

Another approach

# prelim stuff to set up d
files=`/bin/ls $d`
if [ ${#files} -eq 0 ]
then
    echo "No files were found"
else
    # do processing
fi
GreenMatt
A: 
# NOTE: using only bash builtins
# Assuming $d contains directory path

shopt -s nullglob

# Assign matching files to array
files=( "$d"/* )

if [ ${#files[@]} -eq 0 ]; then
    echo 'No files found.'
else
    # Whatever
fi

Assignment to an array has other benefits, including desirable (correct!) handling of filenames/paths containing white-space, and simple iteration without using a sub-shell, as the following code does:

find "$d" -type f |
while read; do
    # Process $REPLY
done

Instead, you can use:

for file in "${files[@]}"; do
    # Process $file
done

with the benefit that the loop is run by the main shell, meaning that side-effects (such as variable assignment, say) made within the loop are visible for the remainder of script. Of course, it's also way faster, if performance is an issue.
Finally, an array can also be inserted in command line arguments (without splitting arguments containing white-space):

$ md5sum fileA "${files[@]}" fileZ

You should always attempt to correctly handle files/paths containing white-space, because one day, they will happen!

Peter