0

我对 bash 脚本有疑问。我有两个 cron 任务,它们从同一文件夹中获取一些文件以进行进一步处理。

ls -1h "targdir/*.json" | head -n ${LIMIT} > ${TMP_LIST_FILE}
while read REMOTE_FILE 
do
    mv $REMOTE_FILE $SCRDRL
done < "${TMP_LIST_FILE}"
rm -f "${TMP_LIST_FILE}"

但是随后两个脚本实例同时运行相同的文件,因为它们被移动到 $SRCDRL 中,这对于实例不同。问题是如何防止文件被不同的脚本移动?

UPD: 也许我不太清楚......我有文件夹“targdir”,我在其中存储 json 文件。我有两个 cron 任务,它们从该目录中获取一些文件进行处理。例如,在 targdir 中存在 25 个文件,第一个 cron 任务应该获取前 10 个文件并将它们移动到 /tmp/task1,第二个 cron 任务应该获取接下来的 10 个文件并将它们移动到 /tmp/task2 等但是现在前 10 个文件移动到 / tmp/task1 和 /tmp/task2。

4

3 回答 3

1

两个 cron 作业将同一个文件移动到同一个路径这一事实对您来说并不重要,除非您对从其中一个作业中得到的错误感到不安(一个会成功,另一个会失败)。

您可以使用以下方法忽略错误:

    ...
    mv $REMOTE_FILE $SCRDRL 2>/dev/null
    ...
于 2013-10-21T14:30:00.470 回答
1

由于您的脚本应该从列表中移动特定数量的文件,因此两个实例最多只能移动两倍的文件。除非它们甚至相互干扰,否则移动文件的数量可能会更少。

无论如何,这可能是一个糟糕的开始。如果你有办法阻止两个脚本同时运行,你应该这样做。

但是,如果您无法阻止两个脚本实例同时运行,您至少应该强化脚本以防出错:

mv "$REMOTE_FILE" "$SCRDRL" 2>/dev/null

否则,您的脚本将产生错误输出(在 cron 脚本中不是好主意)。

此外,我希望您${TMP_LIST_FILE}在两种情况下都不相同(您可以使用$$它来避免这种情况);否则他们甚至会覆盖这个临时文件,在最坏的情况下会导致包含您不想移动的路径的损坏文件。

于 2013-10-21T14:35:01.993 回答
1

首先也是最重要的:rename 是 atomic一个文件不可能被移动两次。其中一个移动将失败,因为该文件不再存在。如果脚本并行运行,则两个文件都列出相同的 10 个文件,而不是前 10 个文件移动到/tmp/task1和接下来的 10个文件,/tmp/task2您可能会得到 4 个移动到/tmp/task1和 6 个到/tmp/task2. 或者可能是 5 和 5 或 9 和 1 或任何其他组合。但是每个文件只会以一个任务结束

所以没有什么是不正确的;每个文件仍然只处理一次。但这将是低效的,因为您一次可以处理 10 个文件,但您只处理 5 个。如果您想确保在有足够文件可用的情况下始终处理 10 个,则必须进行一些同步。基本上有两种选择:

  1. 在列表+副本周围加锁。flock使用(1)和锁定文件最容易做到这一点。也有两种方法可以调用它:

    1. 通过flock调用整个复制操作:

      flock targdir -c copy-script
      

      这要求您将应排除的部分制作为单独的脚本。

    2. 通过文件描述符锁定。在复制之前,做

      exec 3>targdir/.lock
      flock 3
      

      然后做

      flock -u 3
      

      这使您可以仅锁定部分脚本。这在 Cygwin 中不起作用(但您可能不需要它)。

  2. 一个一个地移动文件,直到你有足够的空间。

    ls -1h targdir/*.json > ${TMP_LIST_FILE}
    #                   ^^^ do NOT limit here
    COUNT=0
    while read REMOTE_FILE 
    do
        if mv $REMOTE_FILE $SCRDRL 2>/dev/null; then
            COUNT=$(($COUNT + 1))
        fi
        if [ "$COUNT" -ge "$LIMIT" ]; then
            break
        fi
    done < "${TMP_LIST_FILE}"
    rm -f "${TMP_LIST_FILE}"
    

    有时会失败,在这种mv情况下,您不计算文件并尝试移动下一个文件,假设mv失败是因为文件同时被另一个脚本移动了。每个脚本最多$LIMIT复制文件,但它可能是相当随机的选择。

附带说明一下,如果您绝对不需要在while循环中设置环境变量,则无需临时文件即可。简单地:

ls -1h targdir/*.json | while read REMOTE_FILE
do
    ...
done

您不能将变量传播到此类循环之外,因为作为管道的一部分,它在子shell 中运行。

如果您确实需要设置环境变量并且可以专门使用 bash (我通常尝试坚持/bin/sh),您也可以编写

while read REMOTE_FILE
do
    ...
done <(ls -1h targdir/*.json)

在这种情况下,循环在当前 shell 中运行,但这种重定向是 bash 扩展。

于 2013-10-22T06:16:53.467 回答