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

StackOverflow https://stackoverflow.com/questions/1078044

  •  21-08-2019
  •  | 
  •  

Вопрос

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

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

waitLoop()
{
   local timeout=$1
   local test="$2"

   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", но это не сработало.Может кто-нибудь сказать мне, почему?

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

Это было полезно?

Решение

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

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

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

if ! eval $test

Другие советы

я бы сказал тот способ проверить наличие строки в текстовом файле — grep.

В чем конкретно у вас проблема?

Также вы можете настроить параметры монтирования NFS, чтобы избавиться от корневой проблемы.Синхронизация также может помочь.См. документацию 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 тоже подойдет.Этот хэш был передан от записывающего устройства (несколько) читателям, и читатель ждет, пока а) размер файла не перестанет увеличиваться и б) (свежевычисленный) хэш файла не будет соответствовать хэшу, отправленному записывающим устройством.

У нас похожая проблема, но по другим причинам.Мы читаем файл, который отправляется на SFTP-сервер.Машина, на которой выполняется сценарий, не является SFTP-сервером.

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

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

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

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

#! /bin/bash

waitFor()
{
  local tries=$1
  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

Жаль, что машинописный текст не так интересен, как смотреть его в реальном времени.

Ладно... это немного странно...

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

В самом простом виде:

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

mkfifo file.txt

Настройте синхронизируемый приемник:

while :
do
    process.sh < file.txt
end

Создайте тестового отправителя:

echo "Hello There" > file.txt

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

ПРЕДУПРЕЖДЕНИЕ:если получатель по какой-то причине не работает, вы можете заблокировать отправителя!

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top