我有一个 Bourne shell 脚本,它接受单个参数(文本文件大小通常为几 KB)。本质上,该脚本是一个包装器,用于scp
将该文本文件复制到远程服务器。脚本不会尝试scp
原始文件,但它会创建该文件的硬链接或副本:
#!/bin/sh
TRANSFER_FILE=/var/tmp/acc_transfer_link_$$
INPUT_FILE=$1
# Linking is always a better option, so try it first
ln $INPUT_FILE $TRANSFER_FILE 2>/dev/null
RC=$?
if [ $RC -ne 0 ]; then
cp $INPUT_FILE $TRANSFER_FILE
fi
该脚本的作者留下了评论:
链接始终是更好的选择,因此请先尝试一下
为什么会这样?是因为复制比创建硬链接花费的时间稍长吗?还有其他原因吗?
答案1
这取决于。
建立链接比复制数据更快,但生成的文件将具有与原始文件相同的内容,并且任何修改都会显示在两者中。这是否是优势,取决于制作链接/副本的原因。此外,硬链接仅在同一文件系统内工作,因此如果/var
或/var/tmp
是与源目录分开的挂载,则链接将不起作用。
但我想知道这里的用例是什么。如果脚本的目的是复制名为 in 的文件$1
,为什么要复制到$TRANSFER_FILE
而不是scp
直接在原始文件上运行?仅当有理由假设源文件在复制过程中可能被修改时才需要首先进行本地复制,并且不应以不一致的状态复制文件。但这里的方法有一些问题:
1)制作本地副本也cp
有同样的问题:在本地复制过程中源也可能被修改。 2)链接ln
是即时的,但由于硬链接指向与原始数据相同的数据,因此在链接之前打开文件的任何进程仍然可以继续修改数据。
人们要么需要确保文件在链接后不会在另一个进程中打开(通过使用类似的方法lsof
),要么制作副本并通过某种特定于应用程序的方法验证数据的一致性。由于两者都不是那么简单,因此进行原子修改的通常方法是编写文件的新副本,然后将其重命名为旧的副本。这样,在重命名之前打开该文件的任何进程都将获取旧版本,而在重命名之后打开该文件的任何进程都将获取新版本。但两者都不会看到不完整的副本。然而,这必须在修改文件时完成,而不是在读取文件的程序中完成。