Как добавить строку в вывод программы, не дожидаясь всей строки?

У меня есть скрипт, который запускает команду на удаленном сервере, используя 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; }
Другие вопросы по тегам