1

I have number of files in a directory and a list of expected files in it.

For example: files I have are:

  • file1
  • file2
  • file3

expected files are

  • file1
  • file2
  • file4

I'd like to test the dir and find that file4 is NOT in it.

1
  • 1
    You have accepted an answer that shows you how to print all file names in an array that do not actually exist in your directory, which is quite the opposite of your question title (list names of files that exist but are not present in the array)... Mar 28, 2017 at 22:08

4 Answers 4

4

Assuming you have an array with the filenames and want to find out which ones are not present in the directory, just loop through the array, and check if the files exist. (-f tests for regular files, -e for any type)

files=(file1 file2 file4)
for f in "${files[@]}" ; do 
    [ -f "$f" ] || echo "$f: not found"
done 

The opposite is similar, but requires a double-loop or turning the array into an associative array. With the double-loop:

files=(file1 file2 file4)
for f in * ; do
        found=0
        for g in "${files[@]}" ; do
                [ "$f" = "$g" ] && found=1
        done
        [ "$found" = 0 ] && echo "$f: in directory but not listed"
done 
4
  • 1
    ls -ld "${array[@]}" 1>/dev/null would be a quick and dirty way to do it although that doesn't make any distinction between regular files and the rest... Mar 28, 2017 at 22:35
  • Strictly speaking, -f is regular file or symlink to regular file that can be stat()ed and -e for files (of any type) that can be stat()ed. See also [ -e "$file" ] || [ -L "$file" ] (or ls -d -- "$file") for files of any type that can be lstat()ed. For directory entries, you could use globbing. Mar 28, 2017 at 23:31
  • Just to be clear - my comment on the question was an (ignored, apparently) attempt to convince the OP to clarify the question and fix the title. Your initial answer was (and still is) fine as far as I am concerned (it shows the canonical way to print the elements of an array which do not exist as file names) Mar 29, 2017 at 11:15
  • @don_crissti, yeah, I didn't take it like that, I just noticed the difference (which I missed when first writing the answer), but didn't want to stomp on the question either. So adding the answer to the other question was the obvious other thing to do.
    – ilkkachu
    Mar 29, 2017 at 11:22
4

For array union and subtraction, look at zsh instead of bash.

$ expected=(file1 file2 file4)
$ existing=(file1 file2 file3) # or existing=(file*) to use globbing

$ echo missing: ${expected:|existing}
missing: file4
$ echo found: ${expected:*existing}
found: file1 file2
$ echo unexpected: ${existing:|expected}
unexpected: file3

Mnemonic (mine at least, I don't know if they are the official ones):

  • ${A:|B}: elements of $A bar those of $B
  • ${A:*B}: elements of $A starring those of $B.
0

If you've got to test for a bunch of files, then a function is a better solution. Otherwise, a simple ls of the file will suffice! Below is the output you will get when the file you're looking for isn't there:

../theDirIWantToCheck $ ls theFileIWantToFind.txt
ls: theFileIWantToFind.txt: No such file or directory

I would also recommend utilizing tab-completion. Give the ls command and then start typing the name of any file you expect. You only have to give a few characters and then press tab (if that doesn't work, press tab twice). All file names matching that pattern will be presented if they exist.

$ ls fi<tab>
file1.txt file2.txt file3.txt file_new.txt filetypes.txt

You can also pass ls multiple arguments to check on multiple files:

$ ls file1.txt file2.txt file3.txt
...

If the files share the same pattern prefix (such as above: file1, file2, file3), you can use brace expansion. This is like passing all three arguments in a condensed form. The .. represents a range.

$ ls file{1..3}.txt
ls: file1.txt: No such file or directory
ls: file2.txt: No such file or directory
ls: file3.txt: No such file or directory

Alternatively, you could use a wildcard to pass all the possible matching patterns with the common prefix "file" (such as file1.txt, file_other.txt):

$ ls file*
...

Finally, you can pass all the arguments in an array to ls and let it do the printing for you, rather than using echo. What's more, if you use the -l option to ls, when the files do exist, you can get some extra details about them, such as permissions, file size, and last modification date/time.

0
perl -le '-e or print for qw/file1 file2 file4/'

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .