There are a few things wrong with this snippet, but surprisingly, the double file check isn't one of them.
First, the $()
construction around find
is wrong, as @EricRenouf shows. The correct way to do that would be to use something like
while read filename
do
source $filename
done < <(find ....)
which calls find in a subshell, and reads filenames into a filename
variable in the main shell (it's the find | while read x
idea on its head, but unfortunately not as portable)
Second, the -d
directory test is redundant; find
won't find any files if the directory doesn't exist, so testing for its existence doesn't make sense.
However, the -type f
argument to find
doesn't test the exact same thing as the -f
argument to test
. POSIX has the following to say about test
:
-f pathname
True if pathname resolves to an existing directory entry for a regular file. False if pathname cannot be resolved, or if pathname resolves to an existing directory entry for a file that is not a regular file.
and has this to say about find
:
-type c
The primary shall evaluate as true if the type of the file is c, where c is 'b', 'c', 'd', 'l', 'p', 'f', or 's' for block special file, character special file, directory, symbolic link, FIFO, regular file, or socket, respectively.
So, if you have a regular file that "cannot be resolved", then you have a file for which test -f
will fail, but find -type f
will succeed. One way in which to do that is to have a file in a directory that you can read, but that you cannot access:
wouter@gangtai:~$ ls -ld foo
drw-r--r--. 2 wouter wouter 4096 apr 1 18:38 foo
wouter@gangtai:~$ find foo -type f | while read file; do if [ -f $file ]; then echo $file tests -f; else echo $file does not test -f; fi; done
foo/bar does not test -f
wouter@gangtai:~$ ls -l foo
ls: cannot access 'foo/bar': Permission denied
total 0
-????????? ? ? ? ? ? bar
The type of the file (whether it is a directory, a regular file, or whatever) can be detected if you have r
, but not x
, permissions on the directory in which the file is stored. However, in order to be able to "resolve" it (i.e., call one of the stat()
functions on that file), you need the x
permission bit on that directory.
Of course, whether doing such a test in a shell scrippet for your .bashrc
is at all useful is a different matter. I would say not. However, that doesn't mean that the two tests are the same, and there are cases where this difference is important...
for f in $(find...)
construct. It will fail iffind
results contain IFS characters. Sadly, most answers here use the same error-prone technique (and even more sad, some of the posters are veteran members here) not to mention the accepted answer is using awhile..read
loop to process a list (which is bad practice too) and will fail for the very same reasons: word splitting ($filename
is not quoted so will fail for filenames containing IFS chars and even if it was quoted it would still fail - in theory - for file names containing newlines) – don_crissti May 10 '16 at 14:45