Como lidar com a latência NFS em scripts shell
Pergunta
Eu estou escrevendo scripts shell onde bastante regularidade alguma coisa está escrito
para um arquivo, depois que um aplicativo é executado que lê esse arquivo. Acho que através da nossa empresa os difere de latência de rede muito, portanto, um sleep 2
simples, por exemplo, não será suficiente robusta.
Eu tentei escrever um (configurável) loop de tempo limite como esta:
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
}
Isso funciona para test="[ -e $somefilename ]"
. No entanto, testando a existência não é o suficiente, eu às vezes é preciso testar se uma determinada cadeia foi gravada no arquivo. eu tentei
test="grep -sq \"^sometext$\" $somefilename"
, mas isso não funcionou. Alguém pode me dizer por quê?
Existem outras, menos detalhado opções para realizar tal teste?
Solução
Você pode definir seu variável de teste desta maneira:
test=$(grep -sq "^sometext$" $somefilename)
A razão seu grep
não está funcionando é que as aspas são realmente difíceis de passar argumentos. Você vai precisar usar eval
:
if ! eval $test
Outras dicas
Eu diria que o maneira de verificar se há uma seqüência em um arquivo de texto é grep.
Qual é o seu problema exato com ele?
Além disso, você pode ajustar seus NFS parâmetros de montagem, para se livrar do problema de raiz. A sincronização também pode ajudar. Veja docs NFS.
Se você está querendo usar waitLoop em um "se", você pode querer mudar a "saída" para um "retorno", para que o resto do script pode lidar com a situação de erro (não há nem mesmo uma mensagem para o usuário sobre o que falhou antes de as matrizes de script não).
A outra questão é o uso de "$ test" para manter um meio de comando que não recebem expansão shell quando realmente executar, apenas avaliando. Então, se você diz test = "grep \" foo \ "\ 'bar baz \'", em vez de olhar para o três letras foo string no arquivo com os sete baz nome do personagem bar, ele vai olhar para a cadeia de cinco carvão "foo" no arquivo de nove char "bar baz".
Então você pode decidir que não precisa a magia shell, e teste de set = 'grep -sq ^ sometext $ somefilename', ou você pode obter o shell para lidar com a citar explicitamente com algo como:
if /bin/sh -c "$test"
then
...
Tente usar o tempo de modificação do arquivo para detectar quando é escrito sem abri-lo. Algo como
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
Este não vai funcionar, no entanto, se vários processos tentar acessar o arquivo ao mesmo tempo.
Eu só tinha exatamente o mesmo problema. Eu usei uma abordagem semelhante para o tempo limite de espera que você incluir na sua OP; no entanto, eu também incluiu uma verificação de tamanho do ficheiro. Eu redefinir minha temporizador tempo limite se o arquivo tinha aumentado de tamanho desde a última foi marcada. Os arquivos que eu estou escrevendo pode ser um pouco show, assim que tomar um tempo para escrever através NFS.
Este pode ser um exagero para o seu caso particular, mas eu também tinha o meu processo de escrita calcular um hash do arquivo depois que ele foi feito por escrito. Eu costumava md5, mas algo como crc32 iria funcionar, também. Esse hash foi transmitido a partir do escritor para os (vários) leitores, eo leitor espera até que a) o tamanho do arquivo pára aumentar e b) o (recém computadorizada) hash do arquivo coincide com o hash enviado pelo escritor.
Nós temos um problema semelhante, mas por razões diferentes. Estamos lendo arquivo s, que é enviado para um servidor de SFTP. A máquina executando o script não é o servidor SFTP.
O que tenho feito é configurá-lo no cron (embora um laço com um sono iria trabalhar muito) para fazer uma cksum do arquivo. Quando o velho cksum corresponde ao cksum atual (o arquivo não foi alterado para a quantidade de tempo determinado) sabemos que as gravações estão completos, e transferir o arquivo.
Apenas para ser seguro extra, nós nunca substituir um arquivo local antes de fazer uma cópia de segurança, e só transferir em tudo quando o arquivo remoto tem dois cksums consecutivo em que jogo, e que cksum não coincide com o arquivo local.
Se precisar de exemplos de código, estou certo de que posso desenterrá-los.
O escudo foi dividir o seu predicado em palavras. Pegue tudo com $@
como no código a seguir:
#! /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"
Output:
$ ./waitngo [ -e /etc/passwd ] satisfied grep ^hasfoo /tmp/baz satisfied
Pena que o texto datilografado não é tão interessante quanto vê-lo em tempo real.
Ok ... isso é um pouco whacky ...
Se você tem controle sobre o arquivo: você pode ser capaz de criar um 'pipe nomeado' aqui. Então, (dependendo de como o programa funciona escrita) você pode monitorar o arquivo de forma sincronizada.
Na sua forma mais simples:
Criar o pipe nomeado:
mkfifo file.txt
Configurar o receptor sincronizado:
while :
do
process.sh < file.txt
end
Criar um remetente de teste:
echo "Hello There" > file.txt
O 'process.sh' é onde a sua lógica é: isto irá bloquear até que o remetente tem escrito sua saída. Em teoria, o programa escritor não vai precisar modifiying ....
AVISO: se o receptor não está funcionando por algum motivo, você pode acabar bloqueando o remetente
Não tenho certeza que se adapta à sua exigência aqui, mas pode valer a pena olhar em.
ou para evitar sincronizado, try 'lsof'?
http://en.wikipedia.org/wiki/Lsof
Assumindo que você só quer ler a partir do arquivo quando nada mais está escrevendo para ele (ou seja, o processo de escrita tenha terminado?) - você pode verificar se nada mais tem identificador de arquivo para ele