http://www.politiker-stopp.de/gfx/politiker-stopp-print.png

Benjamin Schieder

I HATE YOU, OPENSSH!

2015 January 13 | 6 comments

Sometimes, but just sometimes, I really, really hate OpenSSH. Consider the following code:

#!/bin/bash

while read hostname; do
	ssh -o PasswordAuthentication=no -i secret.key "${hostname}" \
				      "echo \$(hostname): \$(date -u)"
done < listofhosts.txt

The file listofhosts.txt contains - surprise - a list of hostnames, one per line. The expected output of this small script is something like this:

flora: Tue Jan 13 09:39:18 UTC 2015
pallas: Tue Jan 13 09:39:19 UTC 2015
athene: Tue Jan 13 09:39:21 UTC 2015
lutetia: Tue Jan 13 09:39:22 UTC 2015

Simple enough as far as scripts go, right? WRONG!
Now, what you DO get is this:

flora: Tue Jan 13 09:40:07 UTC 2015

Why? Because OpenSSHs ssh command drains STDIN. Why? No idea! Because it can, I guess. To get the above expected output, you need to do this:

#!/bin/bash

exec 3<listofhosts.txt

while read hostname <&3; do
	ssh -o PasswordAuthentication=no -i secret.key "${hostname}" \
				      "echo \$(hostname): \$(date -u)"
done

Hope that helps you somewhere down the line.

EOF

Category: blog

Tags: openssh rant shellscripting


6 Comments

From: KB
2015-01-13 22:35

Weird! Perhaps the password option has a bug, and is trying to read a password 3 times? ];-)

From: antrik
2015-01-14 18:24

I always found "read" to be rather icky. I'd just do "for i in `cat hosts.txt` do ..." or " Only case I would see for actually using read, is when the input can grow during exectution (e.g. read from a device/pipe) -- but I'm not sure I have *ever* run into such a situation myself...

From: blindcoder
2015-01-15 09:50

While I agree about ickyness of read, I haven't found a better way to handle it here. I have an input file from a third party in a csv-resembling-but-not-quite format with random whitespace, so for x in $(cat file) will not do it because x would cut around the whitespaces.

From: theYinYeti
2015-01-15 13:18

I did not test any of this, but I think another solution is to keep the read from stdin the way it was, and instead alter the SSH call, either like this: ssh … tYY.

From: Stephan
2015-02-07 22:10

You can of course use "for blub in $();" to read a file containing spaces. You can bash let only split on newlines, by placing "IFS=$'\n'" above your for Loop.
In many scripts I have the same block:
OldIFS="$IFS" IFS=$'\n' for blub in $();do IFS="$OldIFS";
do some stuff done
Together with bashs string manipulation, you can parse csv and similar.

From: blindcoder
2015-02-09 22:30

That is actually a pretty nice solution which I have also implemented now. Definately better than redirecting random filedescriptors.
I have however never succeeded in properly parsing CSV with bashs string manipulation alone. Stuff like this is incredibly hard to parse:
Value;Value;"Value;Value";Value;"Value";"Val\"ue;Value";Otherstuff

Post a comment

All comments are held for moderation; basic HTML formatting is accepted.

Name: (required)
E-mail: (required, not published)
Website: (optional)
Comment: