if command in find -exec

17,885

Solution 1

First, your snippet executes the command

echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi

because it needs its output to evaluate the command substitution. Since there is no file named {}, this produces the output

{} :
directory

Then the find command is executed with the arguments -exec, echo, {}, :, directory, so for every file, it outputs the file name followed by a space and : directory.

What you actually want to do is to execute the shell snippet echo {} :; … on each file found by find. This snippet must be executed by a shell spawned by find, not by the shell that starts find, since it is receiving data from find on its command line. Therefore you need to instruct find to run a shell:

find -exec sh -c 'echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi' \;

This is better, but still not right. It'll work with some (not all) find implementations if your file names don't contain any special characters, but since you are interpolating the file name in a shell script, you allow file names to execute arbitrary shell commands, e.g. if you have a file called $(rm -rf /) then the command rm -rf / will be executed. To pass file names to the script, pass them as separate arguments.

Also the first echo prints a newline after the colon. Use echo -n (if your shell supports it) or printf to avoid this.

find -exec sh -c 'printf "%s :" "$0"; if [ -f "$0" ]; then echo file; else echo directory; fi' {} \;

You can use -exec … {} + to group shell invocations, which is faster.

find -exec sh -c 'for x; do printf "%s :" "$x"; if [ -f "$x" ]; then echo file; else echo directory; fi; done' _ {} +

Solution 2

Another way for executing if; then; else; fi together with find is:

find |
while read p; do if [ -f "$p" ]; then echo file; else echo directory; fi; done
Share:
17,885

Related videos on Youtube

Esref
Author by

Esref

Updated on September 18, 2022

Comments

  • Esref
    Esref almost 2 years

    I was just trying to list all directories and files under current directory and also write if they are file or directory with the following command:

    find -exec echo `echo {} : ;if [ -f {} ]; then echo file; else echo directory;fi` \;
    

    I know it is a silly command, I can use other things like -type f or -type d, but I want to learn why that piece of code did not work as I expected. It just prints directory to all of them. For example while output of find is:

    .
    ./dir
    ./dir/file
    

    output of my code is :

    . : directory
    ./dir : directory
    ./dir/file : directory
    

    And output of

    echo `echo dir/file : ;if [ -f dir/file ]; then echo file; else echo directory;fi`
    

    is

    dir/file : file
    

    I am working on Ubuntu 14.10 and using find (GNU findutils) 4.4.2

    • Costas
      Costas over 9 years
      find -exec bash -c 'echo -n "{} : ";if [ -f "{}" ]; then echo file; else echo directory;fi' \;
    • Esref
      Esref over 9 years
      Thanks @Costas but I know that using bash or sh I can achieve my initial goal, but now I want to understand why if behaves different with the exec parameter.
    • Esref
      Esref over 9 years
      I already tried that and it gives the same output. find -exec echo echo "{}" : ;if [ -f "{}" ]; then echo file; else echo directory;fi \;
  • gerlos
    gerlos about 6 years
    Cool solution. It avoids complex quote patterns and subshell complications. You may have previously defined a shell function, and execute it on the files matching the "if" condition.