I cannot figure out what\'s wrong with this. When I run it in terminal and enter password, nothing happens, but if I run every command separately in terminal, it works. Than
sudo su
is not a command run within a shell -- it starts a new shell.
That new shell is no longer running your script, and the old shell that is running the script waits for the new one to exit before it continues.
You can use Here Documents to redirect input into an interactive shell script. The operator <<
is an instruction to read input until it finds a line containing the specified delimiter, as EOF
(end of file).
sudo su <<EOF
echo "code"
EOF
e.g.
#!/bin/bash
sudo su <<EOF
mkdir /opt/D3GO/
cp `pwd`/D3GO /opt/D3GO/
cp `pwd`/D3GO.png /opt/D3GO/
cp `pwd`/D3GO.desktop /usr/share/applications/
chmod +x /opt/D3GO/D3GO
EOF
Because running "sudo su" opens a new shell and the command does not return until you exit from that shell. Perhaps split the script into 2 files: first one runs sudo and executes that 2nd script under sudo.
sudo su will attempt to start a new shell as root. Once that new shell is opened, the original script will not continue until the new shell is closed.
For a fix try:
In the shell script try:
su <username> -c "my command"
So if the user was "userA":
su userA -c "mkdir /opt/D3GO/"
However, if you are userA for example and you want to run the part of script as root, you will be prompted for a pass.
su root -c "mkdir /opt/D3GO/"
You can also get around that by just running the script with sudo in the first place
sudo ./myScript.sh
That way the script retains the original user inside the script which you can access using the standard variables like ${USERNAME}, ${UID} etc
Depends on what works better for you.
The accepted answer works well, but the idiom for a script re-invoking itself with sudo
on demand can be simplified and made more portable:
[[ $(id -u) -eq 0 ]] || exec sudo /bin/bash -c "$(printf '%q ' "$BASH_SOURCE" "$@")"
Using [[ ... ]]
instead of [ ... ]
makes prepending x
to the operands (or double-quoting the LHS) unnecessary.
Using bash -c
instead of su -c
to interpret the reconstructed command line makes the command more portable, because not all platforms support su -c
(e.g., macOS doesn't).
In bash
, $BASH_SOURCE
is generally the more reliable way to refer to the running script.
With the above approach, any variable references or command / arithmetic substitutions in the arguments are invariably expanded by the calling shell.
If you instead you wanted delayed expansion - so that variable references aren't expanded until the sudo
shell runs, in the context of the root user - use this:
(( __reinvoked )) || exec sudo -s __reinvoked=1 "$BASH_SOURCE" "$@"
Note that you'd then have to single-quote any arguments containing variable references or command substitutions for them to be delayed-expanded; e.g., '$USER'
.
Note the use of ad-hoc environment variable __reinvoked
to ensure re-invocation exactly once (even when initially already invoked as the root user).
Here's a sample script that demonstrates the first technique:
If not invoked as root, the script reinvokes itself with sudo -s
, passing all arguments through as-is.
Unless previously authenticated and still within the timeout period, sudo
will prompt for an administrator password.
#!/bin/bash
[[ $(id -u) -eq 0 ]] || exec sudo /bin/bash -c "$(printf '%q ' "$BASH_SOURCE" "$@")"
# Print the username and all arguments.
echo "Running as: $(id -un)"
echo "Arguments:"
for arg; do echo " $((++i)): [$arg]"; done
acfreitas's helpful answer demonstrates a "script-inside-a-script" technique where a here-document is used to provide shell code via stdin to sudo su
.
Again, sudo -s
is sufficient and quoting is important:
sudo -s -H <<'EOF'
echo "$HOME"
EOF
Note how the opening here-document delimiter, EOF
in this case, is quoted in order to prevent the contents of the document from up-front interpretation by the current shell.
If you didn't quote (any part of) EOF
, $HOME
would be expand to the current user's home directory.
If you want to mix up-front and delayed expansion, leave the opening here-document delimiter unquoted and selectively \
-quote $
instances:
sudo -s -H <<EOF
echo "Called by: $USER; root's home dir: \$HOME"
EOF
Command sudo su
starts an interactive root shell, but it will not convert the current shell into a root one.
The idiom to do what you want is something along the lines of this (thanks to @CharlesDuffy for the extra carefulness):
#check for root
UID=$(id -u)
if [ x$UID != x0 ]
then
#Beware of how you compose the command
printf -v cmd_str '%q ' "$0" "$@"
exec sudo su -c "$cmd_str"
fi
#I am root
mkdir /opt/D3GO/
#and the rest of your commands
The idea is to check whether the current user is root, and if not, re-run the same command with su