6

When I type sudo du -sh ~student1 I get the output I expect (see screenshot). However when I try to use the script below:

#!/bin/bash

for user in "$@"; do
    sudo du -sh ~$user
done

to do the same thing, I get an error that the directory doesn't exist. Here is a screenshot.

(I know I can do sudo du -sh student1 student2..., and that the bash script is useless but I still don't understand why it doesn't work.)

I had a look at Total disk usage for a particular user but it wasn't quite what I was looking for.

Sathyajith Bhat
  • 61,504
  • 38
  • 179
  • 264
kg98
  • 63
  • 1
  • 4

3 Answers3

4

The shell does a single pass on expanding parameters, so $user is expanded, but the preceding ~ isn't then expanded. In order to reparse the input line, use eval:

...
eval sudo du -sh ~$user
...
AFH
  • 17,300
  • 3
  • 32
  • 48
  • 1
    True. It's because tilde expansion is done before variable expansion in `bash`, then neither `sudo` nor `du` expands tilde. – Kamil Maciorowski Oct 26 '17 at 11:40
  • Note that `~` is expanded in the user shell, so if permissions prevent the expansion of a particular user here, then the use of `sudo` won't override this. – AFH Oct 26 '17 at 12:07
  • Why use `eval` when you could use `/home/$user` directly? Another way is to not use sudo inside the script, but call the script using sudo, which is IMHO a lot cleaner... – xenoid Oct 26 '17 at 12:48
  • 1
    @xenoid - The default option for a user's home directory is indeed `/home/$user`, but this can be overridden at account creation time, or modified later. It is also not the default for `root`, at least not on Ubuntu and (presumably) other Debian-derived distributions. If you use `sudo` outside the script, you will be running in a different environment from the calling shell, which has other implications: for instance, the script may not be in the `$PATH` which `sudo` uses. – AFH Oct 26 '17 at 13:29
  • Anything is better than using `eval`. It's a script, path problems can be managed. And if you want to picky there are plenty of user ids on Ubuntu without a true /home (none or strange ones such as `/usr/sbin`. But since the script is meant to check regular users anyway... – xenoid Oct 26 '17 at 14:31
  • I made no assumptions about the user definitions and neither should you. So-called "regular" users still need not use the default `adduser` directory: there are all sorts of reasons why a system administrator might not want to use this, and the questioner is absolutely correct to use `~` expansion. Anyway, how do you know the questioner does not want to include `root` in his report? What's wrong with `eval`? Why is anything better than it? It was intended for exactly this sort of use. – AFH Oct 26 '17 at 15:01
  • `thescript "innocuous_user ; echo eval is dangerous"` (I'll lleave it to your imagination to replace the `echo` command by anything that can be really nasty if executed with root privileges). – xenoid Oct 26 '17 at 15:32
  • 1
    @xenoid - Not true: the semicolon in the parameter will delimit the `sudo` command, and the rest of the command will run with user privileges. I am willing to accept that there may be places where `eval` could have unintended consequences, but this is not one of them. In any case, a user who has `sudo` privileges doesn't need to use something as obscure as this to do dangerous things, and users with `sudo` denied won't be able to run the script anyway. – AFH Oct 26 '17 at 16:21
  • Because you didn't try `thescript "innocuous_user ; sudo id"`... – xenoid Oct 26 '17 at 19:59
  • I don't understand your comment (`sudo id`?). What I tried was `thescript "innocuous_user ; whoami"` – AFH Oct 26 '17 at 20:34
  • If the "extra" command is `sudo something` you get the root privs again to run `something` (and no password is asked since you are still in the grace period for password-less sudo). – xenoid Oct 26 '17 at 21:08
  • 1
    I don't see the problem. If `sudo` is permitted, it doesn't matter if you run it on the same line in a script or as a separate command afterwards. I have answered your every comment and you have not proposed a workable alternative. This conversation is getting nowhere, so I am not going to continue it. Thank you for your comments, but we'll have to agree to differ. – AFH Oct 26 '17 at 22:47
0
find . -printf "%u  %s\n" | awk '{user[$1]+=$2}; END{ for( i in user) print i " " user[i]}'

this will print

jsmith 928057466

the same function

find . -user jsmith -type f -printf "%s\n" | awk '{t+=$1}END{print t}'

this will print just

928057466

Joniale
  • 101
  • 2
  • Welcome on Superuser. We are always happy to see some explanation of an answer. Maybe you could explain what happens. – davidbaumann Dec 04 '18 at 11:43
0

As mentioned by @AFH, the tilde (~) isn't expanded, and you could use eval to handle this...

Or you could use getent to get a user's home directory:

$ getent passwd attie | cut -d: -f6
/home/attie

This way it would be easier to detect an error (rather than a mysterious variable expansion issue).

You could even use this to enumerate user directories:

$ getent passwd | cut -d: -f6 | grep -E '^/home/'
/home/attie
/home/bill
Attie
  • 19,231
  • 5
  • 58
  • 73