2

I have moved a file, which makes a reference to four other files with the same parent directory. And now I am stock trying to figure out which parent directory that is since there a number of files with the same name in my build environment. But I don't want to take any chances and copy the wrong file. So I want to know if there a way to tell Linux find command or some other command to give me the results matching two or more strings. So I only want to see matches for locations which contain files:

foo & bar & buz

I can always pipe this into grep for the desired common directory, but if there is an easier way then I would love it. Something like:

common_direcoty/foo & common_directory/bar & common_directory/buz 

In other words, I am looking for the copy of these files with a common directory. I am not looking for the file that matches the name alone.

Here is the output of the command which gave me what I was after:

sansari@ubuntu:~/WORKING_DIRECTORY$ find . -name io.h -exec sh -c '[ -e "${0%/*}/kernel.h" ] && [ -e "${0%/*}/init.h" ]' {} \; -print
./include/linux/io.h
sansari@ubuntu:~/WORKING_DIRECTORY/include/linux$ ls module.h
module.h
sansari@ubuntu:~/WORKING_DIRECTORY/include/linux$ ls kernel.h 
kernel.h
sansari@ubuntu:~/WORKING_DIRECTORY/include/linux$ ls init.h
init.h
sansari@ubuntu:~/WORKING_DIRECTORY/include/linux$ ls io.h
io.h

3 Answers 3

2

One way is to look for one of the file names (pick the rarest one if you know which one it is) then filter the matches to retain only those where the other file exists.

find . -name foo -exec sh -c '[ -e "${0%/*}/bar" ] && [ -e "${0%/*}/buz" ] && echo "${0%/*}"' {} \;

You can use the shell snippet's return code if you want to use a find action, but that would apply to the first file, not to the directory. For example the following snippet prints output of the form ./path/to/foo:

find . -name foo -exec sh -c '[ -e "${0%/*}/bar" ] && [ -e "${0%/*}/buz" ]' {} \; -print

If you need to pass the names as arguments:

find_multiple_files () {
  root=$1; shift
  name1=$2; shift
  find "$root" -name "$name1" -exec sh -c 'dir=${0%/*}; for x; do [ -e "$dir/$x" ] || exit 1; done; echo "$dir"' {} "$@" \;
}
find_multiple_files . foo bar buz

An alternative approach is to generate lists of names and filter them. This is a little more cumbersome and less efficient, but it has the advantage that you can switch from find to locate if you have an index of file names. I assume that the file names don't contain newlines.

locate foo |              # or: find -name foo
sed 's!/[^/]*$!!' |
xargs -I {} sh -c 'for x; do [ -e "$0/$x" ] || exit 1; done; echo "$0"' {} bar buz
4
  • @Everyone- Thanks. all the answers were spot on. I had to pick one to close the case however. Jul 1, 2015 at 8:20
  • 1
    @user3326293 I don't understand why you picked that answer: it doesn't answer your question. It lists directories containing only foo, or only bar, or only buz. Your question asks for directories containing all these files. If you made a mistake when asking the question, please edit the question to make it match the accepted answer; or better, leave the question alone, accept an answer that's correct, and ask the different question in a separate post. Jul 1, 2015 at 8:42
  • I agree. I am just getting around trying it out. I may be doing something wrong. But as it stands, it is not giving me what I am after. I appreciate your advice and everyone else. Jul 1, 2015 at 10:26
  • @Gills- You know? Man, your way worked. I may not have followed the other two methods, but I got just one directory following your method. Thank you all. Thanks; you guys saved me. Jul 1, 2015 at 10:36
0

I am unaware of any find option that can do it all by itself. Using greps you can achieve that - put all the file names that you want to find in a separate file , say /tmp/files_to_find, with the following pattern

In your case files_to_find would be

\/foo$
\/bar$
\/buz$

Then issue the following command

find root_dir_to_search_in | grep -f /tmp/files_to_find | xargs -I{} dirname {} | sort | uniq -c | grep `wc -l /tmp/files_to_find | awk '{print $1}'`
0

You should be able to group find patterns with -or and -and to select different patterns. I don't use such options but its

$ find . \( -name foo -or -name bar -or -name buz \) -and -path '*/G/*'|9 mc
./G/buz ./G/bar ./G/foo

where G = common_directory. I tested this against a file structure

$ find . -name foo -or -name bar -or -name buz|9 mc
./G/buz ./G/bar ./G/foo ./F/buz ./F/bar ./F/foo

where I also have foo,bar and baz in the non common_directory.

The 9 mc command is the multi column formatter from p9p, just to tune the output.

1
  • This lists directories containing any of the files, not directories containing all the files. This is not what the question asks, although from the comments it may be the question that user3326293 meant to ask. Jul 1, 2015 at 8:41

You must log in to answer this question.

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