58

I want to write a shell script to automate a series of commands. The problem is some commands MUST be run as superuser and some commands MUST NOT be run as superuser. What I have done so far is something like this:

#!/bin/bash

command1
sudo command2
command3
sudo command4

The problem is, this means somebody has to wait until command1 finishes before they are prompted for a password, then, if command3 takes long enough, they will then have to wait for command3 to finish. It would be nice if the person could get up and walk away, then come back an hour later and be done. For example, the following script has this problem:

#!/bin/bash

sleep 310
sudo echo "Hi, I'm root"
sleep 310
sudo echo "I'm still root?"

How can I make it so that the user can just enter their password once, at the very start, and then walk away?

Update:

Thanks for the responses. I'm running on Mac OS X Lion and ran Stephen P's script and got different results: (I also added $HOME)

pair@abbey scratch$ ./test2.sh
uid is 501
user is pair
username is 
home directory is /Users/pair
pair@abbey scratch$ sudo ./test2.sh 
Password:
uid is 0
user is root
username is root
home directory is /Users/pair
speedarius
  • 1,006
  • 1
  • 8
  • 15

4 Answers4

52

File sutest

#!/bin/bash
echo "uid is ${UID}"
echo "user is ${USER}"
echo "username is ${USERNAME}"

run it: `./sutest' gives me

uid is 500
user is stephenp
username is stephenp

but using sudo: sudo ./sutest gives

uid is 0
user is root
username is stephenp

So you retain the original user name in $USERNAME when running as sudo. This leads to a solution similar to what others posted:

#!/bin/bash
sudo -u ${USERNAME} normal_command_1
root_command_1
root_command_2
sudo -u ${USERNAME} normal_command_2
# etc.

Just sudo to invoke your script in the first place, it will prompt for the password once.


I originally wrote this answer on Linux, which does have some differences with OS X

OS X (I'm testing this on Mountain Lion 10.8.3) has an environment variable SUDO_USER when you're running sudo, which can be used in place of USERNAME above, or to be more cross-platform the script could check to see if SUDO_USER is set and use it if so, or use USERNAME if that's set.

Changing the original script for OS X, it becomes...

#!/bin/bash
sudo -u ${SUDO_USER} normal_command_1
root_command_1
root_command_2
sudo -u ${SUDO_USER} normal_command_2
# etc.

A first stab at making it cross-platform could be...

#!/bin/bash
#
# set "THE_USER" to SUDO_USER if that's set,
#  else set it to USERNAME if THAT is set,
#   else set it to the string "unknown"
# should probably then test to see if it's "unknown"
#
THE_USER=${SUDO_USER:-${USERNAME:-unknown}}

sudo -u ${THE_USER} normal_command_1
root_command_1
root_command_2
sudo -u ${THE_USER} normal_command_2
# etc.
Stephen P
  • 14,422
  • 2
  • 43
  • 67
  • You can also make a shell script that is owned by root and has the `suid` bit set, but that can be dangerous if you don't do it right. Any time you're running shell scripts as root you may want to consider using `trap` to handle signals. http://www.shelldorado.com/goodcoding/tempfiles.html has some examples. – Stephen P Apr 19 '12 at 01:01
  • Strange, I actually get different results. This is on Mac OS X Lion: `pair@abbey scratch$ ./test2.sh`
    `uid is 501`
    `user is pair`
    `username is `
    `home directory is /Users/pair`
    `pair@abbey scratch$ sudo ./test2.sh `
    `Password:`
    `uid is 0`
    `user is root`
    `username is root`
    `home directory is /Users/pair`
    `pair@abbey scratch$ sudo -u pair ./test2.sh `
    `uid is 501`
    `user is pair`
    `username is pair`
    `home directory is /Users/pair`
    – speedarius Apr 19 '12 at 18:40
  • Sorry for the formatting of the above comment; I'm updating the original post now.. – speedarius Apr 19 '12 at 18:49
  • On Ubuntu 12.04 or newer you also want to use `${SUDO_USER}`. – kynan Oct 23 '13 at 23:21
  • I'm on Xubuntu 14.04 and the USERNAME variable does not exist when I am not root. Would it make sense to define this in my .bashrc file? I don't understand why it only shows up when I am root and not otherwise. I typed `env | grep USERNAME` and nothing showed up but when I typed `sudo env | grep USERNAME` it showed up as `USERNAME=root` – Jesse Apr 25 '14 at 11:39
  • I actually found that the environment variable that reflects my username and that does not change when sudo is run is `SUDO_USER`. I ran `sudo env | grep jesse` to see what variables were still my username – Jesse Apr 25 '14 at 11:46
10

You should run your entire script as superuser. If you want to run some command as non-superuser, use "-u" option of sudo:

#!/bin/bash

sudo -u username command1
command2
sudo -u username command3
command4

When running as root, sudo doesn't ask for a password.

Pavel Strakhov
  • 39,123
  • 5
  • 88
  • 127
3

If you use this, check man sudo too:

#!/bin/bash

sudo echo "Hi, I'm root"

sudo -u nobody echo "I'm nobody"

sudo -u 1000 touch /test_user
3

Well, you have some options.

You could configure sudo to not prompt for a password. This is not recommended, due to the security risks.

You could write an expect script to read the password and supply it to sudo when required, but that's clunky and fragile.

I would recommend designing the script to run as root and drop its privileges whenever they're not needed. Simply have it sudo -u someotheruser command for the commands that don't require root.

(If they have to run specifically as the user invoking the script, then you could have the script save the uid and invoke a second script via sudo with the id as an argument, so it knows who to su to..)

Ben Aubin
  • 5,542
  • 2
  • 34
  • 54
Mark Reed
  • 91,912
  • 16
  • 138
  • 175