Why can't I use 'sudo su' within a shell script? How to make a shell script run with sudo automatically

允我心安 提交于 2019-12-18 12:24:14

问题


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. Thank you!

#!/bin/bash    

sudo su;
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

回答1:


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




回答2:


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



回答3:


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.




回答4:


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



回答5:


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.




回答6:


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.



来源:https://stackoverflow.com/questions/24640340/why-cant-i-use-sudo-su-within-a-shell-script-how-to-make-a-shell-script-run

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!