Discussion:
tree with dir size
(too old to reply)
Northwind
2024-05-30 22:30:01 UTC
Permalink
Hello,

is there a command that shows dir/subdir structure like `tree`, but for
each dir has the size in results as well?

Thanks.
e***@gmx.us
2024-05-30 23:00:01 UTC
Permalink
Post by Northwind
Hello,
is there a command that shows dir/subdir structure like `tree`, but for each
dir has the size in results as well?
It looks like "tree --du" should do it, but "tree -d --du -h" says

├── [452K] Documents

when du says it's 787M.

--
When we've nuked the world to a cinder, the cockroaches picking
over the remains will be crawling over the remaining artifacts
and wondering what "PC LOAD LETTER" means. -- PC / ASR
Greg Wooledge
2024-05-30 23:00:01 UTC
Permalink
Post by e***@gmx.us
It looks like "tree --du" should do it, but "tree -d --du -h" says
├── [452K] Documents
when du says it's 787M.
Well, that sounds like one of the numbers includes subdirectories and
the other only includes files in the immediate directory.

The question is: which one do you want?
Northwind
2024-05-30 23:00:01 UTC
Permalink
both the size of current path and subdir should be expected.

thanks.
Post by Greg Wooledge
The question is: which one do you want?
Greg Wooledge
2024-05-30 23:10:01 UTC
Permalink
Post by Northwind
both the size of current path and subdir should be expected.
According to the man page, that's what it does.

I just installed tree and tried it. There's a subtle behavior that I
did not expect:

hobbit:/usr/local$ tree -d --du -h
[3.9M] .
├── [4.0K] bin
├── [4.0K] etc

Those number are definitely not totals for the files in a directory.
Compare:

hobbit:/usr/local$ tree --du -h | less
[627M] .
├── [ 81M] bin
│   ├── [7.6K] addcr

It looks like the -d option makes it only look at the size of the raw
directory (4096 bytes, as in

hobbit:/usr/local$ ls -ld bin
drwxr-sr-x 2 root staff 4096 Feb 17 15:14 bin/

) rather than the total of the files within the directory.

Solution: don't use -d. But then you get all the files listed instead
of just the directories.

If there's a way to get the pruning of files of -d plus the total-printing
of omitting -d then I don't know what it is.

The best workaround I can immediately see is to include -F to put trailing
slashes on the directories, and then grep for those.

hobbit:/usr/local$ tree --du -h -F | grep /$ | head
[627M] ./
├── [ 81M] bin/
├── [4.0K] etc/
├── [4.0K] games/
├── [126K] include/
│   └── [122K] opus/
├── [ 42M] lib/
│   ├── [3.4M] nethackdir/
│   │   ├── [4.0K] save/
│   ├── [4.3K] pkgconfig/
e***@gmx.us
2024-05-31 00:10:01 UTC
Permalink
Post by Greg Wooledge
Post by e***@gmx.us
It looks like "tree --du" should do it, but "tree -d --du -h" says
├── [452K] Documents
when du says it's 787M.
Well, that sounds like one of the numbers includes subdirectories and
the other only includes files in the immediate directory.
From du(1):

--du For each directory report its size as the accumulation of
sizes of all its files and sub-directories (and their files,
and so on). The total amount of used space is also given in
the final report (like the 'du -c' command.) This option
requires tree to read the entire directory tree before
emitting it, see BUGS AND NOTES below. Implies -s.

OK, now for "du -c". From du(1):

-c, --total
produce a grand total

Well, that doesn't help much.
Post by Greg Wooledge
The question is: which one do you want?
I'd like the size of the entire tree, and I think that's what OP wants too.

I think "du -h -S -s Documents/" gives just the files in Documents, and not
its subdirectories, and it gives 269M. "ls -ldh Documents/" says the
directory itsely takes up 36k, so I'm not sure where "tree" got that number.
Greg Wooledge
2024-05-31 00:20:01 UTC
Permalink
Post by e***@gmx.us
Post by e***@gmx.us
It looks like "tree --du" should do it, but "tree -d --du -h" says
├── [452K] Documents
I think "du -h -S -s Documents/" gives just the files in Documents, and not
its subdirectories, and it gives 269M. "ls -ldh Documents/" says the
directory itsely takes up 36k, so I'm not sure where "tree" got that number.
Hmm. Guessing again, it might be the sum of all the "-d" raw directory
sizes of the directory and its subdirectories, but still ignoring files?
I can't fathom why the programmer(s) decided to do this, because it
isn't useful to anybody, but it's the only answer I can come up with.

hobbit:~$ mkdir -p /tmp/x/y
hobbit:~$ tree --du -dh /tmp/x
[8.0K] /tmp/x
└── [4.0K] y

hobbit:~$ cp /vmlinuz /tmp/x
hobbit:~$ tree --du -dh /tmp/x
[8.0K] /tmp/x
└── [4.0K] y

Confusing and useless. I still don't have a better answer than this:

hobbit:~$ tree --du -Fh /tmp/x | grep /$
[7.8M] /tmp/x/
└── [4.0K] y/
Franco Martelli
2024-05-31 19:20:01 UTC
Permalink
Post by Greg Wooledge
hobbit:~$ tree --du -Fh /tmp/x | grep /$
[7.8M]/tmp/x/
└── [4.0K] y/
It could be improved adding the "-a" switch to show also the hidden
directories and the "--color" switch to the "grep" command but this
sadly doesn't show the expected result (colorized directories) I don't
know why:

~$ tree --du -Fah /tmp/x | grep --color /$

Me too needed to summarize the directory's size thus I wrote a bash
function to accomplish this and added it to my /etc/profile.d/local.sh file:

duhs() { IFS=$'\n\r' ; for d in $(/usr/bin/find $1 -maxdepth 1 -type d
|/usr/bin/sort) ; do du -hs $d ; done }

It doesn't show the output like "tree" does but it works nicely.

Cheers,
--
Franco Martelli
Greg Wooledge
2024-05-31 20:10:01 UTC
Permalink
Post by Franco Martelli
Post by Greg Wooledge
hobbit:~$ tree --du -Fh /tmp/x | grep /$
[7.8M]/tmp/x/
└── [4.0K] y/
It could be improved adding the "-a" switch to show also the hidden
directories and the "--color" switch to the "grep" command but this sadly
~$ tree --du -Fah /tmp/x | grep --color /$
You're only coloring the trailing / characters. If you want everything
from after the last space to the end of the line, you'd want:

tree --du -Fh /usr/local | grep --color '[^[:space:]]*/$'

Of course this fails to colorize the entire directory name if there's
a space in it.
Post by Franco Martelli
Me too needed to summarize the directory's size thus I wrote a bash function
duhs() { IFS=$'\n\r' ; for d in $(/usr/bin/find $1 -maxdepth 1 -type d
|/usr/bin/sort) ; do du -hs $d ; done }
This, too, will fail if there are spaces in a directory name. You'd need
to quote the "$1" and "$d" at the very least. The for d in $(find) part
is also quite fragile; I see you tried to work around that by using IFS,
but that's not a full fix. Also, you never declared IFS as a local
variable in this function, so you will be altering IFS in any shell that
actually runs this function.

Consider this one instead:

hobbit:~$ duhs() { du -hs "${1:-.}"/*/; }
hobbit:~$ duhs /usr/local
81M /usr/local/bin/
4.0K /usr/local/etc/
4.0K /usr/local/games/
136K /usr/local/include/
45M /usr/local/lib/
29M /usr/local/man/
8.0K /usr/local/sbin/
2.2M /usr/local/share/
484M /usr/local/src/
44M /usr/local/tcl8.6/

In bash, a glob that ends with / will match directories only. Globs are
always sorted, so you can also drop the call to sort. The only issue
with this version is if the number of subdirectories is so large that
it triggers the "Argument list too long" error.

If you want to avoid that, you can use xargs -0:

duhs() { printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh; }

As printf is a bash builtin, it can handle an unlimited number of
arguments. So this form should work in all cases.
David Wright
2024-06-01 02:40:01 UTC
Permalink
Post by Greg Wooledge
Post by Franco Martelli
Post by Greg Wooledge
hobbit:~$ tree --du -Fh /tmp/x | grep /$
[7.8M]/tmp/x/
└── [4.0K] y/
It could be improved adding the "-a" switch to show also the hidden
directories and the "--color" switch to the "grep" command but this sadly
~$ tree --du -Fah /tmp/x | grep --color /$
You're only coloring the trailing / characters. If you want everything
tree --du -Fh /usr/local | grep --color '[^[:space:]]*/$'
Of course this fails to colorize the entire directory name if there's
a space in it.
If a coloured ] is unimportant, I suppose you could use:

tree --du -Fh whatever | grep --color '][[:space:]][[:space:]].*/$'

Cheers,
David.
Greg Wooledge
2024-06-01 03:20:01 UTC
Permalink
Post by David Wright
tree --du -Fh whatever | grep --color '][[:space:]][[:space:]].*/$'
You don't need to count spaces. Just '].*/$' would suffice. We already
know we want to start with the first ] character on the line, no matter
how many spaces follow it.

I really question the usefulness of colorizing the directory names,
but since we're already this far down the rabbit hole, we might as
well light some dynamite to make the hole deeper. I'm sure it's safe!

We're using GNU grep for coloring, so we can also use its PCRE syntax
to do "lookbehind" voodoo:

tree --du -Fh /usr/local | grep --color -P '(?<=]).*/$'

which means "start matching after a ] but don't include the ] in the
match".
Lee
2024-06-01 14:20:01 UTC
Permalink
Post by Greg Wooledge
Post by David Wright
tree --du -Fh whatever | grep --color '][[:space:]][[:space:]].*/$'
You don't need to count spaces. Just '].*/$' would suffice. We already
know we want to start with the first ] character on the line, no matter
how many spaces follow it.
I really question the usefulness of colorizing the directory names,
but since we're already this far down the rabbit hole, we might as
well light some dynamite to make the hole deeper. I'm sure it's safe!
We're using GNU grep for coloring, so we can also use its PCRE syntax
tree --du -Fh /usr/local | grep --color -P '(?<=]).*/$'
which means "start matching after a ] but don't include the ] in the
match".
Or use '\K' to cause previously matched characters to not be included
in the match:

tree --du -Fah . | grep --color -P '[^]]*] \K.*/$'

(which required entirely too much RTFMing to learn about '\K')

Regards,
Lee
Franco Martelli
2024-06-03 14:00:01 UTC
Permalink
Hi Greg,
(sorry for the answer's late but I turn off the PC during the weekend) :(
Post by Greg Wooledge
Post by Franco Martelli
It could be improved adding the "-a" switch to show also the hidden
directories and the "--color" switch to the "grep" command but this sadly
~$ tree --du -Fah /tmp/x | grep --color /$
You're only coloring the trailing / characters. If you want everything
tree --du -Fh /usr/local | grep --color '[^[:space:]]*/$'
I realized why "tree" command doesn't colorized the directories: "tree"
detects that its std output goes through a pipe and therefore it
disables the escaped code to colorize (like also "dmesg" does).
To avoid this behavior you must use the "unbuffer" command:

unbuffer tree --du -Fah /usr/local | grep /$

You can also try:

~# unbuffer dmesg | grep snd

/usr/bin/unbuffer is part of the "expect" package.
Post by Greg Wooledge
duhs() { printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh; }
As printf is a bash builtin, it can handle an unlimited number of
arguments. So this form should work in all cases.
Thank you very much for revolutionizing my duhs() function, but sadly
this version omits to show the hidden directories. Do you have a version
that it shows also those directories? For me it's so hard to figure out
what the "${1:-.}"/*/ block does.
Again, is "man 7 glob" the right read or do you have a better one?

Cheers,
--
Franco Martelli
Greg Wooledge
2024-06-03 14:40:01 UTC
Permalink
Post by Franco Martelli
Post by Greg Wooledge
Post by Franco Martelli
It could be improved adding the "-a" switch to show also the hidden
directories and the "--color" switch to the "grep" command but this sadly
~$ tree --du -Fah /tmp/x | grep --color /$
You're only coloring the trailing / characters. If you want everything
tree --du -Fh /usr/local | grep --color '[^[:space:]]*/$'
I realized why "tree" command doesn't colorized the directories: "tree"
detects that its std output goes through a pipe and therefore it disables
the escaped code to colorize (like also "dmesg" does).
unbuffer tree --du -Fah /usr/local | grep /$
According to tree(1) there's a -C option which should force tree's
coloring to be always on, even when stdout goes to a pipe. But tree
never colors anything for me, even with -C and no pipe, so I don't know
what else is needed.
Post by Franco Martelli
Post by Greg Wooledge
duhs() { printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh; }
As printf is a bash builtin, it can handle an unlimited number of
arguments. So this form should work in all cases.
Thank you very much for revolutionizing my duhs() function, but sadly this
version omits to show the hidden directories. Do you have a version that it
shows also those directories? For me it's so hard to figure out what the
"${1:-.}"/*/ block does.
"$1" is the first argument given to the function. It can also be written
as "${1}".

"${1:-default}" is the first argument given to the function, *or* the
constant string 'default' if the first argument is empty or not given.
Therefore, "${1:-.}" is "the first argument, or default to . if no
argument is given".

Since that part is quoted, whatever value is used is taken as a string,
with no word splitting or globbing applied. That means it will handle
directory names that contain spaces, asterisks, etc.

Given the glob "x"/*/ the shell will look for all the subdirectories
of "x". The trailing / forces it to ignore anything that isn't a
directory.

You can see it in action:

hobbit:~$ printf '%s\n' /usr/local/*/
/usr/local/bin/
/usr/local/etc/
/usr/local/games/
/usr/local/include/
/usr/local/lib/
/usr/local/man/
/usr/local/sbin/
/usr/local/share/
/usr/local/src/
/usr/local/tcl8.6/

So, putting it all together, that glob says "all the subdirectories of
the first argument, or all the subdirectories of . if there was no
argument given".

Getting it to include directory names that begin with . is trickier.
The simplest way would be to turn on bash's dotglob option, and then
turn it off again at the end:

duhs() {
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
shopt -u dotglob
}

But this assumes that the option was *not* already on when we entered
the function. If it was on, we've just turned it off. Another way to
do this, which doesn't permanently alter the option for the remainder
of the shell's lifetime, would be to wrap the function body in a subshell:

duhs() {
(
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
)
}

The formatting choices here are legion.

There are a few other options, but they become increasingly arcane from
here. The subshell is probably the most straightforward choice. The
function is already forking child processes, so from a performance point
of view, adding a subshell won't be much worse.

I'll also throw in one last piece of information because if I don't,
someone else is likely to do it, without a good explanation.
Syntactically, the body of a shell function doesn't have to be enclosed
in curly braces. The body can be any compound command, and a curly-brace
command group is just one example of a compound command. A subshell is
another example. So, it could also be written this way:

duhs() (
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
)

I'm not personally fond of this. It's extremely easy to overlook
the fact that the curly braces have been replaced with parentheses,
especially in certain fonts. Nevertheless, some people like this.
Greg Wooledge
2024-06-03 19:10:01 UTC
Permalink
Post by Greg Wooledge
duhs() (
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
)
I'm not personally fond of this. It's extremely easy to overlook
the fact that the curly braces have been replaced with parentheses,
I guess minimalists would make a one-liner out of that.
Myself, I prefer verbosity, and the ability to search and find
all my functions with /function.*{$ in less. For example,
function _Cat_ {
cat
}
_Cat_() { cat; }
... I feel like you've just exemplified what I was talking about, missing
the fact that the curly braces were replaced with parentheses around the
function body to force a subshell every time it's called.

It had nothing to do with how many lines of code were used. I could
have written them as

duhs() { (shopt -s dotglob; printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh); }

and

duhs() (shopt -s dotglob; printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh)

and the same point would have applied.
function _Cat_ {
Do note that the "function" keyword is a bash/ksh extension, and not
part of POSIX sh syntax. In bash, "x()" and "function x" are identical
in behavior. In ksh, they cause two different kinds of variable scoping
behavior. Bash also allows "function x()" which ksh does not.

Just for the record.
David Wright
2024-06-04 00:40:01 UTC
Permalink
Post by Greg Wooledge
Post by Greg Wooledge
duhs() (
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
)
I'm not personally fond of this. It's extremely easy to overlook
the fact that the curly braces have been replaced with parentheses,
I guess minimalists would make a one-liner out of that.
Myself, I prefer verbosity, and the ability to search and find
all my functions with /function.*{$ in less. For example,
function _Cat_ {
cat
}
_Cat_() { cat; }
... I feel like you've just exemplified what I was talking about, missing
the fact that the curly braces were replaced with parentheses around the
function body to force a subshell every time it's called.
_Puss_() ( cat )

if it makes you happy; but no, you made your point about parentheses
perfectly well. It's just that _Cat_ doesn't require a subshell for
/its/ purpose.
Post by Greg Wooledge
It had nothing to do with how many lines of code were used. I could
have written them as
duhs() { (shopt -s dotglob; printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh); }
and
duhs() (shopt -s dotglob; printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh)
and the same point would have applied.
function _Cat_ {
Do note that the "function" keyword is a bash/ksh extension, and not
part of POSIX sh syntax. In bash, "x()" and "function x" are identical
in behavior. In ksh, they cause two different kinds of variable scoping
behavior. Bash also allows "function x()" which ksh does not.
Just for the record.
Noted. (Franco did mention using bash, and I mentioned .bashrc.)
I'm a bit old for switching shells now!

Cheers,
David.
Franco Martelli
2024-06-03 20:50:01 UTC
Permalink
Post by Franco Martelli
duhs() {
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
shopt -u dotglob
}
But this assumes that the option was*not* already on when we entered
the function. If it was on, we've just turned it off. Another way to
do this, which doesn't permanently alter the option for the remainder
duhs() {
(
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
)
}
I've some issue with this function. It doesn't show the size of the
directory specified as argument, it follows the output of my original
function:

duhs1() { IFS=$'\n\r' ; for d in $(/usr/bin/find $1 -maxdepth 1 -type d
|/usr/bin/sort) ; do du -hs $d ; done }

~# duhs1 /usr/local/
88K /usr/local/ ←←←
4,0K /usr/local/bin
4,0K /usr/local/etc
...

According to the very kind Greg's explanation I tried to change the
function in this way:

duhs() {
(
shopt -s dotglob
du -sh "${1:-.}"
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
)
}

the specified directory is shown, but the output has became messed up:

~# duhs /usr/local/
88K /usr/local/
4,0K /usr/local//bin/
4,0K /usr/local//etc/
4,0K /usr/local//games/
4,0K /usr/local//include/
4,0K /usr/local//man/
4,0K /usr/local//sbin/
56K /usr/local//share/
4,0K /usr/local//src/

and worst the size of /usr/local/share isn't 56K:

~# du -hs /usr/local/share/
60K /usr/local/share/

as it's shown by my original function:

~# duhs1 /usr/local/
88K /usr/local/
4,0K /usr/local/bin
4,0K /usr/local/etc
4,0K /usr/local/games
4,0K /usr/local/include
4,0K /usr/local/sbin
60K /usr/local/share
4,0K /usr/local/src

the /usr/local/man/ isn't listed by the "find" command because it's a
symlink:

~# ls -l /usr/local/man
lrwxrwxrwx 1 root root 9 21 giu 2023 /usr/local/man -> share/man

Is there a way to have duhs() function that it doesn't print a double
"/" at the end of the specified directory?
The size mismatch of /usr/local/share could depend by /usr/local/man is
a symlink of 4K's size to a directory embedded in /usr/local/share, am I
right?

Cheers,
--
Franco Martelli
Greg Wooledge
2024-06-03 22:30:01 UTC
Permalink
Post by Franco Martelli
Post by Franco Martelli
duhs() {
(
shopt -s dotglob
printf '%s\0' "${1:-.}"/*/ | xargs -0 du -sh
)
}
I've some issue with this function. It doesn't show the size of the
directory specified as argument, it follows the output of my original
I started writing a response to this, because it seemed easy enough to
fix -- just add "${1:-.}" to the printf arguments, so it gets passed
along to du -sh, and included in the output.

But then I tested it.

hobbit:~$ du -sh /usr/local /usr/local/bin
684M /usr/local

What? Where's the second line?

hobbit:~$ du -sh /usr/local/bin /usr/local/etc
81M /usr/local/bin
4.0K /usr/local/etc

Multiple arguments are allowed....

hobbit:~$ du -sh /usr/local/bin /usr/local/etc /usr/local
81M /usr/local/bin
4.0K /usr/local/etc
603M /usr/local

You're even allowed to have a parent directory after its children...

hobbit:~$ du -sh /usr/local /usr/local/bin /usr/local/etc
684M /usr/local

... but if the parent is FIRST, the children get... swallowed up by
it? Huh? And if the parent is last, then the value shown for the
parent excludes the children that were already reported on?

Why the HELL is it like this?!

It doesn't even match its own documentation. Here's what the info
page says:

‘-s’
‘--summarize’
Display only a total for each argument.

There's supposed to be a total *FOR EACH ARGUMENT*. There isn't.

OK, I officially wash my hands of ANY solution based on du -s. If
you want to try to make it work sensibly, more power to you. As far
as I'm concerned, though, this is broken. Irreparably broken.

It looks like tree(1) is less broken, so it might be better to stick
with that, even if you have to pipe the output through grep to get what
you want.

*Sigh*.
Greg Wooledge
2024-06-04 01:50:01 UTC
Permalink
Post by Greg Wooledge
‘-s’
‘--summarize’
Display only a total for each argument.
There's supposed to be a total *FOR EACH ARGUMENT*. There isn't.
Try adding -l. The idea is that du is trying to avoid double-counting,
so when the subdirectories come first, those ones are "used up" by
the time it reaches the parent. When the parent is first, it's (all)
the subdirectories instead that have been "used up".
‘-l’
‘--count-links’
Count the size of all files, even if they have appeared already (as
a hard link).

This documentation leaves a lot to be desired.

Anyway, you are correct about the result, even if the documentation gives
no realistic way to conclude such a result.

hobbit:~$ printf '%s\0' "${1:-.}" "${1:-.}"/*/ | xargs -0 du -shl
684M /usr/local
81M /usr/local/bin/
4.0K /usr/local/etc/
4.0K /usr/local/games/
136K /usr/local/include/
45M /usr/local/lib/
29M /usr/local/man/
8.0K /usr/local/sbin/
31M /usr/local/share/
484M /usr/local/src/
44M /usr/local/tcl8.6/
Greg Wooledge
2024-06-04 14:50:01 UTC
Permalink
duhs() { ( shopt -s dotglob; printf '%s\0' "${1:-.}" "${1:-.}"*/ | xargs -0
du -shl ) } ↑
Please note that I dropped a "/" in the "${1:-.}"/*/ block because the
That's because you're passing directories with a trailing / from your
shell. Probably because you're using tab completions.

If you pass "/usr/local" as the argument instead of
~# duhs /usr/local/
88K /usr/local/
4,0K /usr/local//bin/
4,0K /usr/local//etc/
4,0K /usr/local//games/
4,0K /usr/local//include/
4,0K /usr/local//man/
4,0K /usr/local//sbin/
60K /usr/local//share/
4,0K /usr/local//src/
you will see that you've broken it now.

If you really care about removing the double slashes from the case
where you pass an extra trailing slash in your input, you should use
parameter expansions or some other filtering to remove one of the
slashes. That would be better than breaking your function.

duhs() {
(
shopt -s dotglob
s=${1:-.}
s=${s%/}
printf '%s\0' "${s:-.}" "${s:-.}"/*/ | xargs -0 du -shl
)
}
Franco Martelli
2024-06-04 14:50:02 UTC
Permalink
Post by Greg Wooledge
Post by Greg Wooledge
‘-s’
‘--summarize’
Display only a total for each argument.
There's supposed to be a total *FOR EACH ARGUMENT*. There isn't.
Try adding -l. The idea is that du is trying to avoid double-counting,
so when the subdirectories come first, those ones are "used up" by
the time it reaches the parent. When the parent is first, it's (all)
the subdirectories instead that have been "used up".
‘-l’
‘--count-links’
Count the size of all files, even if they have appeared already (as
a hard link).
This documentation leaves a lot to be desired.
Anyway, you are correct about the result, even if the documentation gives
no realistic way to conclude such a result.
hobbit:~$ printf '%s\0' "${1:-.}" "${1:-.}"/*/ | xargs -0 du -shl
684M /usr/local
81M /usr/local/bin/
4.0K /usr/local/etc/
4.0K /usr/local/games/
136K /usr/local/include/
45M /usr/local/lib/
29M /usr/local/man/
8.0K /usr/local/sbin/
31M /usr/local/share/
484M /usr/local/src/
44M /usr/local/tcl8.6/
FYI the revision of duhs() I adopt is:

duhs() { ( shopt -s dotglob; printf '%s\0' "${1:-.}" "${1:-.}"*/ | xargs
-0 du -shl ) } ↑

Please note that I dropped a "/" in the "${1:-.}"/*/ block because the
directories listed had a double "//" in their names:

~# duhs /usr/local/
88K /usr/local/
4,0K /usr/local//bin/
4,0K /usr/local//etc/
4,0K /usr/local//games/
4,0K /usr/local//include/
4,0K /usr/local//man/
4,0K /usr/local//sbin/
60K /usr/local//share/
4,0K /usr/local//src/

Thank again,
Cheers,
--
Franco Martelli
t***@tuxteam.de
2024-06-03 14:40:01 UTC
Permalink
Post by Franco Martelli
Post by Greg Wooledge
Post by Franco Martelli
~$ tree --du -Fah /tmp/x | grep --color /$
You're only coloring the trailing / characters. If you want everything
tree --du -Fh /usr/local | grep --color '[^[:space:]]*/$'
I realized why "tree" command doesn't colorized the directories: "tree"
detects that its std output goes through a pipe and therefore it disables
the escaped code to colorize (like also "dmesg" does).
That's what the "-C" option to tree is for.

Cheers
--
"if everything else fails, read the man page"
tomas
Andy Smith
2024-06-03 14:40:01 UTC
Permalink
Hi,
"tree" detects that its std output goes through a pipe and
therefore it disables the escaped code to colorize (like also
"dmesg" does). To avoid this behavior you must use the "unbuffer"
unbuffer tree --du -Fah /usr/local | grep /$
If that's the only thing you're using unbuffer for, why not just use
the -C option of tree? It's a bit like the "--color=always" of ls.
~# unbuffer dmesg | grep snd
Similarly dmesg has "--color=always".

I guess one advantage of unbuffer for this purpose is that you don't
need for an option to exist or to know what it is if it does.

Thanks,
Andy
--
https://bitfolk.com/ -- No-nonsense VPS hosting
t***@tuxteam.de
2024-06-03 17:10:01 UTC
Permalink
On Mon, Jun 03, 2024 at 02:36:43PM +0000, Andy Smith wrote:

[...]
Post by Andy Smith
If that's the only thing you're using unbuffer for, why not just use
the -C option of tree? It's a bit like the "--color=always" of ls.
Oh, and the complementary option for `less', while we're at it, would
be -R:

tree -C | less -R

(I tend to add -SX to less: people might like those or not).

Cheers
--
t
Franco Martelli
2024-06-03 21:00:01 UTC
Permalink
Post by Andy Smith
Post by Franco Martelli
unbuffer tree --du -Fah /usr/local | grep /$
If that's the only thing you're using unbuffer for, why not just use
the -C option of tree? It's a bit like the "--color=always" of ls.
Yeah, RTFMB4 I've all the "aliases" with the "unbuffer" command…
Cheers,
--
Franco Martelli
Sridhar M A
2024-05-31 00:50:01 UTC
Permalink
Post by Northwind
is there a command that shows dir/subdir structure like `tree`, but for
each dir has the size in results as well?
You can try the program broot (https://dystroy.org/broot/) which does
things you have mentioned and much more.

Regards,
--
Sridhar M. A.
Felix Miata
2024-05-31 01:30:01 UTC
Permalink
Post by Northwind
is there a command that shows dir/subdir structure like `tree`, but for
each dir has the size in results as well?
Is ncdu any use to your need?
--
Evolution as taught in public schools is, like religion,
based on faith, not based on science.

Team OS/2 ** Reg. Linux User #211409 ** a11y rocks!

Felix Miata
Loading...