Search code examples
bashechosudo

How to literally include echo in a command?


In my script there's:

#!/bin/bash

# Take sudo password from the first argument if present
SUDO="sudo -u postgres"
if [ "$1" != "" ]; then
    SUDO="echo $1 | sudo -S -u postgres"
fi

${SUDO} psql -c "create database foo;"

... several other similar commands here ...

When I run the script with myPassword I get:

myPassword | sudo -S -u postgres psql -c create database foo;

So it just echoes the line. Obviously what I want is to literally run that command, but so that $1 gets expanded:

$ echo myPassword | sudo -S -u postgres psql -c create database foo;

How can I do that?


Solution

  • Pipes |, redirection < and stuff like that are not interpreted by the shell when you run a command by expanding a variable. Only words are split and globs (*, ?, []) are expanded. Example:

    $ v='echo a | cat'
    $ $v
    a | cat > b
    

    To interpret them, you could use eval

    $ v='echo a | cat'
    $ eval "$v"
    a
    

    However, this is frowned upon as you often end up with quoting issues. Usually, there are better ways to solve a specific problem.

    As a somewhat cleaner solution you could wrap your pipe inside a function:

    #!/bin/bash
    
    # Take sudo password from the first argument if present
    SUDO="sudo -u postgres"
    if [ "$1" != "" ]; then
        pw="$1"
        sudoWithPw() { echo "$pw" | sudo -S -u postgres "$@"; }
        SUDO="sudoWithPw"
    fi
    
    ${SUDO} psql -c "create database foo;"
    

    An even better solution would be to login as user postgres only once instead of prefixing every command with ${SUDO}. You can do so using su or sudo bash -c aFunctionYouExportedEarlier.