Skip to main content
added 148 characters in body
Source Link
Stéphane Chazelas
  • 587k
  • 96
  • 1.1k
  • 1.7k
while IFS= read -r -u9 HOST ; do ssh "$HOST" "uname -a" ; done 9< servers.txt
#                  ^^^                                         ^^

ought to work. 9 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening filesshells will start from the first availableuse fds above some arbitrary number (so 3 is nextguaranteed to be used)at least 9 for their own internal soup, so users can freely make use of fds 0 to 9 in any POSIX shell. 9 is thus high enough to stay out of the way and the maximum one you can safely use portably. Another common one is 3, the first non-special one.

Instead of read -u9, in any Bourne-like shell, you can do read <&9 which is more portable.

It doesn't tell read to read from fd 9 but from fd 0 after it has been temporarily (only for the execution of read) to the same resource as on fd 9 which is functionally equivalent.

while IFS= read -r -u9 HOST ; do ssh "$HOST" "uname -a" ; done 9< servers.txt
#                  ^^^                                       ^^

ought to work. 9 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 9 is thus high enough to stay out of the way and the maximum you can use portably.

Instead of read -u9, in any Bourne-like shell, you can do read <&9

It doesn't tell read to read from fd 9 but from fd 0 after it has been temporarily (only for the execution of read) to the same resource as on fd 9.

while IFS= read -r -u9 HOST ; do ssh "$HOST" "uname -a" ; done 9< servers.txt
#                  ^^^                                         ^^

ought to work. 9 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, shells will use fds above some arbitrary number guaranteed to be at least 9 for their own internal soup, so users can freely make use of fds 0 to 9 in any POSIX shell. 9 is the maximum one you can safely use portably. Another common one is 3, the first non-special one.

Instead of read -u9, in any Bourne-like shell, you can do read <&9 which is more portable.

It doesn't tell read to read from fd 9 but from fd 0 after it has been temporarily (only for the execution of read) to the same resource as on fd 9 which is functionally equivalent.

added 319 characters in body
Source Link
Stéphane Chazelas
  • 587k
  • 96
  • 1.1k
  • 1.7k

At least inIn Korn-like shells such as ksh, bash or zsh, you can have read use a different file descriptor.

while IFS= read -u10r -u9 HOST ; do ssh $HOST"$HOST" "uname -a" ; done 10<9< servers.txt
#          ^^^^        ^^^                                       ^^

ought to work. 109 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 109 is thus high enough to stay out of the way and the maximum you can use portably.

See also the IFS= and -r to avoid read mangling backslash or IFS characters.

Instead of read -u9, in any Bourne-like shell, you can do read <&9

It doesn't tell read to read from fd 9 but low enoughfrom fd 0 after it has been temporarily (only for the execution of read) to be under the limit in some shells. Plus its a nice round number..same resource as on fd 9.

while IFS= read -r HOST ; do ssh $HOST"$HOST" "uname -a" < /dev/null; done < servers.txt

At least in bash, you can have read use a different file descriptor.

while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
#          ^^^^                                       ^^

ought to work. 10 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 10 is thus high enough to stay out of the way, but low enough to be under the limit in some shells. Plus its a nice round number...

while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt

In Korn-like shells such as ksh, bash or zsh, you can have read use a different file descriptor.

while IFS= read -r -u9 HOST ; do ssh "$HOST" "uname -a" ; done 9< servers.txt
#                  ^^^                                       ^^

ought to work. 9 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 9 is thus high enough to stay out of the way and the maximum you can use portably.

See also the IFS= and -r to avoid read mangling backslash or IFS characters.

Instead of read -u9, in any Bourne-like shell, you can do read <&9

It doesn't tell read to read from fd 9 but from fd 0 after it has been temporarily (only for the execution of read) to the same resource as on fd 9.

while IFS= read -r HOST ; do ssh "$HOST" "uname -a" < /dev/null; done < servers.txt
replaced http://unix.stackexchange.com/ with https://unix.stackexchange.com/
Source Link

ssh is reading the rest of your standard input.

while read HOST ; do … ; done < servers.txt

read reads from stdin. The < redirects stdin from a file.

Unfortunately, the command you're trying to run also reads stdin, so it winds up eating the rest of your file. You can see it clearly with:

$ while read HOST ; do echo start $HOST end; cat; done < servers.txt 
start server1.mydomain.com end
server2.mydomain.com
server3.mydomain.com

Notice how cat ate (and echoed) the remaining two lines. (Had read done it as expected, each line would have the "start" and "end" around the host.)

Why does for work?

Your for line doesn't redirect to stdin. (In fact, it reads the entire contents of the servers.txt file into memory before the first iteration). So ssh continues to read its stdin from the terminal (or possibly nothing, depending on how your script is called).

Solution

At least in bash, you can have read use a different file descriptor.

while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
#          ^^^^                                       ^^

ought to work. 10 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 10 is thus high enough to stay out of the way, but low enough to be under the limit in some shells. Plus its a nice round number...

Alternative Solution 1: -n

As McNisseMcNisse points out in his/her answerhis/her answer, the OpenSSH client has an -n option that'll prevent it from reading stdin. This works well in the particular case of ssh, but of course other commands may lack this—the other solutions work regardless of which command is eating your stdin.

Alternative Solution 2: second redirect

You can apparently (as in, I tried it, it works in my version of Bash at least...) do a second redirect, which looks something like this:

while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt

You can use this with any command, but it'll be difficult if you actually want terminal input going to the command.

ssh is reading the rest of your standard input.

while read HOST ; do … ; done < servers.txt

read reads from stdin. The < redirects stdin from a file.

Unfortunately, the command you're trying to run also reads stdin, so it winds up eating the rest of your file. You can see it clearly with:

$ while read HOST ; do echo start $HOST end; cat; done < servers.txt 
start server1.mydomain.com end
server2.mydomain.com
server3.mydomain.com

Notice how cat ate (and echoed) the remaining two lines. (Had read done it as expected, each line would have the "start" and "end" around the host.)

Why does for work?

Your for line doesn't redirect to stdin. (In fact, it reads the entire contents of the servers.txt file into memory before the first iteration). So ssh continues to read its stdin from the terminal (or possibly nothing, depending on how your script is called).

Solution

At least in bash, you can have read use a different file descriptor.

while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
#          ^^^^                                       ^^

ought to work. 10 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 10 is thus high enough to stay out of the way, but low enough to be under the limit in some shells. Plus its a nice round number...

Alternative Solution 1: -n

As McNisse points out in his/her answer, the OpenSSH client has an -n option that'll prevent it from reading stdin. This works well in the particular case of ssh, but of course other commands may lack this—the other solutions work regardless of which command is eating your stdin.

Alternative Solution 2: second redirect

You can apparently (as in, I tried it, it works in my version of Bash at least...) do a second redirect, which looks something like this:

while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt

You can use this with any command, but it'll be difficult if you actually want terminal input going to the command.

ssh is reading the rest of your standard input.

while read HOST ; do … ; done < servers.txt

read reads from stdin. The < redirects stdin from a file.

Unfortunately, the command you're trying to run also reads stdin, so it winds up eating the rest of your file. You can see it clearly with:

$ while read HOST ; do echo start $HOST end; cat; done < servers.txt 
start server1.mydomain.com end
server2.mydomain.com
server3.mydomain.com

Notice how cat ate (and echoed) the remaining two lines. (Had read done it as expected, each line would have the "start" and "end" around the host.)

Why does for work?

Your for line doesn't redirect to stdin. (In fact, it reads the entire contents of the servers.txt file into memory before the first iteration). So ssh continues to read its stdin from the terminal (or possibly nothing, depending on how your script is called).

Solution

At least in bash, you can have read use a different file descriptor.

while read -u10 HOST ; do ssh $HOST "uname -a" ; done 10< servers.txt
#          ^^^^                                       ^^

ought to work. 10 is just an arbitrary file number I picked. 0, 1, and 2 have defined meanings, and typically opening files will start from the first available number (so 3 is next to be used). 10 is thus high enough to stay out of the way, but low enough to be under the limit in some shells. Plus its a nice round number...

Alternative Solution 1: -n

As McNisse points out in his/her answer, the OpenSSH client has an -n option that'll prevent it from reading stdin. This works well in the particular case of ssh, but of course other commands may lack this—the other solutions work regardless of which command is eating your stdin.

Alternative Solution 2: second redirect

You can apparently (as in, I tried it, it works in my version of Bash at least...) do a second redirect, which looks something like this:

while read HOST ; do ssh $HOST "uname -a" < /dev/null; done < servers.txt

You can use this with any command, but it'll be difficult if you actually want terminal input going to the command.

ups
Source Link
Braiam
  • 36.9k
  • 29
  • 114
  • 176
Loading
some more solutions.
Source Link
derobert
  • 113.3k
  • 20
  • 242
  • 289
Loading
added 611 characters in body
Source Link
derobert
  • 113.3k
  • 20
  • 242
  • 289
Loading
added 92 characters in body
Source Link
derobert
  • 113.3k
  • 20
  • 242
  • 289
Loading
Source Link
derobert
  • 113.3k
  • 20
  • 242
  • 289
Loading