Why doesn't "sudo su" in a shell script run the rest of the script as root?

The commands in a script execute one by one, independently. The Script itself as the parent of all commands in the script, is another independent process and the su command does not and can not change it to root: the su command creates a new process with root privileges.

After that su command completes, the parent process, still running as the same user, will execute the rest of the script.

What you want to do is write a wrapper script. The privileged commands goes into the main script, for example ~/main.sh

#!/bin/sh
ls /root

The wrapper script calls the main script with root permissions, like this

#!/bin/sh
su -c ~/main.sh root

To launch this process you run the wrapper, which in turn launches the main script after switching user to the root user.

This wrapper technique can be used to turn the script into a wrapper around itself. Basically check to see if it is running as root, if not, use "su" to re-launch itself.

$0 is a handy way of making a script refer to itself, and the whoami command can tell us who we are (are we root?)

So the main script with built-in wrapper becomes

#!/bin/sh
[ `whoami` = root ] || exec su -c $0 root
ls /root

Note the use of exec. It means "replace this program by", which effectively ends its execution and starts the new program, launched by su, with root, to run from the top. The replacement instance is "root" so it doesn't execute the right side of the ||


Use the following in script.

sudo su <<HERE
ls /root
HERE

The code between the HERE block will be run as root.


Without further arguments su will run the login shell for root. That's what the first line of your script actually does. When you exit, the login shell closes, su returns and your script continues execution, that is with the second line: ls /root. I think you can simply sudo ls /root to do what you want.