8

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.

CDuv
  • 362
  • By the way, putting the {} within the shell command is a major security risk. If you ever need that effect (which you don't here, anyway), use this pattern instead. (Set the found filename as a shell positional parameter and refer to it as such.) – Wildcard May 13 '16 at 22:57

2 Answers2

2

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'
phemmer
  • 71,831
  • Thanks: works just fine and is simpler. I now do get why the blank lines: is there a way to skip/ignore them prior to calling xargs (not all cases can be resolved only via find :p)? – CDuv May 23 '14 at 10:28
  • 1
    @CDuv --no-run-if-empty. However find is much safer. It will handle things that the grep/pipe method can't, such as filenames with newlines in them. – phemmer May 23 '14 at 12:25
0

You've got this error:

dirname: missing operand. Try dirname --help' for more information

because dirname has missing operand (nothing was passed as an argument). This happened, because grep returned empty results.

If you're using GNU xargs, you can use -r (--no-run-if-empty) to not run the command, when input is empty (does not contain any nonblanks).

To make it work with BSD xargs, you probably need to rewrite you command to check whether the input is empty and run the command accordingly. Or just ignore the error by suppressing stderr, for example:

find . ... -exec sh -c "grep ... | xargs dirname 2> /dev/null || true" ';'

Or you can use while loop, so you can avoid parsing empty files or having problems with spaces in filenames, e.g.

find . -type f -name ".htaccess" -print0 | while IFS= read -r -d '' file; do

    grep -Eli '^deny from all$' "$file" | while IFS= read -r -d '' deny_file; do
      dirname "$deny_file"
    done

done
kenorb
  • 20,988