2

So I got this script from this guide: www.tldp.org/LDP/abs/abs-guide.pdf

LOG_DIR=/var/log
ROOT_UID=0
LINES=50
E_XCD=86
E_NOTROOT=87

if [ "$UID" != "$ROOT_UID" ]
then
echo "Must be root to run this script."
exit $E_NOTROOT
fi

if [ -n "$1" ]
then
lines=$1
else
lines=$LINES
fi

cd $LOG_DIR

if [ 'pwd' != "$LOG_DIR" ]
then
echo "Can't change to $LOG_DIR."
exit $E_XCD
fi

Now I did sudo su on terminal and echoed $UID which gives me 0. That means my if condition turns out to be false but still I am getting "Must be root to run this script." while running this script.

Also in original script this if condition was given as

if [ "$UID" -ne "$ROOT_UID" ]

but I was getting Illegal number error because apparently -ne is only used for string so I changed it to !=.

  • What is the question? – Dan Aug 22 '14 at 18:18
  • @dan08 $UID is equal to $ROOT_UID so why it is going inside if statement i.e. printing "Must be root to run this script"? – rishiag Aug 22 '14 at 18:27
  • 1
    For shell, within [...], the != operator does string comparison and -ne does numeric comparison. – glenn jackman Aug 22 '14 at 19:07
  • Note that this [ 'pwd' != should be this [ `pwd` != or preferably this [ $(pwd) !=, or in bash, this [ $PWD != – glenn jackman Aug 22 '14 at 19:08
  • 1
    @glennjackman Unlike $UID, $PWD is not bash-specific. It's standard for Bourne-style shells. For example, see man dash. – Eliah Kagan Aug 23 '14 at 3:25
3

Instead of executing it with

sh script.sh

execute it with

bash script.sh

(or add #!/bin/bash as the first line to set the interpreter).

The sh shell in Ubuntu is not bash, but a separate shell called dash. dash does not have as many features as bash, which makes it more efficient, but these missing features sometimes break scripts intended for bash. One such feature is the $UID variable, which is not defined in dash.

This means that when the script is executed in dash, the comparison will become ["" != "0"], which evaluates to true no matter what user is running it. However, if it is run in bash with root permissions, the strings will be equal, and the script will work.

  • 3
    Or alternately, declare the interpreter to use by adding #!/bin/bash to the top of the script. – Dan Aug 22 '14 at 18:39
  • Thanks, I was editing my post to add that when I saw your comment. – McLovin Aug 22 '14 at 18:42
  • 2
    Note that a hashbang line at the top of script.sh doesn't affect the behavior of sh script.sh (nor of bash script.sh). sh ... always uses sh as the interpreter. A hashbang line determines the interpreter of an executable script that is itself run as a program. To cause #!... to be read and used, run script.sh with the command ./script.sh (if it's in the current directory) or just as /path/to/script.sh. For this to work, script.sh must be marked executable (chmod +x script.sh does this). – Eliah Kagan Aug 22 '14 at 18:48
1

As McLovin says, the automatically set, readonly UID (and EUID) variable is a special feature of bash (and some other shells). It's not standard for Bourne-style shells and sh cannot be assumed to set these variables. In particular, sh in Ubuntu is (currently, by default) dash, which does not set them.

You have two options:

  1. Cause your script to be run by bash instead of sh.
  2. Change your script so it's more portable and doesn't require bash.

Running Your Script With bash

You haven't told us the name of your script (which is okay). I'm going to call it cleanup, as that's the name of the similar script in the guide you're working from.

Note in particular that I am not calling it something like cleanup.sh. This is because I don't recommend you name it with a .sh suffix:

  • A .sh suffix suggests it is compatible with sh!
  • Most scripts aren't given a suffix at all, and having a .sh or .bash suffix may even be interpreted as suggesting your file is a "library" rather than a "top level" script. That is, that it provides code to be sourced by other scripts (for example, with the . builtin) rather than actually run directly.

However, including a .sh suffix will not affect the technical behavior of your script in any way. There is no technical advantage or disadvantage associated with doing so.

Add a hashbang line as the very first line of your script: #!/bin/bash

Make your script executable with chmod +x cleanup.

Now you can run your script by running the command:

  • ./cleanup, when it's in the current directory. (Especially when scripting for education, exploration, testing, fun, or other limited or one-time use, this is perhaps the most common way.)

  • /path/to/cleanup, in general. If the first word of a command contains a / it is interpreted as path to an executable file. (That's the reason for the above ./ syntax, as . means the current directory.)

    By first word I mean the command up to but not including its first unescaped space or tab:
    foo-bar/baz is the first word of foo-bar/baz qux, while spam\ ham is the first word of spam\ ham eggs because the space before ham is escaped with a backslash.

  • cleanup, if it's in a directory in your PATH (and no directory listed first has an executable of the same name, and it is not superseded by a shell builtin, function, or alias).

Regardless of whether or not a script cleanup has a #!... hashbang line or what that line says:

  • sh cleanup will still (attempt to) run it with sh as the interpreter.
  • bash cleanup will still (attempt to) run it with bash as the interpreter.

This is because, in those cases, you are actually running the interpreter (sh or bash) and passing the name of the script to the interpreter, to be run. Many programs accept names of files to open on the command line, and shells like sh and bash follow this convention.

Making Your Script Portable

If you want to make your script portable, the simplest way is to not use $UID at all, and instead use some facility available to all Bourne-style shells.

In general there's no built-in shell facility to determine user ID. But you can use id. This is an external program rather than a shell builtin, but like cat and ls, it's reasonable to expect it will be present.

id by itself outputs a bunch of info, but id -u gives just the effective user ID.

So instead of if [ "$UID" != "$ROOT_UID" ], you can use:

if [ "$(id -u)" != "$ROOT_UID" ]

Technically, this is not equivalent. $UID in bash gives the real user ID, and id -u really corresponds to bash's $EUID. If you really want the UID and not the EUID, use id -ru.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.