views:

334

answers:

5

I'm using find for a task and I noticed that when I do something like this:

find `pwd` -name "file.ext" -exec echo $(dirname {}) \;

it will give you dots only for each match. When you s/dirname/basename in that command you get the full pathnames. Am I screwing something up here or is this expected behavior? I'm used to basename giving you the name of the file (in this case "file.ext") and dirname giving you the rest of the path.

+4  A: 

Consider the following script:

#!/bin/sh
set -x
find `pwd` -name "file.ext" -exec echo $(dirname {}) \;

set -x shows how the expansion works and what the final command is. When run, it gives the following output:

++ pwd
++ dirname '{}'
+ find /home/kibab -name file.ext -exec echo . ';'

So, the first thing that is expanded is the pwd. Second is $(dirname {}). The result of those two commands is then dropped into the find command. Thus, you're telling find to -exec echo ., so you're seeing the expected output.

When you substitute basename for dirname, the expansion still takes places, but the results of the expansion are different:

  1. pwd is expanded to the current path. In my example above, the result is /home/kibab
  2. basename {} is executed. The result of this command is {}.
  3. The find command is executed with the above substitutions in place. The final command executed looks like this:

    find /home/kibab -name '*.png' -exec echo '{}' ';'

Upon inspecting the above command, you'll notice that the command now simply echo's whatever file was found.

Perhaps you want something like this?

find `pwd` -name "file.ext" -printf "%f\n"
Kaleb Pederson
@Martin - Good point, explanation added.
Kaleb Pederson
This helped a ton. I didn't know about set -x, and the only context I've used basename/dirname was when I was setting it to a variable.
temp2290
You might want to take a look at `set -v` and some of set's other options as well. Good luck.
Kaleb Pederson
A: 

I don't know why you're getting that, but try this:

find `pwd` -name file.ext |xargs -l1 dirname
Paul Tomblin
A: 

This is because find prints paths relative to the path it searches from. If you tried this search from / you would get `pwd` for each path.

pajton
+3  A: 

$(dirname {}) gets evaluated by the shell before being passed to find. The result of the evaluation is ., so you're just telling find to execute echo . for each file it finds.

You don't need to echo the result of dirname. Just running it directly will output the directory names:

find `pwd` -name "file.ext" -exec dirname {} \;
Phil Ross
+2  A: 

you do not have to call dirname() for each file found. with GNU find, you can use -printf and its faster this way

find /path -type f -iname "*.ext" -printf "%h\n"
ghostdog74