Bash 如何在shell脚本中处理NFS延迟

Bash 如何在shell脚本中处理NFS延迟,bash,ksh,latency,Bash,Ksh,Latency,我正在编写shell脚本,其中经常会编写一些东西 到文件,然后执行读取该文件的应用程序。我发现,通过我们的公司,网络延迟差异很大,因此简单的sleep 2就不够健壮 我尝试编写一个(可配置的)超时循环,如下所示: waitLoop() { local timeout=$1 local test="$2" if ! $test then local counter=0 while ! $test && [ $counter -lt

我正在编写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”
,但这不起作用。有人能告诉我为什么吗


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

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

你到底有什么问题


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

如果您希望在“如果”中使用waitLoop,您可能希望将“退出”更改为“返回”,以便脚本的其余部分可以处理错误情况(在脚本终止之前,甚至没有向用户发送关于失败原因的消息)

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

因此,您可以决定不需要shell magic,并设置test='grep-sq^sometext$somefilename',或者让shell显式地处理引用,例如:

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

您可以通过以下方式设置测试变量:

test=$(grep -sq "^sometext$" $somefilename)
您的
grep
不起作用的原因是引号很难在参数中传递。您需要使用
eval

if ! eval $test

尝试使用文件修改时间来检测何时写入而不打开文件。差不多

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中;但是,我还包括了一个文件大小检查。如果文件自上次检查以来大小增加,我将重置超时计时器。我正在编写的文件可能只有几gig,因此跨NFS编写它们需要一些时间


对于您的特定情况,这可能有点过头了,但我也让我的编写过程在完成编写后计算文件的散列。我使用了md5,但类似crc32的东西也可以使用。此散列从写入程序广播到(多个)读取器,读取器等待,直到a)文件大小停止增加,b)文件的(新计算的)散列与写入程序发送的散列匹配。

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

我所做的是在cron中设置它(尽管带有sleep的循环也可以),以便对文件进行cksum。当旧的cksum与当前的cksum匹配时(文件在确定的时间内没有更改),我们知道写入已完成,并传输文件

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


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

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 美元/瓦廷戈 [-e/etc/passwd]满意 grep^hasfoo/tmp/baz满意
可惜打字脚本没有实时观看有趣。

好的……这有点古怪

如果您可以控制该文件:您可以在此处创建“命名管道”。 因此(取决于编写程序的工作方式),您可以以同步方式监视文件

简单地说:

创建命名管道:

mkfifo file.txt
设置同步接收器:

while :
do
    process.sh < file.txt
end
“process.sh”是您的逻辑所在:这将阻塞,直到发送方写入其输出。理论上,writer程序不需要修改

警告:如果由于某种原因接收器未运行,您可能会阻止发送器

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

或者要避免同步,请尝试“lsof”


假设您只想在没有其他内容写入文件时(即,写入过程已完成)读取文件,您可以检查是否没有其他内容具有文件句柄?

我无法更改装载参数。我的脚本应该足够健壮,能够处理延迟。问题不在于grep本身,而是grep测试没有像我预期的那样得到评估。好吧,如果你不能改变你的环境,那就是一个问题。无论如何,在更改文件后尝试了“同步”?根据NFS文档,它应该这样做(只要它调用sysnc.2)。是否可以简单地在文件上运行tail,只检查tail的输出(即最后几行)?假设tail比脚本在文件更改时进行更聪明的检查(很可能tail以更合适的方式进行检查),我认为正确的方法将取决于“某些东西”和“应用程序”是什么。你能提供更多关于你试图解决的真正问题的细节吗?书面问题有点模糊;可能有完全不同的方法你没有想到。谢谢。如何广播md5散列?对于散列,等待循环本身就足够了吗?我们使用的是现成的网络消息传递协议(POE for P
echo "Hello There" > file.txt