0

Can someone confirm that the following one liner will produce the total human readable size of all the directories which FIND locates as having log in the name?

find -type d -name *log* | du -hcs

2 Answers 2

0

Ready?

find . -type d -name '*log*' -print0 | xargs -0 du -hcs

  • Wrap the *log* in single quotes or else the shell will expand it before find sees it.
  • Use -print0 to separate the find output by null characters for xargs
  • use xargs -0 to put each null-separated filename from find into the command line of du

Easy, right? :)

6
  • They both seem to do the job - yours outputs each directory as it finds it though where mine only prints the total. Not sure which one is 'better', I suppose it depends on the need.
    – Tony
    Mar 13, 2014 at 18:54
  • Actually strike that comment :) - both came up with drastically different numbers hrm.
    – Tony
    Mar 13, 2014 at 19:04
  • Still looking for feedback here on why the two values are different?
    – Tony
    Mar 14, 2014 at 15:03
  • Can you post a tarball of your test-case directory tree somewhere?
    – cxw
    Mar 28, 2014 at 12:04
  • 1
    Oh! It's because "du" doesn't take its input from the command line. In the original command line, the output of find goes to the standard input of du and gets ignored. As a result, du -hcs reports the total size of the whole current directory. The xargs in the answer moves those filenames from standard input to the command line where du will see them. In my /var on cygwin, the command line from the question gives the same output as du -hcs without the find: 79M .. The command line from the answer gives 1.1M ./log, indicating it only processed ./log.
    – cxw
    Mar 28, 2014 at 12:09
0

Your one-liner has two major issues:

  • The command find | du passes the output of find to the stdin of du. However, du completely ignores the standard input, so the find filter is completely ignored, and the command you provided is in fact equivalent to du -hcs. The solution is to use xargs, which passes the list of files as arguments to du, or du --files0-from=- (GNU coreutils).
  • You need to quote the asterisks in your file name pattern either with backslashes (\*log\*) or with single quotes ('*log*'), otherwise your shell will try to perform filename expansion and find will possibly not see the asterisks at all.

Some more improvements:

  • In case of matches inside matched directories like ./log/log/, both ./log/ and ./log/log/ would be passed to du. The second directory would be ignored by du -s, but you can increase performance of the find command by adding the -prune action. It will make sure that find does not further descend into matched directories.
  • I'm adding -print0 (and -0 to xargs) to avoid potential problems with e.g. newlines in file names, see serverfault.
  • I'm adding the path (.) to make the find command compatible to POSIX (and BSD).

This is the final one-liner:

find . -type d -name '*log*' -prune -print0 | xargs -0 du -hcs

Keep in mind that -name is case-sensitive. Personally I almost always use -iname, which is case-insensitive.


Additional note: if you're using GNU coreutils' du, you can use its --files0-from=- option and pass the file paths directly to du, omitting xargs. According to the manual, ”this is useful when the list of file names is so long that it may exceed a command line length limitation.“

find . -type d -name '*log*' -print0 | du --files0-from=- -hcs

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .