移动文件,但仅当文件已关闭时

移动文件,但仅当文件已关闭时

我想在外部进程关闭后立即移动由外部进程创建的大文件。

这个测试命令正确吗?

if lsof "/file/name"
then
        # file is open, don't touch it!
else
        if [ 1 -eq $? ]
        then
                # file is closed
                mv /file/name /other/file/name
        else
                # lsof failed for some other reason
        fi
fi

编辑:该文件代表一个数据集,我必须等到它完成才能移动它,以便另一个程序可以对其进行操作。这就是为什么我需要知道外部进程是否已完成该文件。

答案1

lsof手册页

如果检测到任何错误,包括未能找到要求列出的命令名、文件名、Internet 地址或文件、登录名、NFS 文件、PID、PGID 或 UID,Lsof 将返回一 (1)。如果指定了 -V 选项,lsof 将指示它未能列出的搜索项。

所以这表明你的lsof failed for some other reason条款永远不会被执行。

您是否尝试过在外部进程仍打开文件的情况下移动文件?如果目标目录位于同一文件系统上,那么这样做应该没有问题,除非您需要从第三个进程的原始路径下访问它,因为底层 inode 将保持不变。否则我想mv无论如何都会失败。

如果您确实需要等到外部进程完成文件处理,那么最好使用阻塞命令,而不是重复轮询。在 Linux 上,您可以使用inotifywait它。例如:

 inotifywait -e close_write /path/to/file

如果您必须使用lsof(也许是为了便携性),您可以尝试以下操作:

until err_str=$(lsof /path/to/file 2>&1 >/dev/null); do
  if [ -n "$err_str" ]; then
    # lsof printed an error string, file may or may not be open
    echo "lsof: $err_str" >&2

    # tricky to decide what to do here, you may want to retry a number of times,
    # but for this example just break
    break
  fi

  # lsof returned 1 but didn't print an error string, assume the file is open
  sleep 1
done

if [ -z "$err_str" ]; then
  # file has been closed, move it
  mv /path/to/file /destination/path
fi

更新

正如所指出的@约翰·WHS史密斯下面,最安全的设计总是使用lsof如上所述的循环,因为可能有多个进程打开文件进行写入(一个示例情况可能是一个写得不好的索引守护进程,它在打开文件时带有读/写标志实际上应该是只读的)。inotifywait不过仍然可以用来代替 sleep,只需将 sleep 行替换为inotifywait -e close /path/to/file.

答案2

作为一种替代方法,这是一个完美的案例管道- 第二个进程将在第一个进程的输出可用时立即对其进行处理,而不是等待整个进程完成:

process1 input_file.dat | process2 > output_file.dat

优点:

  • 一般来说要快得多:
    • 不必对磁盘进行写入和读取(如果您使用 ramdisk,则可以避免这种情况)。
    • 应该更充分地利用机器资源。
  • 完成后无需删除中间文件。
  • 不需要像OP中那样复杂的锁定。

如果您无法直接创建管道,但您有GNU 核心工具你可以使用这个:

tail -F -n +0 input_file.dat | process2 > output_file.dat

这将从头开始读取输入文件,无论多远第一个过程是通过写入文件(即使它尚未开始或已经完成)。

相关内容