Search code examples
bashshellsudoio-redirection

How to use -S option with sudo tee in bash script (use inline password with sudo tee)


I am trying to create a file using bash script. Here is the function that I am using.

function create_service_file() {

    # create service file
    echo "Creating service file /etc/systemd/system/${SERVICE_NAME}"
    cat <<- EOF | sudo tee /etc/systemd/system/${SERVICE_NAME}
        [Unit]
        Description=$DESCRIPTION
        After=syslog.target network.target

        [Service]
        User=$USER

        WorkingDirectory=${WORKING_DIRECTORY}

        ExecStart=${JAVA_HOME}/bin/java -jar ${JAR_FILE}
        ExecStop=/bin/kill -15 $MAINPID
        SuccessExitStatus=143

        Restart=always
        RestartSec=30s

        [Install]
        WantedBy=multi-user.target

        EOF
    echo "/etc/systemd/system/${SERVICE_NAME} file has been written successfully"
}

When I run the script. It asks me for password at line cat <<- EOF | sudo tee /etc/systemd/system/${SERVICE_NAME}

enter image description here

After entering the password, it writes the file.

enter image description here

If I check in the directory there is a file

enter image description here

Now instead of giving password from the command I want to run this command directly. In other parts of the script. I am using the following syntax to achieve this thing

local is_service_failed=$(sudo -S <<< "${PASSWORD}" systemctl is-active $service)

or

sudo -S <<< "${PASSWORD}" systemctl start $service

These commands works fine. But now when I am trying to use the same thing it is not working. cat <<- EOF | sudo -S <<< ${PASSWORD} tee /etc/systemd/system/${SERVICE_NAME}

enter image description here

File is creating but nothing is written inside the file.

enter image description here

What am I doing wrong? How can I make it work.


Solution

  • You can't redirect standard input twice to to the same command. Either it reads the password from standard input, or it reads the here document on standard input. It can't do both.

    You can use a subshell to separate the two:

    sudo -S <<<"$password" bash -c 'tee /etc/systemd/system/$1 <<EOF
       .... your here document
    EOF
    ' -s "SERVICE_NAME"
    

    If you need literal single quotes inside the here document, a common solution is to use what I like to call "seesaw quoting". A single-quoted string immediately adjacent to a double-quoted string containing a literal single quote are joined by the shell into a single string with a single quote in it.

    sudo -S <<<"$password" bash -c 'tee /etc/systemd/system/$1 <<EOF
       [Service]
       ExecStart=echo "You don'"'"'t say"
    EOF
    ' -s "SERVICE_NAME"
    

    You might need a double take on that. That's a single-quoted string bash -c '... echo "You don' adjacent to the double-quoted string "'" adjacent to the single-quoted string 't say"...'.

    Perhaps notice also how this avoids the useless use of cat. sudo can perfectly read standard input from a here document (and doesn't even know that it's coming from one; the shell takes care of all of this).

    Anyway, as noted in comments, you really really want to avoid embedding your password in a script. Set up sudo with NOPASSWD or run the script as root in the first place (or avoid requiring root privileges if you can).