6

我正在编写 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"了,但这没有用。有人能告诉我为什么吗?

是否有其他不那么冗长的选项来执行这样的测试?

4

8 回答 8

1

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

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

你不工作的原因grep是引号很难传递参数。您需要使用eval

if ! eval $test
于 2009-07-03T10:31:39.670 回答
0

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

你的具体问题是什么?

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

于 2009-07-03T06:53:20.907 回答
0

如果您想在“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
   ...
于 2009-07-03T07:29:50.370 回答
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

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

于 2009-07-14T23:47:45.767 回答
0

我只是遇到了完全相同的问题。我对包含在 OP 中的超时等待使用了类似的方法;但是,我还包括了文件大小检查。如果自上次检查文件后文件大小增加,我会重置超时计时器。我正在编写的文件可能是几个 gig,因此它们需要一段时间才能跨 NFS 写入。

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

于 2009-12-18T19:11:16.973 回答
0

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

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

为了更加安全,我们在进行备份之前从不覆盖本地文件,并且仅当远程文件连续有两个匹配的 cksum 并且该 cksum 与本地文件不匹配时才传输。

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

于 2009-12-21T21:49:24.223 回答
0

外壳正在将您的谓词拆分为单词。如下$@代码所示:

#! /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 ] 满意
grep ^hasfoo /tmp/baz 满意

太糟糕了,打字稿不像实时观看那样有趣。

于 2009-12-22T14:47:50.380 回答
-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

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

于 2009-12-22T18:13:44.033 回答