AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 558773
Accepted
Toothrot
Toothrot
Asked: 2019-12-25 05:24:39 +0800 CST2019-12-25 05:24:39 +0800 CST 2019-12-25 05:24:39 +0800 CST

将管道弯曲回其原点

  • 772

有人可能认为

echo foo >a
cat a | rev >a

将离开a包含oof;但它是空的。

  1. 为什么?
  2. 否则将如何适用rev于a?
pipe io-redirection
  • 7 7 个回答
  • 1980 Views

7 个回答

  • Voted
  1. Best Answer
    terdon
    2019-12-25T05:53:51+08:002019-12-25T05:53:51+08:00

    有一个应用程序!来自的sponge命令moreutils正是为此而设计的。如果您正在运行 Linux,它可能已经安装,如果没有,请在操作系统的存储库中搜索sponge或moreutils. 然后,你可以这样做:

    echo foo >a
    cat a | rev | sponge a
    

    或者,避免UUoC:

    rev a | sponge a
    

    这种行为的原因在于您的命令的运行顺序。这> a实际上是执行的第一件事并> file清空文件。例如:

    $ echo "foo" > file
    $ cat file
    foo
    $ > file
    $ cat file
    $
    

    所以,当你运行时cat a | rev >a,实际发生的> a是首先运行,清空文件,所以当cat a你执行时,文件已经是空的。这正是sponge写成的原因(来自man sponge,强调我的):

    海绵读取标准输入并将其写入指定文件。 与 shell 重定向不同,海绵在写入输出文件之前会吸收其所有输入。这允许构建读取和写入同一文件的管道。

    • 33
  2. stolenmoment
    2019-12-25T05:40:35+08:002019-12-25T05:40:35+08:00
    1. 输出截断很早就完成了,所以 cat 看到一个空文件。
    2. 第一个文件被构造为临时文件,或者 rev 的输出被定向到您然后重命名的临时文件。
    • 11
  3. Jasen
    2019-12-26T02:27:27+08:002019-12-26T02:27:27+08:00

    解决此问题的另一种方法是使用不会截断的书写方法

      rev a | dd conv=notrunc of=a
    

    这只有效,因为:

    1. rev 在产生输出之前读取内容,并且输出永远不会超过已经读取的量

    2. 新文件内容与原始文件大小相同或更大(在这种情况下大小相同)

    3. dd 打开要写入的文件而不截断它。

    这种方法对于就地修改太大而无法保留临时副本的文件可能很有用。

    • 9
  4. ilkkachu
    2019-12-26T12:51:21+08:002019-12-26T12:51:21+08:00
    cat a | rev > a
    

    为什么[a留空]?

    在上面的管道中,shell 分叉了两个子进程,一个用于管道的两个部分中的每一个。然后这些子进程运行有问题的命令,首先处理任何重定向,然后调用其中一个exec*()函数来启动外部实用程序。子进程并行运行,它们之间没有时间保证。

    执行一个进程不是很快,所以通常发生的是右侧的 shell 在cat有机会读取文件之前设法设置重定向。输出重定向> a会截断文件,因此cat没有可读取的内容,rev不接收任何数据,也不产生任何数据。即使您在左侧也使用了重定向cat < a | rev > a(acat

    另一方面,这非常一致地打印a contains: foo在我的系统上:

    echo foo > a; cat < a | tee a > /dev/null ; echo "a contains: $(cat a)"
    

    在这里,它tee会截断文件,所以这发生在 之后,exec()并且cat有更好的时间读取文件。但如果文件足够大,它可能会在读取过程中被截断。

    我说可能并且可能在那里,因为如果操作系统决定以另一种方式安排进程,确实会发生完全相反的情况。

    否则将如何适用rev于a?

    通常的解决方案是使用临时文件:

    cat a | rev > b && mv b a
    

    尽管通常存在可能覆盖现有文件的问题,除非您可以确定临时文件名可用。您可能应该使用mktemp:

    f=$(mktemp ./tmp.XXXXXX)
    cat a | rev > "$f" && mv "$f" a || rm "$f"
    

    或者,您可以使用该sponge工具,它确保在打开输出文件之前读取它获得的所有输入(否则它就像cat):

    cat a | rev | sponge a
    

    要不就

    rev < a | sponge a
    

    sponge > a与原始命令不起作用的原因相同,这将是一个错误。


    Sponge 来自moreutils,而不是标准工具。在管道到另一个命令之前完全缓冲命令输出中列出了它的一些替代方案?

    一些实用程序可能自己实现类似的功能,例如sort -o outputfile只在完成后打开输出文件,请参阅Sort support sorting a file in-place, like `sed --in-place`?

    • 4
  5. ikegami
    2020-05-14T21:14:39+08:002020-05-14T21:14:39+08:00

    >file创建一个新的空文件或截断现有文件。因此,文件中没有任何内容可供rev阅读。


    正如其他答案所提到的,您可以使用sponge它。但sponge并不是所有人都可以使用。

    以下是仅涉及外壳的通用解决方案:

    exec 3<file; rm file; rev <&3 >file; exec 3<&-
    

    这将打开文件(作为 fd 3)并将其删除。更准确地说,这只会删除文件的目录条目,而不是文件本身。在删除指向它的所有硬链接并关闭它的所有句柄之前,不会删除该文件。

    接下来,rev从“已删除文件”中运行读取。它的输出被发送到一个新文件。虽然这个新文件与原始文件同名,但它是一个不同的文件。因此,没有冲突。

    最后,我们关闭原始文件的描述符,允许它被释放。


    上述方法的问题是,如果出现问题,数据会丢失。这就是为什么人们可能更喜欢以下内容(它使用的磁盘空间不超过上述内容):

    ( rev file >file.new && mv file.new file ) || rm file.new
    
    • 1
  6. Zombo
    2019-12-28T10:16:34+08:002019-12-28T10:16:34+08:00

    你可以在 Ex 模式下使用 Vim:

    ex -s -c '%!rev' -c x a.txt
    
    • %选择所有行
    • !运行命令
    • x保存并关闭
    • 0
  7. tinnick
    2020-05-14T21:53:58+08:002020-05-14T21:53:58+08:00

    其他人已经解释了原因,但您不能这样做的原因基本相同:

    rev a >a
    

    但是如何做到这一点:

    echo foo >a
    echo `cat a | rev` >a
    # or
    echo `rev a` >a
    
    • 0

相关问题

  • 如何将 airodump-ng 的输出保存到文件中?

  • 将句柄传递给标准输入下行管道

  • 如何通过管道传输 bash 命令并保持 Ctrl+C 正常工作?

  • 为什么管道`mysql`到'tail'会改变输出格式?

  • `tee` 和 `bash` 进程替换顺序

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve