2009-07-03 78 views
6

我正在编写shell脚本,其中经常将一些东西写入文件 ,之后执行读取该文件的应用程序。我发现通过我们公司的网络延迟差异很大,所以例如简单的sleep 2将不够健壮。如何处理shell脚本中的NFS延迟

我试着写一(配置)超时循环是这样的:

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",但这没有奏效。有人能告诉我为什么吗?

是否还有其他的,不太详细的选项来执行这样的测试?

+0

是否可以简单地在文件上运行尾部,并只检查尾部的输出(即最后一行)? 这假设尾巴更聪明地检查然后你的脚本检查文件何时更改(机会是,尾巴以更合适的方式) – nos 2009-07-03 15:14:00

+0

我认为正确的方法将取决于“有些东西”是什么以及什么“应用程序”是。你能否提供你想要解决的实际问题的更多细节?所写的问题有点模糊;可能会有完全不同的方法,你没有想到。 – 2009-12-20 09:15:40

+0

看到我编辑的答案。 – 2009-12-24 00:44:46

回答

1

您可以设置测试变量是这样的:

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

grep不工作的原因是报价真的很难在参数传递。你需要使用eval:使用

if ! eval $test 
0

我想说检查文本文件中字符串的方式是grep。

你的确切问题是什么?

此外,您可以调整您的NFS装载参数,以摆脱根本问题。同步可能也有帮助。请参阅NFS文档。

+0

我无法更改安装参数。我的脚本应该足够强大来处理延迟。问题不在于grep本身,而在于grep测试没有像我期望的那样得到评估。 – 2009-07-03 07:10:51

0

如果您想在“if”中使用waitLoop,您可能需要将“exit”更改为“return”,以便脚本的其余部分可以处理错误情况(甚至没有消息向用户通知脚本死亡之前发生了什么故障)。

另一个问题是使用“$ test”来保存一个命令意味着你在实际执行时没有获得shell扩展,只是评估。因此,如果您说test =“grep \”foo \“\”bar baz \“”,而不是在文件中用七个字符名称bar baz查找三个字母字符串foo,它会查找五个char字符串在九个char文件“bar baz”中的“foo”。

所以,你可以决定你不需要外壳的魔法,并设置测试=“grep的-sq^sometext $ somefilename”,或者你可以让外壳来处理的东西,如明确报价:

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

试试文件的修改时间时,它是在没有打开它写入检测。像

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 

这东西是行不通的,但是,如果多个进程尝试在同一时间访问该文件。

0

我刚刚有完全相同的问题。我使用了类似的方法来处理OP中包含的超时等待事件;不过,我还包括一个文件大小的检查。如果文件在上次检查后增加了大小,我会重置超时定时器。我正在写的文件可能只有几个gig,所以他们需要一段时间才能跨NFS进行写入。

对于您的特定情况,这可能是过度的,但我也有我的写作过程计算文件的散列写完后。我使用了md5,但是像crc32这样的东西也可以工作。该散列从作者广播到(多个)阅读器,并且阅读器等待,直到a)文件大小停止增加,以及b)文件的(新计算的)散列与作者发送的散列相匹配。

0

我们有类似的问题,但原因不同。我们正在读取s文件,将其发送到SFTP服务器。运行脚本的机器不是SFTP服务器。

我所做的是在cron中设置它(尽管带睡眠的循环也会起作用)来执行文件的cksum。当旧的cksum与当前的cksum匹配时(文件没有改变确定的时间),我们知道写入完成并传输文件。

为了保证安全,我们从不在备份前覆盖本地文件,只有当远程文件中有两个cksum匹配且cksum与本地文件不匹配时才会传输。

如果你需要代码示例,我相信我可以挖掘它们。

0

shell将您的谓词分解为单词。抓住它所有[email protected]如下面的代码:

#! /bin/bash 

waitFor() 
{ 
    local tries=$1 
    shift 
    local predicate="[email protected]" 

    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 

太糟糕了打字稿是不是看着它实时一样有趣。

-1

好吧......这是一个有点古怪......

如果在文件控制:你也许可以创造一个“命名管道”在这里。 因此(取决于编写程序的工作方式),您可以以同步方式监视文件。

在最简单的:

创建命名管道:

mkfifo file.txt 

设置的同步时间接收器:

while : 
do 
    process.sh < file.txt 
end 

创建测试发件人:

echo "Hello There" > file.txt 

'process.sh'是你的逻辑在哪里:这将阻塞,直到发送者写出它的输出。从理论上讲,作家程序不需要修改....

警告:如果接收器由于某种原因没有运行,最终可能会阻止发送者!

不确定它是否符合您的要求,但可能值得研究。

或者为避免同步,请尝试'lsof'?

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

假设你只希望从文件中读取时没有其他书面形式向它(即写作过程已完成) - 你可以检查是否没有其他具有文件句柄呢?