在Bash中读取可能已删除的文件

时间:2017-11-20 14:04:29

标签: bash sh race-condition cat

在以下代码中:

#!/bin/bash

if [ ! -f "$file" ]
then
    stat --printf="%s" "$file"
    cat "$file"
else
    echo -1
fi

$file是可以在任何时候删除的二进制文件的名称。

我最大的担心是文件可能会在[ ! -f "$file" ]之后但在执行cat "$file"之前被删除,结果会不正确。

但我也想知道如果在执行cat "$file"期间删除文件会发生什么。它是完全/部分输出的,如果在驱动器上覆盖$file,是否存在读取不相关字符的风险? man cat没有解释这一点。编辑:https://stackoverflow.com/a/2031100/4503330

我如何保证输出是?

  • 文件的大小,后跟新行和文件内容
  • -1

注意:文件的大小可能高达5MiB,并且复制它的速度太慢。

编辑:文件是用ffmpeg ... -window_size 5 -extra_window_size 0 -min_seg_duration 2000000 -f dash ...创建的,在我的情况下,在特定目录中最多可以保存5个文件,它们永远不会重复使用相同的名称,并且它们遵循此循环(完全由ffmpeg控制): 1)使用.tmp扩展名创建2)重命名时不使用.tmp 3)(至少10秒后)删除

2 个答案:

答案 0 :(得分:3)

你不能在bash中保证(输出是以其大小为前缀的整个文件,否则为-1),因为,正如你所提到的,两个命令(和进程)之间可能会发生一些事情。

BTW,该文件可能会被其他进程截断(执行ftruncate(2) ...),因此您无法保证获得内容的“整体”。

您可以考虑使用顾问 locking(例如,使用flock(2)lockf(3) ...;在shell脚本中也考虑flock(1)) ,只有在更改该文件的所有程序都同意该锁定时才能正常工作(因此您需要采用整个系统约定)。

也许你想使用一些RDBMS服务器提供ACID - idity保证。

  

但我也想知道如果在执行cat“$ file”期间删除文件会发生什么。它是完全/部分输出的,如果$ file被覆盖在驱动器上,是否存在读取无关字符的风险?

没有。如果某个进程正在运行cat(可能是/bin/cat程序,请参阅cat(1))该进程会在$ file上保持打开的file descriptor。因此,只要某些打开的文件描述符引用该文件,就不会释放(或重写)数据。

也许您可以编写一个简单的C程序(在相同的进程中运行,与某些shell脚本中的几个命令相反)打开文件,使用fstat(2)(也许通过fileno(3)如果你在打开的文件描述符上使用stdio函数,并循环复制其内容。这并不能保护您免受该副本中其他进程所做的敌意ftruncate(2)

如果您不关心截断或覆盖,只关注过早rm(或unlink(2))您可能会使用临时额外的硬链接。也许就像这样简单:

 newhardlink=".newhardlink$$"
 ln "$file" "$newhardlink"
 stat --printf="%s" "$newhardlink"
 cat "$newhardlink"
 rm "$newhardlink"

如果您害怕不同的文件系统,那么您可能会这样做

 mydir=$(dirname "$file")
 newhardlink="$mydir/.newhardlink$$"

代替newhardlink=".newhardlink$$",您可以使用trap技巧在所有情况下完成最终清理rm "$newhardlink"

还要注意inotify(7)(可能对你的情况有些过分)

更好的是,更改ffmpeg的启动方式,以便它使用一些临时文件(请参阅mktemp(1)mkstemp(3))....

或者在stat --printf="%s" -L /dev/stdin

之前使用chepner子shell技巧和子shell cat

答案 1 :(得分:2)

解决方法是不检查文件是否存在;只是尝试打开它,并处理打开文件中的任何错误。如果可行的话,这在子shell中最容易做到:

(
    exec < foo || exit 1
    cat
)

如果您确实需要使用stat,那就有点棘手了。如果没有给出参数,BSD stat将处理附加到标准输入的文件,但是GNU stat(据我所知)必须给出现有的文件名。