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?
Ответы
Ответ 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 ...