1

Trying it this way:

#!/bin/bash
myvals=`psql -d mydb -c "select id from table1 where 't'"`

ssh user1@host1.domain.tld "for i in $myvals; do echo \$i >> values; done"

As long as psql returns just one value, it works fine. But if its several values, I receive this response:

bash: -c: line 1: syntax error near unexpected token `2'
bash: -c: line 1: `2'

Also, I tried to:

myvals='1 2 3'

And then it works fine: the values 1 2 3 are appended to the "values" file on the remote host; no error mesages. If I try another subshell command, such as myvals=ls /bin, errors reappear. It's clear that $myvals is evaluated on the local host already but what makes the subshell results so different?

Brewer
  • 23
  • 6
  • 1
    _what makes the subshell results so different?_ Answer: there are newlines in your variable that screw up the parser. Try it with `myvals=$'1\n2\n3'`. – gniourf_gniourf Nov 25 '17 at 21:59
  • 1
    `myvals` is not an array at all in this script. Run `declare -p myvals` to dump its type and value -- if that outputs `declare -- myvals=...` it's a string; if it emits `declare -a myvars=...` it's an array. – Charles Duffy Nov 25 '17 at 22:04

2 Answers2

5

If It's Not Really An Array...

Iterating over a string as if it were an array is innately buggy. Don't do it. That said, to generate a safely-escaped (eval-safe) version of your value, use printf %q.

#!/bin/bash

myvals=`psql -d mydb -c "select id from table1 where 't'"`
printf -v myvals_q %q "$myvals"

ssh user1@host1.domain.tld \
  "myvals=$myvals_q;"' for i in $myvals; do echo "$i"; done >>values'

If You Actually Had An Array

#!/bin/bash
readarray -t myvals < <(psql -d mydb -c "select id from table1 where 't'")
printf -v myvals_q '%q ' "${myvals[@]}"

ssh user1@host1.domain.tld \
  "myvals=( $myvals_q );"' for i in "${myvals[@]}"; do echo "$i"; done >>values'

If You Don't Need To Store The Value Locally In The First Place

#!/bin/bash

ssh user1@host1.domain.tld \
  'while read -r i; do echo "$i"; done >>values' \
  < <(psql -d mydb -c "select id from table1 where 't'")

General Notes

  • Running echo "$i" >>values over and over in a loop is inefficient: Every time the line is run, it re-opens the values file. Instead, run the redirection >values over the whole loop; this truncates the file exactly once, at the loop's start, and appends all values generated therein.
  • Unquoted expansions are generally dangerous. For example, if foo='*', then $foo will be replaced with a list of files in the current directory, but "$foo" will emit the exact contents -- *. Similarly, tabs, whitespace runs, and various other contents can be unintentionally damaged by unquoted expansion, even when passing directly to echo.
  • You can switch quoting types in the same string -- thus, "$foo"'$foo' is one string, the first part of which is replaced with the value of the variable named foo, and the second component of which is the exact string $foo.
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • I guess OP wants to loop through the lines of the command `psql`... so maybe something like `ssh user1@host1.domain.tld 'while read line; do echo "$line"; done' < <(psql ...)` would be more appropriate. – gniourf_gniourf Nov 25 '17 at 22:13
  • *shrug* -- that's a judgment call, and the answer given here (particularly the `readarray` variant) *does* do what we both agree they're asking for (iterating over values on the remote end). Passing content out-of-band that way certainly does have its place, but it means you can't use stdin for anything else (including passing the script code itself). – Charles Duffy Nov 25 '17 at 22:14
  • 1
    @gniourf_gniourf, ...on further thought, it's well worth editing to add that option; doing so. – Charles Duffy Nov 25 '17 at 22:18
  • Very useful info, thanks Charles Duffy. Case 3 (without storing the values locally) works fine. But in case 2 I get this error: ./fence2.sh: line 6: unexpected EOF while looking for matching `'' ./fence2.sh: line 7: syntax error: unexpected end of file Otherwise, case 2 would be better b/c I need to send the command to more than one host. – Brewer Nov 26 '17 at 10:14
  • @Charles, you probably meant a single quote at the end, as in case 1: done >>values' That way it works. – Brewer Nov 26 '17 at 12:12
  • Quite right -- I fixed that in the first example, but missed that I'd copied it as a template for the second; thanks! – Charles Duffy Nov 26 '17 at 16:24
0

You can send the output as a file:

#!/bin/bash
psql -d mydb -c "select id from table1 where 't'" > /tmp/values
scp values user1@host1.domain.tld:/tmp/

or pipe it to the remote host:

psql -d mydb -c "select id from table1 where 't'" | \
  ssh user1@host1.domain.tld 'while read line; do echo $line; done'
perreal
  • 94,503
  • 21
  • 155
  • 181