While loop only processes the first entry of ssh command
Solution 1:
Finally I assumed that, the rest part of the script is okay. Then I followed @dessert's comment and used shellcheck
that lead me to the actual issue and its solution:
SC2095: Add
< /dev/null
to prevent ssh from swallowing stdin.
So you have to change your script in this way:
ssh -p 2222 "$p" 'who -b' < /dev/null | awk '{print $(NF-1)" "$NF}' >> 'file2'
According to the original answer and thanks to the useful advises, provided in the comments by @EliahKagan and @rexkogitans, the complete script could look as this:
#!/bin/bash
# Collect the user's input, and if it`s empty set the default values
[[ -z "${1}" ]] && OUT_FILE="reboot-indication.txt" || OUT_FILE="$1"
[[ -z "${2}" ]] && IN_FILE="hosts.txt" || IN_FILE="$2"
while IFS= read -r host; do
indication="$(ssh -n "$host" 'LANG=C who -b' | awk '{print $(NF-1)" "$NF}')"
printf '%-14s %s\n' "$host" "$indication" >> "$OUT_FILE"
done < "$IN_FILE"
-
< /dev/null/
is replaced by the-n
option of thessh
command. Fromman ssh
:-n Redirects stdin from /dev/null (actually, prevents reading from stdin). This must be used when ssh is run in the background... This does not work if ssh needs to ask for a password or passphrase; see also the -f option.
-
IFS= read -r line
- as @StéphaneChazelas says in his encyclopedic answer - is the canonical way to read one line of input with theread
builtin.The key is that
read
reads words from a (possibly backslash-continued) line, where words are$IFS
delimited and backslash can be used to escape the delimiters (or continue lines). So theread
command should be tweaked to read lines.IFS=
changes the internal field separator to the null string, thus we preserve leading and trailing whitespace in the result.The option
-r
- raw input - disables interpretion of backslash escapes and line-continuation in the read data (reference).
printf '%s %s' "$VAR1" "$VAR2"
will provide a better output format (reference).LANG=C
will guarantee identical output ofwho -b
on each server, thus the parsing of the output withawk
will be guaranteed as well.Note here is assumed there is
~/.ssh/config
file and additional parameters as-p 2222
are not needed (reference).
Call the above scrip ssh-check.sh
(don't forget to chmod +x
) and use it in this way:
-
Use the default values of the input (hosts.txt) and the output (reboot-indication.txt) files:
./ssh-check.sh
-
Set custom value for the output file; set custom value also for the input files:
./ssh-check.sh 'my-custom.out' ./ssh-check.sh 'my-custom.out' 'my-custom.in'
Read this answer to see how the entire approach could be improved.
Solution 2:
You forgot to close the while-do loop. Add done
to the end.
filename='file1'
while read p; do
ssh -p 2222 $p 'who -b' | awk '{print $(NF-1)" "$NF}' >> file2*
done