Shell – Blank lines when executing “grep | xargs” in a “find -exec”

findgrepshellxargs

I'm trying to list all directories of a web server that are blocked by Apache via a .htaccess (containing deny from all).
I've managed to get the list of blocking .htaccess but when I try to extract the directory path using dirname I have some errors:

  1. List of .htaccess files:

    find . -type f -name ".htaccess"
    ./.htaccess
    ./config/.htaccess
    ./files/.htaccess
    ./plugins/webservices/.htaccess
    ./plugins/webservices/scripts/.htaccess
    ./install/mysql/.htaccess
    ./scripts/.htaccess
    ./locales/.htaccess
    
  2. List of blocking .htaccess files:

    find . -type f -name ".htaccess" -exec sh -c "grep -Eli '^deny from all$' '{}'" \;
    ./config/.htaccess
    ./files/.htaccess
    ./plugins/webservices/scripts/.htaccess
    ./install/mysql/.htaccess
    ./scripts/.htaccess
    ./locales/.htaccess
    
  3. Error come the errors. Same list as in 2. but using xargs and dirname to get the containing directory:

    find . -type f -name ".htaccess" -exec sh -c "grep -Eli '^deny from all$' '{}' | xargs dirname" \;
    dirname: missing operand
    Try dirname --help' for more information
    ./config
    ./files
    dirname: missing operand
    Try dirname --help' for more information
    ./plugins/webservices/scripts
    ./install/mysql
    ./scripts
    ./locales
    
  4. Debug attempt of list 3: we can see 2 blank lines where the 2 errors were:

    find . -type f -name ".htaccess" -exec sh -c "grep -Eli '^deny from all$' '{}' | xargs echo" \;
    
    ./config/.htaccess
    ./files/.htaccess
    
    ./plugins/webservices/scripts/.htaccess
    ./install/mysql/.htaccess
    ./scripts/.htaccess
    ./locales/.htaccess
    

Theses 2 blank lines obviously matches the 2 .htaccess files that were ignore because they don't contains deny from all. I don't understand why I get these in lists 3. and 4. but not in 2.

Best Answer

It's failing because when the grep doesn't match, you're not passing anything to xargs.

For example:

  1. find gets ./.htaccess and calls your -exec.
  2. The grep doesn't match anything in the file, so it outputs nothing
  3. xargs launches dirname without any arguments, and so dirname thinks it was just misused and displays its help message.

The proper way to do this:

find . -type f -name .htaccess -exec grep -iq '^deny from all$' {} \; -printf '%h\n'
Related Question