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