5

У меня есть скрипт, который запускает команду на удаленном сервере, используя SSH. Я хочу добавить строку Remote: к каждой строке вывода, но я не хочу, чтобы каждая строка задерживалась до тех пор, пока не будет доступна вся строка. Вот вывод из моей команды:

$ myproject-db-push my_database_name
Exporting from database... Done
Archiving data... Done
Uploading archive to remote... Done
Running install script on remote
Remote: Decompressing archive into temporary directory... Done
Remote: Using database: my_database_name
Remote: Dropping collections:
Remote:  - my_collection_foo
Remote:  - my_collection_bar
Remote: Importing new data... Done

В этом случае я использую sed следующим образом:

echo "$INSTALLCMD" | ssh -T "deploy@$SERVER" | sed -u "s/^/Remote: /"

Проблема, как я объяснил, в том, что на экран не выводятся частичные строки. Если я удалю | sed часть, это работает, как ожидалось. Сначала это написано:

Importing new data... 

И через несколько секунд строка завершается:

Importing new data... Done

Я предполагаю, что sed может работать только построчно. Я попытался установить его в небуферизованный, но он все еще ждет целых строк. Есть ли другой способ сделать это?

1 ответ1

2

Это немного сложно, потому что все эти утилиты (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; }

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .