7

How do I execute a command where a file is found?
Consider I've an directory named testdir that contains the following:

$ ls -R testdir/
testdir/:
dir1  dir2  dir3  dir4  dir5

testdir/dir1:
doc1.pdf

testdir/dir2:
file1.txt

testdir/dir3:
doc2.pdf

testdir/dir4:
file2.txt

testdir/dir5:
doc5.pdf

Now I want to perform an action (execute a command) where find finds a certain file/file type. For example let me find *.pdf:

$ find . -name '*.pdf'
./testdir/dir3/doc2.pdf
./testdir/dir5/doc5.pdf
./testdir/dir1/doc1.pdf

Now suppose I want to execute a command (for example say touch file) where the above command finds file(s). In other words, I want to create a file named file in each directory where at least one .pdf was found so that I get:

$ ls -R testdir/
testdir/:
dir1  dir2  dir3  dir4  dir5

testdir/dir1:
doc1.pdf  file

testdir/dir2:
file1.txt

testdir/dir3:
doc2.pdf  file

testdir/dir4:
file2.txt

testdir/dir5:
doc5.pdf  file

How do I accomplish such a work?
May be for every time file found, cd to where file exist and perform a command recursively.
I know that find has awesome feature: -exec but I can't get it to work.


This is only an example for getting an idea about what I want to do. Broadly: How to execute an command where file(s) are found (by find) recursively?

  • welcome for tag correction if any. – Pandya Oct 30 '15 at 15:01
  • 3
    your find may have the option -execdir for this. – meuh Oct 30 '15 at 15:03
11

If you run this command your touch file will be run, potentially multiple times, from the directory in which the command has been started:

find -name '*.pdf' -exec touch file \;

On the other hand, if you run this variant, each instance of the command will be run in the target file's directory:

find -name '*.pdf' -execdir touch file \;

In both cases you can see this in action by substituting the touch file with either echo {} and/or pwd.


From manpage:

-execdir command ;
-execdir command {} +

    Like -exec, but the specified command is run from the subdirectory containing the matched file, which is not normally the directory in which you started find.

2

You can extract the filename's directory with dirname and take it from there, something like this:

find . -name "*.pdf" -type f -exec bash myscript {} \;

where the file myscript contains the following:

dir=$(dirname "$1")
cd "$dir"
touch file
  • it is not working, did you try it? It is making file only in folder it first finds – Edward Torvalds Oct 30 '15 at 15:15
  • I know why it is not working, now editing your answer :) – Edward Torvalds Oct 30 '15 at 15:17
2

With zsh you could use glob qualifiers to select e.g. only .pdf files and via modifiers save the directory names in an array with unique elements and then cd into each of those directories and run your command. This way you are running your command only once in each directory, irrespective of the number of .pdfs that were found in that directory:

dirlist=(**/*.pdf(.:a:h))
for d in ${(u)dirlist[@]}
  (cd $d && touch file)

or

typeset -U dirlist
dirlist=(**/*.pdf(.:a:h))
for d in ${dirlist}
  (cd $d && touch file)

You can further combine modifiers and qualifiers, e.g. to glob for regular files (hidden and non-hidden) with the extension .bkp and save unique directory names in an array:

dirlist=(**/*.bkp(D.:a:h))
0

Look no further than entr:

https://bitbucket.org/eradman/entr

What you can do is let entr watch a specified directory. When find locates the file, touch a file called, say, triggerfile.txt in that directory and let entr take over.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.