【问题标题】:Prevent files to be moved by another process in linux防止文件被linux中的另一个进程移动
【发布时间】:2013-10-22 14:31:17
【问题描述】:

我对 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,这对于实例不同。 问题是如何防止文件被不同的脚本移动?

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

【问题讨论】:

  • 为什么你有两个任务从同一个目录获取文件?要么任务做完全相同的事情,而不是 1 应该做的事情,要么任务做不同的事情,但它是一种随机的,一个人将处理任何给定的文件,这是没有意义的。
  • 这是某种负载平衡。在该 kan 之后处理文件的程序接受有限数量的文件。但是硬件允许运行两次..
  • 一个文件只能移动一次。如果第一次调用将文件移动到/tmp/task1,第二次将找不到它并且不会对其进行任何处理。因此,您将获得分布在/tmp/task1/tmp/task2 中的前 10 个文件,但每个文件只会出现一次。如果您需要 /tmp/task1 中的前 10 个和 /tmp/task2 中的后 10 个,则必须进行一些同步。
  • 让两个cron 工作相互避开有意义吗?就像如果你有数字文件名,一个只会触摸匹配 [0-4]* 的文件,而另一个只会触摸匹配通配符 [5-9]* 的文件?

标签: linux bash shell


【解决方案1】:

首先:重命名是原子的不可能将文件移动两次。其中一个移动将失败,因为该文件不再存在。如果脚本并行运行,则两者都列出相同的 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 扩展。

【讨论】:

    【解决方案2】:

    两个 cron 作业将同一个文件移动到同一个路径这一事实对你来说并不重要,除非你被其中一个错误所困扰(一个会成功,另一个会失败)。

    您可以使用以下命令忽略错误:

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

    【讨论】:

    • 否 - 两个 cron 任务的路径不同。例如,对于 task1,$srcdrl 将是 /tmp/task1,对于 task2,$srcdrl 将是 /tmp/task2。结果 - 一个文件将被处理两次。我试图避免这种情况。
    • 等一下——哪个“路径不同”和哪个“文件夹相同”?您在问题中陈述一件事,在您的评论中陈述另一件事。请解释一下。
    • @USeTi:文件一旦被移动,就不会再被移动,所以你不必在意。
    • @USeTi:如果路径不同,同一个路径怎么处理两次呢?我真的不明白。
    【解决方案3】:

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

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

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

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

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

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

    【讨论】:

    • 假设“targdir”是一个挂载的远程文件夹。并且实例在不同的服务器上运行。但现在我正在测试一台服务器和两个实例的设置。系统应该在不知道是否存在其他实例的情况下获取一些数据并对其进行处理。
    猜你喜欢
    • 1970-01-01
    • 2013-03-10
    • 2017-08-02
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    • 2011-04-13
    • 2017-12-02
    • 2011-09-04
    相关资源
    最近更新 更多