Source of problem
The reason your commands do not work is because they only look for executables in the $PATH variable. First, let's test our hypothesis.
dummy:~$ mkdir test dummy:~$ cd test dummy:~/test$ echo '#!/bin/sh' >test.sh dummy:~/test$ chmod +x test.sh dummy:~/test$ cd dummy:~$ command -v test.sh dummy:~$ PATH+=:/home/dummy/test/ dummy:~$ command -v test.sh /home/dummy/test/test.sh
This confirms my presentation above.
Now let's see what $PATH looks like for different users:
dummy:~$ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games dummy:~$ su root:~# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
So, to check if this command is available to this user (in your question, namely: root), you need to know its $PATH environment variable.
Decision
The values of such environment variables on Debian can usually be found in the /etc/profile and /etc/environment/ files. There is no easy way to get these values by catching them from files.
The most basic solution is to temporarily add known directories to your $PATH variable, and then use command -v :
dummy~$ OLDPATH=$PATH dummy~$ PATH=$OLDPATH:/sbin:/usr/sbin/:/usr/local/sbin/ dummy~$ command -v poweroff /sbin/poweroff dummy~$ PATH=$OLDPATH
There is one problem with this solution: if you want to be portable, you really don't know which folders you should concatenate. In most cases, this approach should be sufficient.
Alternative solution
Instead, you can write a script program that uses setuid bit . The Setuid bit is a somewhat hidden feature of Linux operating systems that allows you to run programs with the rights of their owners. Thus, you write a program that executes certain commands, such as the superuser, except that ordinary users can run it. So you can see the output of command -v poweroff , as the root would do.
Unfortunately, the material that shebang uses cannot have the setuid bit , so you cannot create a shell script for this, and you need a program in C. Here is an example of a program that will do the job:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char** argv) { if (argc <= 1) { fprintf(stderr, "No arguments.\n"); return 1; } //validate the argv char* prog = argv[1]; int i; for (i = 0; i < strlen(prog); i ++) { if (prog[i] < 'a' || prog[i] > 'z') { fprintf(stderr, "%s contains invalid characters (%c), exiting.", prog, prog[i]); return 1; } } //here the `which` command. We start it in new interactive shell, //since this program inherits environment variables from its //parent shell. We need to start *new* shell that will initialize //and overwrite existing PATH environment variable. char* command = (char*) malloc(strlen(prog) + 30); if (!command) { fprintf(stderr, "No memory!\n"); return 1; } sprintf(command, "bash -cli 'command -v %s'", prog); int exists = 0; //first we try to execute the command as a dummy user. exists |= system(command) == 0; if (!exists) { //then we try to execute the command as a root user. setuid(0); exists |= system(command) == 0; } return exists ? 0 : 1; }
Safety Notice . The above version has a very simple argument check (it only skips lines matching ^[az]*$ ). The real program should probably include a better check.
Testing
Suppose we saved a file in test.c We compile it and add the setuid bit:
root:~# gcc ./test.c -o ./test root:~# chown root:root ./test root:~# chmod 4755 ./test
Note that chown comes before chmod . 4 before the regular pattern 755 is the setuid bit.
Now we can test the program as a regular user.
dummy:~$ ./test ls; echo $? alias ls='ls -vhF1 --color=auto --group-directories-first' 0 dummy:~$ ./test blah; echo $? 1 dummy:~$ ./test poweroff; echo $? /sbin/poweroff 0
And best of all - it is portable enough to run on cygwin without any problems. :)