views:

2835

answers:

6

I've been banging my head against for wall for a while with this one.

I want to SSH into a set of machines and check whether they are available (accepting connections and not being used). I have created a small script, tssh, which does just that:

#!/bin/bash

host=$1
timeout=${2:-1}

ssh -qo "ConnectTimeout $timeout" $host "[ \`who | cut -f1 | wc -l \` -eq 0 ] && exit 0 || exit 1"

This script works correctly. Returning 255 if there was a connection problem, 1 if the machine is busy and 0 if everything is good. If anyone knows a better way to do this please let me know.

So next I try and call tssh on my set of machines using a while read loop, and this is where it all goes wrong. The loop exits as soon as tssh returns 0 and never completes the full set.

while read nu ; do tssh "MYBOXES$nu" ; done < <(ruby -e '(0..20).each { |i| puts i }')

At first I thought this was a subshell problem but apparently not. Any help, along with comments on style/content, would be much appreciated! I know I'm going to kick myself when I find out why...

+2  A: 

Don't know if it would help, but a cleaner way of writing that would be

for nu in `ruby -e '(0..20).each { |i| puts i}'`; do
  tssh "MYBOXES$nu" 
done
Paul Tomblin
Also, if you have GNU Coreutils, you can use `seq 0 20` instead of the ruby command.
Jouni K. Seppänen
@PaulThanks, that works! Now if I only i knew why!Maybe it is a subshell issue after all. Certainly reminds of previous subshell issues I've had.@JouniI replaced the ugly ruby part with seq. Wasn't aware of that command, thanks.@AllAny insight into why this fix works?
+3  A: 

I'm also unsure about why it fails, but i like xargs and seq:

seq 0 20 | xargs -n1 tssh MYBOXES
Johannes Schaub - litb
+2  A: 

i cant believe it was the result of 0 that broke your loop, you can test against this by replacing your tssh command in the loop with "/bin/true" which also returns 0.

regarding style i dont understand why a simple looping shell script needs ruby, perl, seq or jot or any other binary that is not on my *BSD.

you can alternatively use the shells builtin for loop construct, which works at least in ksh, bash:

for ((i=0; $i<=20; i++)); do
    tssh "MYBOXES$i"
done
Kaii
+1  A: 

I ran into this today -- rsh and/or ssh can break a while read loop due to it using stdin. I put a -n into the ssh line which stops it from trying to use stdin and it fixed the problem.

+1  A: 

As Kaii mentioned, it's really overkill to call ruby or seq (which won't work on BSD machines) just to output a range of numbers. If you're happy with using bash you can:

for i in {0..20}; do
    # command
done

I believe this should work for bash 2.05b and up.

guns
+3  A: 

Chris is correct. The source of the loop breaking was SSH using stdin, however guns is correct in is usage of a looping methodology.

If you are looping through input (a file with a list of hostnames for example), and calling SSH, you need to pass the -n parameter, otherwise your loop based on input will fail.

while read host; do
  ssh -n $host "remote command" >> output.txt
done << host_list_file.txt
azirbes