The issue is that the command substitution that you want to run on the remote servers is run locally. This is due to the substitution occurring within double quotes, meaning the local shell will compute its expansion before calling ssh
.
Also, reading the output of ls
is ill-advised (see Why *not* parse `ls` (and what to do instead)?).
Instead:
servers=(
blue
green
red
)
for server in "${servers[@]}"; do
ssh -T "$server" <<END_SCRIPT
shopt -s nullglob
for pathname in /dir/xyz_$server*/details.txt; do
printf 'file path is "%s"\n' "$pathname"
done
END_SCRIPT
done
This uses an unquoted "here-document" as the script that is sent to the remote servers. The script first sets the nullglob
shell option (this is assuming that the remote shell is bash
) to avoid looping at all in the inner loop if the pattern does not match any filenames. It then iterates over the pattern /dir/xyz_$server*/details.txt
where $server
will be substituted with the current server name.
The loop then prints out a short message about what filenames matched the pattern, and the code ensures that the $pathname
variable is not expanded by the local shell by escaping the $
.
Note that $
in $server
is unescaped and will therefore be expanded by the local shell, which is what we want, but that $
in $pathname
needs to be escaped to supress the local expansion.
We don't need to set IFS
to anything non-default. I'm assuming you did that to be able to write the array assignment as you did, or for some other reason, but none of the code above needs that.
I'm using -T
with ssh
to explicitly turn off pseudo-TTY allocation.
You could also provide the script as a single string if you feel that this is somehow needed:
servers=(
blue
green
red
)
for server in "${servers[@]}"; do
ssh "$server" "
shopt -s nullglob
for pathname in /dir/xyz_$server*/details.txt; do
printf 'file path is "%s"\n' "$pathname"
done"
done
In this case, you must additionally ensure that each embedded double quote is escaped (or they would end the double-quoted string that constitutes the remote shell script).
ls
example was stupid by me... I'm ssh'ing to various servers then parsing the multitude of 'red', 'green', 'blue' files. For that I need to capture output in variables using$()
, but that doesn't work, also I'm having to escape variables for them to show like\$var
and I'm using functions defined locally, I'll try your answer but thought I'd leave this question if you answer applies to my case. – Nickotine Jan 30 '24 at 12:31$
, as in\$(some-command)
. – Kusalananda Jan 30 '24 at 12:47x=$(cmd file)
thenecho $x
returns nothing – Nickotine Jan 30 '24 at 12:50