确定文件是否正在写入中?


25

我需要部署一个自动过程(通过1分钟的cron脚本),该过程在特定目录中查找tar文件。如果找到tar文件,则将其解压缩到适当的位置,然后删除该tar文件。

tar文件会通过SSH自动从另一台服务器复制到此服务器。在某些情况下,tar文件非常大,其中包含很多文件。

我期望遇到的问题:如果将tar文件复制到服务器上花费的时间超过1分钟,并且cron脚本每分钟运行一次,它将看到.tar.gz文件并尝试执行解压缩它,即使tar文件仍在写入过程中。

有什么方法(通过bash命令)测试当前是否正在写入文件,或者仅是部分文件等?

我想到的一种替代方法是,将文件复制为其他文件扩展名(如.tar.gz.part),然后.tar.gz在传输完成后重命名为。但是我认为我会尝试找出是否只有一种方法可以首先在命令行上确定文件是否完整...有任何线索吗?


2
文件传输的准确程度如何?例如,rsync在传输过程中使用临时文件名(默认情况下),并且只有文件完全传输后才将其重命名为实际文件名。
Piskvor 2014年

Answers:


12

您处在正确的轨道上,重命名文件是一个原子操作,因此在上传后执行重命名是简单,优雅且不易出错的。我可以想到的另一种方法是用来lsof | grep filename.tar.gz检查文件是否正在被另一个进程访问。


7
lsof filename.tar.gz比效率更高,更准确lsof | grep filename.tar.gz
Rich

顺便说一句,它应该是文件名的绝对路径
DennisLi

14

最好的选择是用来lsof确定文件是否已通过任何进程打开:

#  lsof -f -- /var/log/syslog
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF  NODE NAME
rsyslogd 1520 syslog    1w   REG  252,2    72692 16719 /var/log/syslog

您无法轻易确定它是否正在写入过程中,但是如果正在写入中,则必须将其打开。


编辑:让我们在这里解决实际问题,而不是尝试实施建议的解决方案!

使用rsync传输文件:

  rsync -e ssh remote:big.tar.gz .

这样,文件将不会被复制到现有文件的顶部,而会被复制到一个临时文件(.big.tar.gz.XXXXXX)中,直到传输完成,然后将其移动到位。


6

有点老了,但是大多数答案完全没有回答问题的重点:

但是我想我会尝试找出是否只有一种方法可以首先在命令行上确定文件是否完整...

一般来说,没有。您只是没有足够的信息来确定这一点。

因为确定文件已关闭与确定文件是否完整不同。例如,如果在传输过程中丢失连接,则文件将“关闭”。

只有@Alex的答案才正确。甚至他也因使用lsof某种东西而失败。

要确定文件是否已完全存储,成功传输需要更多数据。如:

我想到的一种替代方法是,将文件复制为其他文件扩展名(如.tar.gz.part),然后.tar.gz在传输完成后重命名为。

这是传达文件已被完全成功传输的完美方法。您也可以将文件从一个目录移动到另一目录,只要您位于同一文件系统中即可。或者让发送方发送一个空filename.done文件来表示完成。

但是所有方法都必须以某种方式依赖发送方,以信号通知传输已成功完成。因为只有发件人才具有该信息。

某些文件格式(例如PDF)中包含数据,可让您确定文件是否完整。但是您必须打开并阅读几乎整个文件才能找到答案。

lsof只会告诉您该文件不再打开-不会告诉您为什么它不再打开。它也不会告诉您该文件应该多大。


1
我不能对此表示足够的赞同。很好的解决了XY问题。
Beefster

5

最好的方法是使用Incron(“ intify cron system”)。它允许您在目录上设置一个inotify监视,该监视随后将通知您文件操作。在这种情况下,您应该在目录中观察close_write。一旦写入后关闭文件,这将允许您运行命令。


2

看起来lsof可以检测文件在哪种模式下打开:

lsof -f -- a_file
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
cat     52391 bob    1w   REG    1,2       15 19545007 a_file

看到哪里写着1w?这意味着文件描述符号为1,模式为w或写。


打开文件以供阅读时,该FD字段会3r为我显示。
Sopalajo de Arrierez,

0

使用inotifywait可以实现您所追求的目标-它具有等待文件写入完成然后再执行命令的功能。

下面的操作将连续监视文件夹中的新文件,并在写入文件完成后在循环中执行命令。

WATCH_DIR=/directory/to/monitor
DEST_DIR=/x/y/z

/usr/bin/inotifywait --recursive --monitor --quiet -e moved_to -e close_write --format '%w%f' "$WATCH_DIR" | while read -r INPUT_FILE; do

mv "$0" "$DEST_DIR"

done

有关更多配置选项,请参见https://linux.die.net/man/1/inotifywatch

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.