焦油的有趣用法......但是发生了什么?

时间:2008-11-25 01:10:00

标签: bash scripting tar

我在同事的Bash脚本中看到了以下有趣的tar用法:

`tar cf - * | (cd <dest> ; tar xf - )`

显然它的工作方式与rsync -av相似,但速度更快。问题出现了,怎么样?

-m


编辑:任何人都可以解释为什么这个解决方案应该优于以下内容吗?

cp -rfp * dest

前者更快吗?

12 个答案:

答案 0 :(得分:12)

它将存档写入标准输出,然后将其传送到子进程 - 由括号括起 - 更改为不同的目录并从标准输入读取/提取。这就是f参数后的破折号字符的含义。它基本上将当前目录的所有可见文件和子目录复制到另一个目录。

答案 1 :(得分:9)

关于复制目录层次结构的cp和tar之间的区别,可以进行一个简单的实验来显示差异:

alastair box:~/hack/cptest [1134]% mkdir src
alastair box:~/hack/cptest [1135]% cd src
alastair box:~/hack/cptest/src [1136]% touch foo
alastair box:~/hack/cptest/src [1137]% ln -s foo foo-s
alastair box:~/hack/cptest/src [1138]% ln foo foo-h
alastair box:~/hack/cptest/src [1139]% ls -a
total 0
-rw-r--r--  2 alastair alastair    0 Nov 25 14:59 foo
-rw-r--r--  2 alastair alastair    0 Nov 25 14:59 foo-h
lrwxrwxrwx  1 alastair alastair    3 Nov 25 14:59 foo-s -> foo
alastair box:~/hack/cptest/src [1142]% mkdir ../cpdest
alastair box:~/hack/cptest/src [1143]% cp -rfp * ../cpdest
alastair box:~/hack/cptest/src [1144]% mkdir ../tardest
alastair box:~/hack/cptest/src [1145]% tar cf - * | (cd ../tardest ; tar xf - )
alastair box:~/hack/cptest/src [1146]% cd ..
alastair box:~/hack/cptest [1147]% ls -l cpdest
total 0
-rw-r--r--  1 alastair alastair    0 Nov 25 14:59 foo
-rw-r--r--  1 alastair alastair    0 Nov 25 14:59 foo-h
lrwxrwxrwx  1 alastair alastair    3 Nov 25 15:00 foo-s -> foo
alastair box:~/hack/cptest [1148]% ls -l tardest
total 0
-rw-r--r--  2 alastair alastair    0 Nov 25 14:59 foo
-rw-r--r--  2 alastair alastair    0 Nov 25 14:59 foo-h
lrwxrwxrwx  1 alastair alastair    3 Nov 25 15:00 foo-s -> foo

区别在于硬链接文件。请注意如何使用cptar单独复制硬链接文件。为了使差异更明显,请查看每个的inode:

alastair box:~/hack/cptest [1149]% ls -i cpdest
24690722 foo  24690723 foo-h  24690724 foo-s
alastair box:~/hack/cptest [1150]% ls -i tardest
24690801 foo  24690801 foo-h  24690802 foo-s

可能还有其他理由喜欢tar,但这是一个很大的原因,至少如果你有广泛的硬链接文件。

答案 2 :(得分:5)

对于包含25,000个空文件的目录:

$ time { tar -cf - * | (cd ../bar; tar -xf - ); }
real    0m4.209s
user    0m0.724s
sys 0m3.380s

$ time { cp * ../baz/; }
real    0m18.727s
user    0m0.644s
sys 0m7.127s

对于包含4个文件1073741824字节(1GB)的目录

$ time { tar -cf - * | (cd ../bar; tar -xf - ); }
real    3m44.007s
user    0m3.390s
sys 0m25.644s

$ time { cp * ../baz/; }
real    3m11.197s
user    0m0.023s
sys 0m9.576s

我猜这种现象是高度依赖于文件系统的。如果我是对的,你会发现专门研究众多小文件的文件系统(例如reiserfs 3.6)与处理大文件的文件系统之间存在巨大差异。

(我在HFS +上运行了上述测试。)

答案 3 :(得分:2)

这是管道的独特用法。基本上,第一个tar通常直接写入文件,但是它会写入stdout( - ),然后重定向到另一个tar,它接受stdin而不是文件。基本上这与tar文件相关并稍后解压,除非中间没有文件。

答案 4 :(得分:2)

PowerTools书的副本为:

tar cf - * | (cd <dest> && tar xvBf - )

'&amp;&amp;'是一个检查前一个命令的返回码的条件。也就是说,如果“cd”失败,则不会执行“tar xf - ”。我总是抛出-v(详细)和-B(重新输入)。

我一直使用tar。它对于复制到远程系统特别有用,例如:

tar cvf - 。 | ssh someone @ somemachine'(cd some some&amp;&amp; tar xBf - )'

答案 5 :(得分:1)

tar cf - * | (cd <dest> ; tar xf - )

将所有当前目录的隐藏文件/目录都tar到stdout,然后将其汇总到新的子shell中.stdin。该shell首先将当前工作目录更改为<dest>,然后将其解压缩到该目录。

答案 6 :(得分:1)

某些旧版本的cp没有-f / -p(和类似的)选项来保留权限,所以这个tar技巧完成了这项工作。

答案 7 :(得分:1)

我相信tar会对深度嵌套的目录进行Windows风格的“合并”操作,而cp会覆盖子目录。

例如,如果你有布局:

dir/subdir/file1

并将其复制到包含以下内容的目的地:

dir/subdir/file2

然后通过复制,您将留下:

dir/subdir/file1

但是使用tar命令,您的目标将包含:

dir/subdir/file1
dir/subdir/file2

答案 8 :(得分:0)

tar cf - *

这使用tar将*发送到stdout

|

这显然将stdout重定向到......

(cd <dest> ; tar xf - )

这会将PWD更改为适当的位置,然后从stdin中提取

我不知道为什么这会比rsync更快,因为不涉及压缩。

答案 9 :(得分:0)

tar解决方案将保留符号链接,而cp只会复制并销毁链接。

tar一直是标准的Unix实用程序,比rsync长很多。当目录层次结构需要复制到另一个位置(甚至是另一台计算机)时,您更有可能找到它。 rsync现在可能更容易使用,但速度较慢,因为它比较源和目标并同步它们。 tar只是向一个方向复制。

答案 10 :(得分:0)

如果您有GNU cp(所有基于Linux的系统都会这样),cp --archive即可在硬链接文件上运行,也不需要tar。

答案 11 :(得分:0)

碰巧,一位同事在我们的一个脚本中编写了一个几乎相同的命令。在我花了一些时间困惑之后,我问他为什么使用它而不是cp。我记得他的回答是,当从一个文件系统复制到另一个文件系统时,cp速度很慢。

这是否属实是需要比我在这个问题上花费更多的测试,但它有一定的意义。第一个tar进程尽可能快地从源设备读取,只等待该设备读取。同时,第二个tar进程从其输入管道读取并尽快写入。它可能必须等待输入,但如果目标设备上的写入比源设备上的读取慢,则它只会在目标设备上等待。单个cp命令必须在源设备和目标设备上等待。

另一方面,现代操作系统在预缓存IO操作方面做得非常好。完全有可能cp将花费大部分时间等待写入并从内存而不是设备本身进行读取。似乎需要使用两个tar命令而不是更简单的cp命令来选择真正可靠的数据。