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

前端 未结 6 611
Happy的楠姐
Happy的楠姐 2020-12-29 09:22

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

相关标签:
6条回答
  • 2020-12-29 09:24

    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.

    0 讨论(0)
  • 2020-12-29 09:32

    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
    
    0 讨论(0)
  • 2020-12-29 09:33

    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.

    0 讨论(0)
  • 2020-12-29 09:34

    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.

    0 讨论(0)
  • 2020-12-29 09:37

    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
    
    0 讨论(0)
  • 2020-12-29 09:50

    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

    0 讨论(0)
提交回复
热议问题