lapwingg
lapwingg

Reputation: 112

Pass value of variable to complex command

I am trying to pass value from string variable to complex bash command and I have problem with it. When I executing commands:

files=($(ls abc*))

for file in "${files[@]}"
do
    scp $file [email protected]:~
    ssh [email protected] 'PGPASSWORD="abcd" psql -h domain.com -U user -d dbb -f ~/$file'
    ssh [email protected] 'rm ~/$file'
done

I get some errors. Only 'scp' is right executed but lines after them go fail. I am seeing "missing parameter for psql -f" and "file not found / cannot remove dictionary" What is problem with this code? How to pass string value to ssh commands?

Upvotes: 0

Views: 195

Answers (3)

chepner
chepner

Reputation: 532053

Instead of copying each file to the remote host for execution, set up an SSH tunnel and run psql locally, connecting to the remote db via the tunnel.

ssh -N -L 12345:domain.com:$DB_PORT [email protected] & ssh_pid=$!

files=(abc*)

for file in "${files[@]}"
do
    psql -p 12345 -U user -d dbb -f "$file"
done

kill "$ssh_pid"

This has the added benefit of not exposing the database password, since you can simply type it manually or use a local .pgpass file.

Note that the tunnel is only necessary if the remote database isn't configured to accept network connections.

Upvotes: 0

tripleee
tripleee

Reputation: 189749

Single quotes will prevent the remote shell from receiving your variable. Don't use ls in scripts. Quote your variables. Use http://shellcheck.net/ before asking for human review.

for file in abc*
do
    scp "$file" [email protected]:~
    ssh [email protected] "PGPASSWORD='abcd' psql -h domain.com -U user -d dbb -f ~/'$file'; 
        rm ~/'$file'"
done

The quoting here is slightly tricky, and won't work if your file names might contain e.g. literal single quotes. The double quotes cause the local shell to expand $file; then the expanded value is in single quotes to prevent the remote ssh from manipulating it any further.

I took out the array because it didn't seem to serve any useful purpose. If you want an array, assign it simply with a wildcard:

files=(abc*)

Upvotes: 3

John Bollinger
John Bollinger

Reputation: 181159

What is problem with this code? How to pass string value to ssh commands?

One of the important distinctions between quoting with double quotes (") and quoting with single quotes (') is that the latter suppresses parameter expansion. You need to either move the $file outside the quotes, or change to double-quote style. Additionally, however, you should properly quote the expansions of $file, else you leave yourself open to breakage or even nasty surprises in the event of unusual file names, such as those containing spaces.

Moreover, the fact that you are formatting a command string that will be processed by a different shell adds some complication. The easiest way to be sure that you get the quoting right is to use bash's printf builtin, whose %q formatting option serves exactly the purpose of quoting an arbitrary string so that the result can be re-read as shell input to reproduce the same string. Overall, then, here's my recommendation for the loop:

for file in "${files[@]}"
do
    scp "${file}" [email protected]:~
    ssh [email protected] "PGPASSWORD='abcd' psql -h domain.com -U user -d dbb -f $(printf %q "~/${file}")"
    ssh [email protected] "rm $(printf %q "~/${file}")"
done

Upvotes: 2

Related Questions