Цикл while обрабатывает только первую запись команды ssh

Так что я не очень разбираюсь в bash и нуждаюсь в профессиональной помощи. Я пытаюсь запустить скрипт как этот:

filename='file1'
while read p; do
ssh -p 2222 $p 'who -b' | awk '{print $(NF-1)" "$NF}' >> file2*

я пытаюсь создать скрипт, который просматривает все адреса в файле1, чтобы увидеть, когда они в последний раз перезагружаются, а затем - ответ в файле2.

Проблема в том, что он проходит только по первому адресу, а не по другому.

Первый адрес получил пароль, который мне нужно ввести, чтобы продолжить процесс. Может ли это быть проблемой, или я указываю каждую строку в file1, или я делаю абсолютно неправильно с самого начала?

2 ответа

Решение

Наконец я предположил, что остальная часть сценария в порядке. Затем я последовал комментарию @ десерт и использовал shellcheck что привело меня к актуальной проблеме и ее решению:

SC2095: добавить < /dev/null чтобы предотвратить ssh от глотания стандартного ввода.

Таким образом, вы должны изменить свой скрипт следующим образом:

ssh -p 2222 "$p" 'who -b' < /dev/null | awk '{print $(NF-1)" "$NF}' >> 'file2'

Согласно первоначальному ответу и благодаря полезным советам, предоставленным в комментариях @EliahKagan и @rexkogitans, полный сценарий может выглядеть так:

#!/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/ заменяется -n вариант ssh команда. От man 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 - как говорит @StéphaneChazelas в своем энциклопедическом ответе - это канонический способ прочитать одну строку ввода с помощью read встроенный

    • Ключ в том, что read читает слова из строки (возможно, с обратной косой чертой), где слова $IFS delimited и backslash могут использоваться для экранирования (или продолжения строк). Итак read Команда должна быть настроена на чтение строк.

    • IFS= изменяет внутренний разделитель полей на пустую строку, таким образом мы сохраняем начальные и конечные пробелы в результате.

    • Опция -r - r aw input - отключает интерпретацию обратной косой черты и продолжения строки в прочитанных данных ( ссылка).

  • printf '%s %s' "$VAR1" "$VAR2" обеспечит лучший выходной формат ( ссылка).

  • LANG=C будет гарантировать идентичный выход who -b на каждом сервере, таким образом, анализ выходных данных с awk будет гарантировано.

  • Примечание здесь предполагается, что есть ~/.ssh/config файл и дополнительные параметры как -p 2222 не нужны ( ссылка).


Вызовите вышеупомянутый сценарий ssh-check.sh (не забудьте chmod +x) и использовать его следующим образом:

  • Используйте значения по умолчанию для входных (hosts.txt) и выходных (reboot- display.txt) файлов:

    ./ssh-check.sh
    
  • Установить пользовательское значение для выходного файла; установить пользовательское значение также для входных файлов:

    ./ssh-check.sh 'my-custom.out'
    ./ssh-check.sh 'my-custom.out' 'my-custom.in'
    

Прочтите этот ответ, чтобы увидеть, как можно улучшить весь подход.

Вы забыли закрыть цикл while-do. добавлять done к концу.

filename='file1'
while read p; do
   ssh -p 2222 $p 'who -b' | awk '{print $(NF-1)" "$NF}' >> file2*
done
Другие вопросы по тегам