假设我有一个登陆文件夹/opt/landing
,外部系统全天都会在其中放置多个文件。有些文件很小,有些文件很大。我们想要编写一段脚本来将已从登陆文件夹完全复制的文件复制到可用文件夹/opt/available
。我们不想复制正在传输的文件。
我们如何使用 UNIX 脚本代码来实现这一点。
答案1
和zsh
:
#! /bin/zsh -
files=( /opt/landing/**/*(D.:P) )
typeset -U inuse=( /proc/<->/fd/<->(D-.:P) )
cp ${files:|inuse} /opt/available/
将复制其中我们当前在任何进程文件描述符上都没有看到的常规文件(我们不是在寻找在进程地址空间中映射的文件,但我不希望这会是文件的方式)正在那里着陆)。
在这里,我们不检查文件是否以读模式或写模式打开。这样做会更加复杂,因为我们还需要查看文件/proc/*/fdinfo/*
或解析其输出,lsof
这并不简单。
请注意,您需要超级用户权限才能查找不属于您的进程的文件描述符。
通过 NFS 或其他内核网络文件系统上传的文件不会显示在$inuse
列表中。请注意,文件可能会在每次操作之间被重命名或重新打开,因此它仍然是一种脆弱的方法。如果您可以更新您的着陆系统自己在完成删除文件后移动文件,这会更可靠。
**/
glob 运算符代表任何级别的子目录。<->
任何十进制数字序列。(ND.:P)
/(N-.:P)
:全局限定符:D
:D
otglob: 还考虑隐藏文件.
: 仅有的常规的文件(不是其他类型的文件,如套接字、目录、符号链接...)-.
:相同,但检查符号链接解析后的类型:P
: likerealpath()
获取文件的规范(无符号链接)绝对路径。
typeset -U
:使数组元素U
独特(删除重复项)${files:|inuse}
:数组减法(未使用的文件)。- arg 列表太长如果文件太多,可以使用
zargs
或 使用 的zsh
内置函数cp
(通过 启用zmodload zsh/file
)来避免错误。
答案2
您可能想要添加进一步的限制,不仅文件不应被使用,而且它们已被成功复制。发送客户端可以最好地识别第二个标准。
这不是 UNIX/Linux 问题,而是算法问题。
满足这两个标准(即仅处理完全成功复制的文件)的常用方法是使用临时文件名后缀传输文件,并让发送者仅在成功完成时重命名文件。
客户
- 客户端发送
datafile.xml.tmp
- 成功转移后,客户端将重命名
datafile.xml.tmp
为datafile.xml
服务器
- 服务器查找不以以下结尾的文件
.tmp
- 成功匹配后,服务器以所需的任何方式处理文件
- 服务器查找以 结尾
.tmp
且未“使用”(超过一天未更改)的文件 - 成功匹配后,服务器删除此类临时文件
多年来我不得不使用的其他方法来处理无法正确编码的客户端发送系统,包括在文件本身中查找文件结束标记。例如,通过应用 XML 感知解析器(例如 ),可以轻松识别 XML 文件是否完整xmlstarlet
,并且 JPEG 文件可以容易识别convert
使用ImageMagick等工具。
最坏的情况是您必须猜测文件是否完整,因为它已经有一段时间没有更新了。不幸的是,您无法知道它是否已完全传输,或者网络错误是否中途截断了传输。