Как справиться с задержкой NFS в сценариях оболочки

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

Я попытался написать (настраиваемый) цикл таймаута следующим образом:

waitLoop()
{
   local timeout=
   local test=""

   if ! $test
   then
      local counter=0
      while ! $test && [ $counter -lt $timeout ]
      do
         sleep 1
         ((counter++))
      done

      if ! $test
      then
         exit 1
      fi
   fi
}

это работает для test="[ -e $somefilename ]". Однако тестирования существования недостаточно, мне иногда нужно проверить, есть ли определенный строка была записана в файл. Я попытался test="grep -sq "^sometext$" $somefilename", но это не работало. Может кто-нибудь сказать мне, почему?

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

8 ответов


вы можете установить тестовую переменную следующим образом:

test=$(grep -sq "^sometext$" $somefilename)

причина grep не работает, что кавычки действительно трудно передать в аргументах. Вам нужно будет использовать eval:

if ! eval $test

Я бы сказал на способ проверить строку в текстовом файле-grep.

в чем твоя проблема?

также вы можете настроить параметры монтирования NFS, чтобы избавиться от корневой проблемы. Синхронизация также может помочь. Увидеть НФС документы.


Если вы хотите использовать waitLoop в "if", вы можете изменить" exit "на" return", поэтому остальная часть скрипта может обрабатывать ситуацию с ошибкой (нет даже сообщения пользователю о том, что не удалось, прежде чем скрипт умрет в противном случае).

другая проблема-использование "$test " для удержания команды означает, что вы не получаете расширение оболочки при фактическом выполнении, просто оценивая. Поэтому, если вы говорите test= "grep \" foo\ "\ " bar baz\"", а не ищете строку из трех букв foo в файле с семью символами bar baz, он будет искать пять символов строки " foo "в файле девяти символов"bar baz".

таким образом, вы можете либо решить, что вам не нужна магия оболочки, и установить test='grep-sq ^sometext$ somefilename', либо вы можете заставить оболочку обрабатывать цитирование явно с чем-то вроде:

if /bin/sh -c "$test"
then
   ...

попробуйте использовать время изменения файла, чтобы определить, когда он написан, не открывая его. Что-то вроде

old_mtime=`stat --format="%Z" file`
# Write to file.
new_mtime=$old_mtime
while [[ "$old_mtime" -eq "$new_mtime" ]]; do 
  sleep 2;
  new_mtime=`stat --format="%Z" file`
done

Это не сработает, однако, если несколько процессов пытаются получить доступ к файлу одновременно.


У меня была точно такая же проблема. Я использовал аналогичный подход к таймауту ожидания, который вы включаете в свой OP; однако я также включил проверку размера файла. Я сбрасываю таймер тайм-аута, если файл увеличился в размере с момента последней проверки. Файлы, которые я пишу, могут быть несколькими концертами, поэтому они занимают некоторое время, чтобы писать через NFS.

Это может быть излишним для вашего конкретного случая, но у меня также был процесс написания вычислить хэш файла после того, как он был сделан. Я используется MD5, но что-то вроде crc32 тоже сработает. Этот хэш был передан от писателя (нескольким) читателям, и читатель ждет, пока a) размер файла не перестанет увеличиваться и b) (недавно вычисленный) хэш файла соответствует хэшу, отправленному писателем.


У нас аналогичная проблема, но по разным причинам. Мы читаем файл s, который отправляется на сервер SFTP. Машина работает скрипт не на SFTP-сервер.

то, что я сделал, это настроить его в cron (хотя цикл со сном тоже будет работать), чтобы сделать cksum файла. Когда старый cksum соответствует текущему cksum (файл не изменился за определенное количество времени), мы знаем, что записи завершены, и передаем файл.

просто большей безопасности, мы никогда не перезаписать локальный файл перед сделать резервную копию, и только передача вообще, если удаленный файл имеет два cksums подряд матч, и что cksum не соответствует локальный файл.

Если вам нужны примеры кода, я уверен, что смогу их откопать.


оболочка расщепляла предикат на слова. Возьмите все это с $@ как в коде ниже:

#! /bin/bash

waitFor()
{
  local tries=
  shift
  local predicate="$@"

  while [ $tries -ge 1 ]; do
    (( tries-- ))

    if $predicate >/dev/null 2>&1; then
      return
    else
      [ $tries -gt 0 ] && sleep 1
    fi
  done

  exit 1
}

pred='[ -e /etc/passwd ]'
waitFor 5 $pred
echo "$pred satisfied"

rm -f /tmp/baz
(sleep 2; echo blahblah >>/tmp/baz) &
(sleep 4; echo hasfoo   >>/tmp/baz) &

pred='grep ^hasfoo /tmp/baz'
waitFor 5 $pred
echo "$pred satisfied"

выход:

$ ./waitngo 
[ -e /etc/passwd ] satisfied
grep ^hasfoo /tmp/baz satisfied

жаль, что текст не так интересен, как просмотр его в режиме реального времени.


Ok...это немного странно...

Если у вас есть контроль над файлом: вы можете создать "именованный канал" здесь. Таким образом (в зависимости от того, как работает программа записи) вы можете контролировать файл в синхронизированном режиме.

В самом простом:

создать именованный канал:

mkfifo file.txt

настройка приемника синхронизации:

while :
do
    process.sh < file.txt
end

создать тестовый отправитель:

echo "Hello There" > file.txt

'process.sh-вот к чему ведет твоя логика : блокируется до тех пор, пока отправитель не запишет свой вывод. Теоретически программа писателя не нуждается в модификации....

предупреждение: Если приемник не работает по какой-либо причине, вы можете в конечном итоге блокировать отправителя!

не уверен, что это соответствует вашим требованиям здесь, но, возможно, стоит изучить.

или, чтобы избежать синхронизации, попробуйте "lsof"?

http://en.wikipedia.org/wiki/Lsof

предполагая, что вы хотите только читать из файла когда на него больше ничего не записывается (т. е. процесс записи завершен) - вы можете проверить, нет ли у него дескриптора файла ?