Как добавить строку в вывод программы, не дожидаясь всей строки?
У меня есть скрипт, который запускает команду на удаленном сервере, используя SSH. Я хочу добавить строку Remote:
для каждой строки вывода, но я не хочу, чтобы каждая строка была отложена, пока не будет доступна вся строка. Вот вывод из моей команды:
$ myproject-db-push my_database_name Экспорт из базы данных... Готово Архивирование данных... Готово Загрузка архива на удаленный... Готово Запуск установочного скрипта на пульте Удаленный: распаковка архива во временный каталог... Готово Удаленный: Использование базы данных: my_database_name Remote: удаление коллекций: Удаленный: - my_collection_foo Удаленный: - my_collection_bar Удаленный: импорт новых данных... Готово
В этом случае я использую sed
как это:
echo "$INSTALLCMD" | ssh -T "deploy@$SERVER" | sed -u "s/^/Remote: /"
Проблема, как я объяснил, в том, что на экран не выводятся частичные строки. Если я удалю | sed
часть, это работает как ожидалось. Сначала это написано:
Импорт новых данных...
И через несколько секунд строка завершается:
Импорт новых данных... Готово
Я предполагаю, что sed
может работать только построчно. Я попытался установить его в небуферизованный, но он все еще ждет целых строк. Есть ли другой способ сделать это?
1 ответ
Это немного сложно, потому что все эти утилиты (sed
, awk
, grep
) являются буферизованными. Это означает, что они выводят выходные данные только после завершения строки (новая строка имеет значение). Они не могут читать вводимые символы за символом.
Поэтому для тестирования я сделал небольшую последовательность, которая имитирует ваше поведение:
{
echo -n "first task: "
sleep 2
echo "done"
echo -n "second task: "
sleep 2
echo "done"
}
Как и в вашем вопросе, он печатает first task:
и через 2 секунды done
, Попробуйте сами, скопировав его в свой терминал.
Решение:
Добавьте следующее за вашей командой:
IFS=
command | { x=1; while IFS= read -d'' -s -N 1 char; do
[ $x ] && printf "Remote: "
printf "$char"
unset x
[ "$char" == "
" ] && x=1
done; }
Объяснение:
read
встроенный из bash
может читать ввод за символом. Часть read -d'' -s -N 1 char
отключает разделитель -d''
, активирует беззвучный режим -s
и читает только 1 символ за раз -N 1
в переменную $char
, Затем команда проверяет, является ли переменная $x
существует. Если да, мы находимся в новой строке и печатаем "префикс". Затем напечатайте символ. Отозвать $x
, Затем последний оператор проверяет, является ли символ новой строкой. Если это набор новой строки $x
в 1
и в следующем цикле будет напечатан "префикс".
Все это можно проверить, когда вы объедините две последовательности:
{
echo -n "first task: "
sleep 2
echo "done"
echo -n "second task: "
sleep 2
echo "done"
} | { x=1; while IFS= read -d'' -s -N 1 char; do
[ $x ] && printf "Remote: "
printf "$char"
unset x
[ "$char" == "
" ] && x=1
done; }