There are several issues with your approach:
Your here doc gets interpolated locally. The actual command you are running is: for w in var asd dsa 123 ; do echo >&2; echo "123"; done
You can see that by using set -x to enable tracing locally, and run ssh with cat instead of the login shell:
ssh pi cat << EOF
for w in $(echo "/var/asd/dsa/123" | tr "/" " ") ; do echo $w >&2; echo "123"; done
EOF
+ ssh pi cat
++ echo /var/asd/dsa/123
++ tr / ' '
for w in var asd dsa 123 ; do echo >&2; echo "123"; done
As you can see echo /var/asd/dsa/123
and tr / ' '
are executed locally, and $w is interpolated to the empty string.
You probably want to use single quotes around 'EOF':
ssh pi cat << 'EOF'
for w in $(echo "/var/asd/dsa/123" | tr "/" " ") ; do echo $w >&2; echo "123"; done
EOF
+ ssh pi cat
for w in $(echo "/var/asd/dsa/123" | tr "/" " ") ; do echo $w >&2; echo "123"; done
Now, the command gets there correctly.
Note: if you wish that $(echo "/var/asd/dsa/123" | tr "/" " ")
is run locally, then you at least need to escape $w.
ssh pi << EOF
for w in $(echo "/var/asd/dsa/123" | tr "/" " ") ; do echo \$w >&2; echo "123"; done
EOF
Buffering will mess up the order. That's why your empty lines (the ones printed by echo >&2
) are at the end of your output. You can use tee
to "force" line buffering:
ssh -T pi << 'EOF'
for w in $(echo "/var/asd/dsa/123" | tr "/" " ") ; do echo $w >&2; echo "123"; done
EOF
+ ssh -T pi
Linux mserv 3.10.25+ #622 PREEMPT Fri Jan 3 18:41:00 GMT 2014 armv6l
...
123
123
123
123
var
asd
dsa
123
Versus:
ssh pi << 'EOF'
for w in $(echo "/var/asd/dsa/123" | tr "/" " ") ; do echo $w >&2; echo "123"; done 2>&1 | tee
EOF
+ ssh pi
Linux mserv 3.10.25+ #622 PREEMPT Fri Jan 3 18:41:00 GMT 2014 armv6l
...
var
123
asd
123
dsa
123
123
123
(in the first case STDIN is printed first, then STDERR, in the second, lines are alternated)
Piping commands to ssh and forcing a login shell
If you run commands by piping them to stdin of ssh, it will run a login shell and print the MOTD (depending on the server configuration) and run a login shell (which has several side effects).
In my example, Linux mserv 3.10.25+ #622 PREEMPT Fri Jan 3 18:41:00 GMT 2014 armv6l
is part of the MOTD. However, there is no need for piping, ssh will run last argument via the user shell, without the need for piping it to STDIN. This might be limited to MAX_ARG_STRLEN (which is ~128K), but if you have a script that size, you probably should scp-it to the host first.
$ ssh pi '
for w in $(echo "/var/asd/dsa/123" | tr "/" " ")
do
echo $w >&2
echo "123"
done 2>&1 | tee
'
var
123
asd
123
dsa
123
123
123
Note: This might be limited to MAX_ARG_STRLEN (which is ~128K), but if you have a script that size, you probably should scp-it to the host first.
Note: pi
is a test server I use, replace it with your appropriate connection info.