Question

So I think I have a little user right issue. Have been scripting a Webserver Control panel.

At a point in this script I'm listing all folders for a user into an array so I can compare it to the user input. (Each user is a new customer, and each customer can have multiple sites). Up until Friday last week when I ran this command:

[rjo@WEB ~]$ sudo ls -d /home/hejhej/*/ 2>/dev/null

Everything was working fine running the command from my own user (rjo). But now when I run the command nothing happens, so I removed "2>/dev/null" and tried again and now I'm getting this:

[rjo@WEB ~]$ sudo ls -d /home/hejhej/*/
ls: cannot access /home/hejhej/*/: No such file or directory

But when running it from root everything works fine:

[root@WEB ~]# ls -d /home/hejhej/*/
/home/hejhej/bumbum.dk/

Here's the line regarding my user from visudo file.

rjo     ALL=(ALL)       NOPASSWD: ALL

If you need any other info to help pleas tell me and I will post.

Was it helpful?

Solution

This will happen if your user doesn't have enough permissions on /home/hejhej to list its contents.

When you run:

sudo ls -d /home/hejhej/*/

It is your shell that tried to do filename globbing. If it can't read that directory, it can't expand anything. So the literal string /home/hejhej/*/ is left as is, and passed as an argument to the ls run by root via sudo. It appears you don't have a directory called * in that directory, so ls fails.

You need the shell started by sudo to do the globbing here. One option might be:

sudo sh -c 'ls -d /home/hejhej/*/'

If you want the folder to search to contain a shell variable, then the ' hard quotes won't do - this prevents your shell from globbing (good) but also prevents variable expansion by your shell (bad here). You should use double-quotes (") instead so that variable expansion is done by your shell:

sudo sh -c "ls -d /home/$username/*/"

If you want to capture the ouput of that command into a variable, prefer the $() form of command substitution rather than backticks (`) - easier for quoting:

result=$(sudo sh -c "ls -d /home/$username/*/")

Now if $username can contain spaces (say username="hello world"), what is above won't work - the root shell will see two "tokens" (/home/hello and world/*/). You can fix that with another layer of quotes:

result=$(sudo sh -c "ls -d /home/'$username'/*/")

Given how the quotes are nested, this will not prevent your shell from expanding $username, but will enable the root shell to glob correctly on the single pattern /home/hello world/*/.

Now, this is still not perfect. You'll have a hard time dealing with issues if that glob matches more than one directory, or if the directory matched contains spaces. But at this point I'd advise you to:

  • Read this advice: Why you shouldn't parse the output of ls?
  • Make a proper script run by root to handle finding the appropriate directory - one-liners are fine if they're short and easy to read. Once things get too complex, writing a proper script is a win in the long run.

That being said, if you want a list of directories from another user's home into an array, the following (bash specific I think) should do it, without the problems of parsing ls, and should handle spaces in files and directories:

while read -r -d $'\0' dir ; do
  array+=("$dir")
done < <(sudo find "/home/$username" -maxdepth 1 -mindepth 1 -print0)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top