代码之家  ›  专栏  ›  技术社区  ›  andreas buykx

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

  •  6
  • andreas buykx  · 技术社区  · 15 年前

    我写的是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 回复  |  直到 15 年前
        1
  •  1
  •   Dennis Williamson    15 年前

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

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

    你的原因 grep 不起作用的是,引号真的很难传递参数。你需要使用 eval :

    if ! eval $test
    
        2
  •  0
  •   TheBonsai    15 年前

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

    你到底有什么问题?

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

        3
  •  0
  •   Anthony Towns    15 年前

    如果您希望在“if”中使用waitloop,那么您可能希望将“exit”更改为“return”,这样脚本的其余部分就可以处理错误情况(甚至不会向用户发送消息,告诉用户在脚本死之前发生的失败)。

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

    因此,您可以决定不需要shell魔力,并设置test='grep-sq^someText$someFileName',或者让shell使用如下内容来显式处理引用:

    if /bin/sh -c "$test"
    then
       ...
    
        4
  •  0
  •   Steve K    15 年前

    尝试使用文件修改时间来检测何时写入而不打开文件。类似的东西

    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
    

    但是,如果多个进程试图同时访问该文件,这将不起作用。

        5
  •  0
  •   Andrew Barnett    15 年前

    我也有同样的问题。我使用了类似于超时等待的方法,您可以在您的操作中包括超时等待;但是,我还包括了一个文件大小检查。如果文件的大小自上次检查以来有所增加,我会重置超时计时器。我正在写的文件可能需要几gig,因此它们需要一些时间在NFS上进行写操作。

    对于您的特定情况,这可能是多余的,但我也让我的写入过程在完成写入后计算文件的散列值。我用的是MD5,但类似CRC32的东西也可以。这个散列是从编写器广播到(多个)读卡器的,读卡器等待,直到a)文件大小停止增加,b)文件的(新计算的)散列与编写器发送的散列相匹配。

        6
  •  0
  •   Grant Johnson    15 年前

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

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

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

    如果您需要代码示例,我相信我可以找到它们。

        7
  •  0
  •   Greg Bacon    15 年前

    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
    

    可惜的是,打字稿不像实时观看那么有趣。

        8
  •  -1
  •   monojohnny    15 年前

    好吧…这有点怪…

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

    最简单的是:

    创建命名管道:

    mkfifo file.txt
    

    设置同步接收器:

    while :
    do
        process.sh < file.txt
    end
    

    创建测试发件人:

    echo "Hello There" > file.txt
    

    “process.sh”是您的逻辑所在:在发送方写入其输出之前,这将被阻塞。理论上,编写程序不需要修改……

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

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

    还是要避免同步,请尝试“lsof”?

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

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