Ssh вырывается из цикла while в bash - программирование
Подтвердить что ты не робот

Ssh вырывается из цикла while в bash

Я использую этот bash -код для загрузки файлов на удаленный сервер, для нормальных файлов это прекрасно работает:

for i in `find devel/ -newer $UPLOAD_FILE`
do
    echo "Upload:" $i
    if [ -d $i ]
    then
        echo "Creating directory" $i
        ssh [email protected]$SERVER "cd ${REMOTE_PATH}; mkdir -p $i"
        continue
    fi
    if scp -Cp $i [email protected]$SERVER:$REMOTE_PATH/$i
    then
        echo "$i OK"
    else
        echo "$i NOK"
        rm ${UPLOAD_FILE}_tmp
    fi
done

Единственная проблема заключается в том, что для файлов с пробелом в имени, for-loop терпит неудачу, поэтому я заменил первую строку следующим образом:

find devel/ -newer $UPLOAD_FILE | while read i
do
    echo "Upload:" $i
    if [ -d $i ]
    then
        echo "Creating directory" $i
        ssh [email protected]$SERVER "cd ${REMOTE_PATH}; mkdir -p $i"
        continue
    fi
    if scp -Cp $i [email protected]$SERVER:$REMOTE_PATH/$i
    then
        echo "$i OK"
    else
        echo "$i NOK"
        rm ${UPLOAD_FILE}_tmp
    fi
done

По какой-то странной причине ssh-команда вырывается из цикла while, поэтому первый недостающий каталог создается отлично, но все последующие отсутствующие файлы/каталоги игнорируются.

Я предполагаю, что это связано с тем, что ssh пишет что-то в stdout, что смущает команду "читать". Комментируя ssh-команду, цикл работает так, как должен.

Кто-нибудь знает, почему это происходит, и как можно предотвратить ssh от нарушения цикла while?

4b9b3361

Ответ 1

Проблема заключается в том, что ssh читается со стандартного ввода, поэтому он ест все оставшиеся строки. Вы можете просто подключить свой стандартный вход в никуда:

ssh [email protected]$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" < /dev/null

Вы также можете использовать ssh -n вместо перенаправления.

Ответ 2

Другим подходом является цикл над FD, отличный от stdin:

while IFS= read -u 3 -r -d '' filename; do
  if [[ -d $filename ]]; then
    printf -v cmd_str 'cd %q; mkdir -p %q' "$REMOTE_PATH" "$filename"
    ssh "[email protected]$SERVER" "$cmd_str"
  else
    printf -v remote_path_str '%[email protected]%q:%q/%q' "$USER" "$SERVER" "$REMOTE_PATH" "$filename"
    scp -Cp "$filename" "$remote_path_str"
  fi
done 3< <(find devel/ -newer "$UPLOAD_FILE" -print0)

Операторы -u 3 и 3< здесь имеют решающее значение, используя FD 3, а не стандартный FD 0 (stdin).

Приведенный здесь подход - используя -print0, очищенное значение IFS и т.п. - также менее ошибочно, чем исходный код и существующий ответ, который не может правильно обрабатывать интересные имена файлов. (Ответ Гленна Джекмана близок, но даже это не может иметь дело с именами файлов с новыми символами или именами файлов с завершающим пробелом).

Использование printf %q имеет решающее значение для создания команд, которые нельзя использовать для атаки на удаленную машину. Подумайте, что произойдет с файлом с именем devel/$(rm -rf /)/hello с кодом, который не имел этой паранойи.

Ответ 3

В дополнение к choroba answer, не используйте цикл for для чтения имен файлов:

find devel/ -newer $UPLOAD_FILE | 
while read -r i
do ...