将一个文件重定向到另一个文件是UUOC(对cat的无用使用)吗?


36

如果我想使内容file2与的内容匹配file1,我显然可以运行cp file1 file2

但是,如果我想保留一切有关file2 除了内容所有者,权限,扩展属性,ACL的,硬链接,等等等等,那么我就不会想运行cp。*在这种情况下,我只是想在扑通内容file1file2

似乎可以做到以下几点:

< file1 > file2

但这是行不通的。 file2被截断为空且未写入。然而,

cat < file1 > file2

确实有效。

令我惊讶的是第一个版本不起作用。

第二个版本是UUOC吗?有没有一种方法,仅通过使用重定向就可以在不调用命令的情况下做到这一点?

注意:我知道UUOC不仅仅是真正的反模式,更是一个学问点。

* tniles09发现的,实际上在这种情况下cp 起作用。


3
无论< file1 > file2你想要做什么是壳依赖。
Michael Homer

13
好吧,这<... 的无用使用
jwodder 2015年

2
什么是反模式
mikeserv

6
@jwodder-不是这样。特别是在谈论副本时。考虑file1不存在或不可读时发生的情况,并在打开输出< 之前 >将其打开,然后考虑允许cat尝试打开它时发生的情况。
mikeserv

3
@JonathanLeffler在zsh中,带有重定向的空命令将被调用cat(默认情况下),实际上运行第二个命令。请参阅下面的StéphaneChazelas的答案,以获取更多的信息,而不是在评论中。
Michael Homer

Answers:


58

cat < file1 > file2不是UUOC。经典,<并且>做重定向对应于在系统级文件描述符重复。文件描述符复制本身不会做任何事情(嗯,>重定向以O_TRUNC开头,所以准确地说,输出重定向会截断输出文件)。不要让< >符号使您感到困惑。重定向不会移动数据,而是将文件描述符分配给其他文件描述符。

在这种情况下,您打开file1该文件描述符并将其分配给文件描述符0<file1== 0<file1),file2然后将该文件描述符分配给文件描述符1>file2== 1>file2)。

现在您已经有了两个文件描述符,您需要一个在两个文件之间交换数据的过程,这就是cat目的。


11
也许只有我一个人,但是我最喜欢的答案是您对“铲子”的使用。:)非常清楚,谢谢。
通配符

1
@Wildcard我更喜欢“ pump”而不是“ shovel”,但还是个好词。+1
Mehrdad

为什么铲好词?
bubakazouba 2015年

1
当一个缓冲区一个缓冲区地复制数据时,一个人将一堆泥土铲起,一次又一次装满。这是一个很好的类比。
bsd 2015年

1
在第一句话中,您说文件描述符正在重复。它们是重复的还是重新分配的(如第二段以及该功能的行为所示)?
格雷格·贝尔

17

并非如此,因为正如其他人指出的那样,所讨论的行为是依赖于外壳的。正如您(OP)所指出的那样,这有点古怪,甚至幽默吗?,是个话题。

但是,在GNU系统上,您的初始前提是可以使用另一个解决方案:cp --no-preserve=all file1 file2。试试看,我认为它将满足您描述的情况(例如,file2在不修改其属性的情况下修改的内容)。

范例

$ ls -l
    total 8
    -rw-r--r-- 1 tniles sambashare 16 Dec 16 12:21 fezzik
    -rw-r--r-- 1 tniles tniles     14 Dec 16 12:16 fred
$ cat *
    Lookout, world!
    Hello, world!
$ cp --no-preserve=all fred fezzik 
$ ls -l
    total 8
    -rw-r--r-- 1 tniles sambashare 14 Dec 16 12:22 fezzik
    -rw-r--r-- 1 tniles tniles     14 Dec 16 12:16 fred
$ cat *
    Hello, world!
    Hello, world!

更新 实际上,我只是注意到,cp除非指定-a-p,否则我的系统本身似乎会保留属性。我正在使用bash shell和GNU coreutils。我想你每天都会学到一些新东西。


测试结果(通配符),包括硬链接和不同的权限:

$ ls -li
total 12
913966 -rw-rw-r-- 1 vagrant vagrant 30 Dec 16 20:26 file1
913965 -rwxrw---- 2 pete    vagrant 39 Dec 16 20:35 file2
913965 -rwxrw---- 2 pete    vagrant 39 Dec 16 20:35 hardlinktofile2
$ cat file1
This is the contents of file1
$ cat file2
This is the original contents of file2
$ cp file1 file2
$ ls -li
total 12
913966 -rw-rw-r-- 1 vagrant vagrant 30 Dec 16 20:26 file1
913965 -rwxrw---- 2 pete    vagrant 30 Dec 16 20:37 file2
913965 -rwxrw---- 2 pete    vagrant 30 Dec 16 20:37 hardlinktofile2
$ cat file1
This is the contents of file1
$ cat file2
This is the contents of file1
$ 

真好 我进行了自己的测试,包括硬链接和不同的权限,看来您是正确的。
2015年

添加了我的测试结果;希望你不要介意。:)我没有测试ACL或扩展属性,但考虑到索引节点号已保留,我有99%的把握也可以。
2015年

很好...一点都不介意。:-)
tniles

13

在工作zsh的shell中< file1 > file2,shell会调用cat

对于仅由重定向组成且不包含命令和赋值的命令行,请zsh调用$NULLCMDcat默认情况下),除非唯一的重定向是<在这种情况下$READNULLCMDpager默认情况下)被调用。(除非zshsh或处于csh仿真状态,否则它的行为类似于所仿真的外壳)。

所以:

< file1 > file2

实际上与

cat < file1 > file2

< file1

是相同的

pager < file1

根据记录,此语法不适用于ksh93
fpmurphy

8
< from > to

不起作用,因为那里没有命令;没有过程。Shell打开/创建文件并安排重定向(这意味着引用这些文件的文件描述符被植入0和1:标准输入和标准输出)。但是,无需执行任何循环即可从标准输入读取并写入标准输出。

zsh通过在这种“空命令”情况下替换一个用户可配置的命令来完成此工作。该命令在命令行中不可见,但仍然存在。为此将创建一个进程,并且其工作方式相同。NULLCMDcat在默认情况下,所以< from > to实际上意味着 cat < from > tozsh,除非NULLCMD设置为别的东西; 这是一个“隐性猫”命令。

cat用作中介从文件中读取数据并将数据馈送到另一个进程时,就会发生“猫的无用使用” ,该进程的文件描述符可能只是连接到原始文件。

如果cat可以从情况中删除该命令,以使其余命令仍可以执行相同任务,则它是无用的。如果它是不可移动的,那么它就不是没有用的。

# useless, removable:
$ cat archive.tar | tar tf -    #  -->  tar tf archive.tar

# not removable (in POSIX shell):
$ cat > file
abc
[Ctrl-D]

# likewise:
STRING=$(cat file)

cat替换的 A 不是同一回事。例如,cat > file我们可以使用代替vi file创建文件。cat在使用剩下的任何东西来完成相同任务时,这并不算是去除。

如果cat是管道中的唯一命令,那么当然不能删除它;剩下的任何东西都不会进行重新布置来完成相同的工作。

一些shell脚本编写者cat之所以使用它,是因为他们认为它可以使输入操作数更靠近命令行的左侧。但是,重定向可以在命令行中的任何位置:

# If you're so inclined:
# move source archive operand to the left without cat:
$ < archive.tar tar xf - > listing

顺便说一句,您不必使用f -tar。tar xf -就是tar x
dnt 2015年

@mikeserv在哪里说cat文件创建涉及到?答案很清楚地说外壳程序就是这样做的。> file您指的是什么问题?我经常单独使用它来将现有文件截断为零长度或确保其存在。这个问题是关于UUoC和UUoC 为何< from > to不起作用的问题cat < from > to,而不是“请给我为什么cat不能很好替代的理由cp”。
哈兹2015年

1
@dnt tar磁带存档器tar默认情况下,许多实现仍可与第一个磁带设备一起使用。
斯特凡Chazelas

1

< file1 > file2 似乎是依赖于shell的,在zsh上它可以工作,而在bash上却不能。

编辑:删除虚假陈述


cp -a保留file1的属性并覆盖 file2的属性。与期望的行为相反。另外,即使通过查看手册页也无法确定硬链接会发生什么,但是我认为可以肯定地说,不会保留file2的硬链接。
2015年

没错,我没有足够仔细地阅读问题。
freshtea

1

除了所有的很好的答案,您可以通过避免UUOC 模拟一个cat

awk 1 file1 > file2   # For line-oriented text, not binaries.
dd if=file1 of=file2  # Works for binary files, too.
# many more geeky ways.

这些命令不会像平常cp那样复制文件元数据。


的确如此,但是值得一提的是,与相比,它们没有优点,而只有缺点(性能,可靠性)cat。在这里,您确实需要一个命令来在两个文件描述符之间转换数据,这cat是最好的命令之一。另请参阅pv哪些将能够splice()在Linux上用于fifos(尽管它fadvise(POSIX_FADV_SEQUENTIAL)不像GNU cat那样)。
斯特凡Chazelas

dd二进制文件的命令似乎不错...或者对于二进制文件也能cat正常工作吗?
2015年

@Wildcard cat也适用于二进制文件(Unix通常不能区分;但是,某些工具专门逐行工作,例如awk,grep,wc,... POSIX还定义了最小最大行长,因此从理论上讲面向行的工具可能会拒绝处理过大的行。)
Jens

2
@StéphaneChazelas这个答案也被用在嘲讽中。似乎,尽管有很多季节,但有些人还是对乐趣过敏(不针对您;我很重视您的shell专业知识和Opengroup标准工作)。
詹斯(Jens)

sed '' < file1 > file2;-)
Digital Trauma

0

如果可行,请不要修复。

我会用

cat < file1 > file2

并不会使PC失去语义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.