如何处理shell脚本中的NFS延迟

时间:2009-07-03 06:48:02

标签: bash ksh latency

我正在编写shell脚本,其中经常写一些东西 到一个文件,然后执行一个读取该文件的应用程序。我发现,通过我们公司,网络延迟差别很大,因此简单的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",但这不起作用。有人可以告诉我为什么吗?

是否还有其他更简洁的选项来执行此类测试?

8 个答案:

答案 0 :(得分:1)

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

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

你的grep无法正常工作的原因是引号很难在参数中传递。您需要使用eval

if ! eval $test

答案 1 :(得分:0)

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

你的确切问题是什么?

您也可以调整NFS挂载参数,以消除根本问题。同步也可能有所帮助。请参阅NFS文档。

答案 2 :(得分:0)

如果您想在“if”中使用waitLoop,您可能希望将“exit”更改为“return”,因此脚本的其余部分可以处理错误情况(甚至没有消息用户在脚本死之前失败的原因。)

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

所以你可以决定你不需要shell魔法,并设置test ='grep -sq ^ sometext $ somefilename',或者你可以让shell明确地处理引用:

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

答案 3 :(得分: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

但是,如果多个进程同时尝试访问该文件,则无效。

答案 4 :(得分:0)

我刚才遇到了同样的问题。我对你在OP中包含的超时等待采用了类似的方法;但是,我还包括一个文件大小的检查。如果文件自上次检查后文件大小增加,我会重置我的超时计时器。我正在写的文件可能只有几个,所以他们需要一段时间来编写NFS。

对于您的特定情况,这可能有点过分,但我的写作过程也会在完成写入后计算文件的哈希值。我使用的是md5,但是像crc32这样的东西也可以使用。这个哈希从作者广播到(多个)读者,并且读者等待直到a)文件大小停止增加和b)文件的(新计算的)哈希与写入者发送的哈希相匹配。

答案 5 :(得分:0)

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

我所做的是在cron中设置它(虽然一个带睡眠的循环也可以工作)来做一个文件的cksum。当旧的cksum与当前的cksum匹配时(文件在确定的时间内没有改变),我们知道写入完成,并传输文件。

为了更加安全,我们永远不会在进行备份之前覆盖本地文件,并且只在远程文件在一行中有两个匹配的cksum并且cksum与本地文件不匹配时才传输。

如果您需要代码示例,我相信我可以将它们挖掘出来。

答案 6 :(得分:0)

shell将你的谓词分成单词。使用$@抓取所有内容,如下面的代码所示:

#! /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

打字稿并不像实时观看那样有趣。

答案 7 :(得分:-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

假设您只想在没有其他内容写入时从文件中读取(即写入过程已完成) - 您可以检查是否有其他文件处理它?<​​/ p>