В сценарии удара, как получить stdout линию за линией

Во-первых, можно упростить команды SSH путем добавления псевдонима к Вашему ~/.ssh/config файл.

Host somehost
HostName some.host
User vivek
IdentityFile /where/is/my/id_dsa

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

mkdir somehost
sshfs somehost:/ somehost

Можно теперь действовать полностью на локальные файлы. Необходимо, вероятно, передать --no-whole-file опция к rsync, потому что иначе это предполагает, что локальная файловая система быстра и будет всегда копировать целый файл, а не применять свой возрастающий алгоритм дельты.

rsync -avz --progress --no-whole-file $(find somehost/remote/source/dir -type f -name '*.sql' | sort -nr | head -1) .

Теперь, вместо того, чтобы звонить find, используйте zsh в качестве своей оболочки. Как большинство find вызовы, Ваш может быть заменен несколькими спецификаторами шарика: . для -type f, On для числового вида, и [1] сохранять только первое соответствие.

rsync -avz --progress --no-whole-file somehost/remote/source/dir/**/*.sql(.on[1]) .

26
28.02.2014, 18:49
2 ответа

Просто вставьте команду в цикл while. Здесь есть ряд нюансов, но в основном (в bash или любой оболочке POSIX):

longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

Другая основная проблема с этим (кроме IFS ниже) - это когда вы пытаетесь использовать переменные внутри цикла после его завершения. Это происходит потому, что цикл на самом деле выполняется в под-оболочке (просто другой процесс оболочки), из которой вы не можете получить доступ к переменным (также она завершается, когда завершается цикл, и в этот момент переменные полностью исчезают. Чтобы обойти это, вы можете сделать:

longcommand | {
  while IFS= read -r line
  do
    whatever "$line"
    lastline="$line"
  done

  # This won't work without the braces.
  echo "The last line was: $lastline"
}

Пример Хауке о настройке lastpipe в bash является другим решением.

Обновление

Чтобы убедиться, что вы обрабатываете вывод команды "по мере ее выполнения", вы можете использовать stdbuf для установки stdout процесса в режим линейной буферизации.

stdbuf -oL longcommand |
  while IFS= read -r line
  do
    whatever "$line"
  done

Это настроит процесс на запись одной строки за раз в трубу вместо внутренней буферизации вывода в блоки. Имейте в виду, что программа может сама изменить эту настройку. Аналогичного эффекта можно добиться с помощью unbuffer (часть expect) или script.

stdbuf доступен в системах GNU и FreeBSD, он влияет только на stdio буферизацию и работает только для не-setuid, не-setgid приложений, которые динамически связаны (поскольку использует трюк LD_PRELOAD).

33
27.01.2020, 19:40
#! /bin/bash
set +m # disable job control in order to allow lastpipe
shopt -s lastpipe
longcommand |
  while IFS= read -r line; do lines[i]="$line"; ((i++)); done
echo "${lines[1]}" # second line
2
27.01.2020, 19:40

Теги

Похожие вопросы