读取 Bash 中可能已删除的文件
Read potentially deleted file in Bash
在下面的代码中:
#!/bin/bash
if [ ! -f "$file" ]
then
stat --printf="%s" "$file"
cat "$file"
else
echo -1
fi
$file
是可以随时删除的二进制文件的名称。
我最担心的是文件可能会在 [ ! -f "$file" ]
之后但在 cat "$file"
执行之前被删除,结果会不正确。
但是我也想知道如果在执行cat "$file"
的过程中删除了文件会怎么样。是否会输出 fully/partially,如果 $file
在驱动器上被覆盖,是否有读取不相关字符的风险? man cat
没有解释。 编辑:
我如何保证输出是?
- 文件大小,后跟换行符和文件内容
-1
注意:文件的大小可能高达 5MiB,复制它会太慢。
编辑:文件是用 ffmpeg ... -window_size 5 -extra_window_size 0 -min_seg_duration 2000000 -f dash ...
创建的,在我的例子中,在特定目录中同时保留最多 5 个文件,它们从不重复使用相同的名称,并且它们遵循这个循环(完全由 ffmpeg 控制) : 1) 使用 .tmp 扩展名创建 2) 不使用 .tmp 重命名 3) (至少 10 秒后)删除
你不能在 bash 中保证(输出是整个文件以其大小为前缀,否则是 -1),因为,正如你提到的,两个命令之间可能会发生某些事情(和进程)。
顺便说一句,该文件可能会被其他进程截断(执行 ftruncate(2)...),因此您无法保证获得内容的 "totality"。
您可以考虑在 shell 脚本中使用 advisory locking (e.g. with flock(2) or lockf(3) ...; consider also flock(1)),只有当更改该文件的所有程序都同意该锁定时,它才能正常工作(所以你需要采用整个系统约定).
也许您想使用一些 RDBMS server providing ACID-身份保证。
But I also wonder what will happen if the file is deleted during the execution of cat "$file". Will it be fully/partially outputted, is there a risk to read unrelated characters if $file get overwritten on the drive?
没有。如果你有一些进程 运行 cat
(可能是 /bin/cat
程序,请参阅 $file 上的 cat(1)) that process keeps an opened file descriptor 。所以只要数据不会被释放(或重写)因为某些打开的文件描述符引用了该文件。
也许你可以编写一个简单的 C 程序(它在 相同的 进程中运行,与某些 shell 脚本中的几个命令相反)打开文件,在打开的文件描述符上使用 fstat(2)(如果使用 stdio 函数,可能通过 fileno(3)),并循环复制其内容.这并不能保护您免受其他进程在该副本期间所做的敌对 ftruncate(2)。
如果您不关心截断或覆盖,只关心过早的 rm
(或 unlink(2)),您可以使用临时的附加硬盘 link。也许就这么简单:
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))...
或者使用 subshell 技巧,在 cat
之前的那个 subshell stat --printf="%s" -L /dev/stdin
解决方法是不检查文件是否存在;只需尝试打开它,并处理打开文件时出现的任何错误。如果可行的话,这是在子 shell 中最容易做到的:
(
exec < foo || exit 1
cat
)
如果你真的需要使用stat
,那就有点棘手了。如果没有给出参数,BSD stat
将处理附加到标准输入的文件,但是 GNU stat
(据我所知)必须给出一个现有的文件名。
在下面的代码中:
#!/bin/bash
if [ ! -f "$file" ]
then
stat --printf="%s" "$file"
cat "$file"
else
echo -1
fi
$file
是可以随时删除的二进制文件的名称。
我最担心的是文件可能会在 [ ! -f "$file" ]
之后但在 cat "$file"
执行之前被删除,结果会不正确。
但是我也想知道如果在执行 编辑:cat "$file"
的过程中删除了文件会怎么样。是否会输出 fully/partially,如果 $file
在驱动器上被覆盖,是否有读取不相关字符的风险? man cat
没有解释。
我如何保证输出是?
- 文件大小,后跟换行符和文件内容
-1
注意:文件的大小可能高达 5MiB,复制它会太慢。
编辑:文件是用 ffmpeg ... -window_size 5 -extra_window_size 0 -min_seg_duration 2000000 -f dash ...
创建的,在我的例子中,在特定目录中同时保留最多 5 个文件,它们从不重复使用相同的名称,并且它们遵循这个循环(完全由 ffmpeg 控制) : 1) 使用 .tmp 扩展名创建 2) 不使用 .tmp 重命名 3) (至少 10 秒后)删除
你不能在 bash 中保证(输出是整个文件以其大小为前缀,否则是 -1),因为,正如你提到的,两个命令之间可能会发生某些事情(和进程)。
顺便说一句,该文件可能会被其他进程截断(执行 ftruncate(2)...),因此您无法保证获得内容的 "totality"。
您可以考虑在 shell 脚本中使用 advisory locking (e.g. with flock(2) or lockf(3) ...; consider also flock(1)),只有当更改该文件的所有程序都同意该锁定时,它才能正常工作(所以你需要采用整个系统约定).
也许您想使用一些 RDBMS server providing ACID-身份保证。
But I also wonder what will happen if the file is deleted during the execution of cat "$file". Will it be fully/partially outputted, is there a risk to read unrelated characters if $file get overwritten on the drive?
没有。如果你有一些进程 运行 cat
(可能是 /bin/cat
程序,请参阅 $file 上的 cat(1)) that process keeps an opened file descriptor 。所以只要数据不会被释放(或重写)因为某些打开的文件描述符引用了该文件。
也许你可以编写一个简单的 C 程序(它在 相同的 进程中运行,与某些 shell 脚本中的几个命令相反)打开文件,在打开的文件描述符上使用 fstat(2)(如果使用 stdio 函数,可能通过 fileno(3)),并循环复制其内容.这并不能保护您免受其他进程在该副本期间所做的敌对 ftruncate(2)。
如果您不关心截断或覆盖,只关心过早的 rm
(或 unlink(2)),您可以使用临时的附加硬盘 link。也许就这么简单:
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))...
或者使用 cat
stat --printf="%s" -L /dev/stdin
解决方法是不检查文件是否存在;只需尝试打开它,并处理打开文件时出现的任何错误。如果可行的话,这是在子 shell 中最容易做到的:
(
exec < foo || exit 1
cat
)
如果你真的需要使用stat
,那就有点棘手了。如果没有给出参数,BSD stat
将处理附加到标准输入的文件,但是 GNU stat
(据我所知)必须给出一个现有的文件名。