V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
CodeCodeStudy
V2EX  ›  Linux

Linux 重定向生成文件的执行顺序问题

  •  
  •   CodeCodeStudy · Apr 20, 2023 · 3186 views
    This topic created in 1111 days ago, the information mentioned may be changed or developed.

    ls -l > foo.txt

    再通过 cat foo.txt 查看文件内容可以看到 foo.txt 的文件大小是 0

    也就是说,先生成了文件,然后再执行 ls 命令,然后再将内容输出到文件里

    请问一下这是什么原理?

    20 replies    2023-04-21 09:11:51 +08:00
    zhlxsh
        1
    zhlxsh  
       Apr 20, 2023 via iPhone
    这不就是从左到右吗?先执行 ls ,得到的结果是 foo 大小 0 。然后将结果写到 foo
    ysc3839
        2
    ysc3839  
       Apr 20, 2023 via Android
    因为“重定向 stdout 到文件”这个操作就是把新进程的“file descriptor 1”设置成目标文件,设置成目标文件之前要先打开目标文件,打开目标文件前要先创建目标文件,所以就先有了目标文件。
    AoEiuV020CN
        3
    AoEiuV020CN  
       Apr 20, 2023
    关键应该是 fork exec 重定向 相关逻辑,

    c 语言入门前几课,fopen 参数 w 就是直接先创建空文件的,
    重定向是在 exec 执行 ls 进程前 shell 就要先给准备好 012 三个标准 io ,
    > foo.txt 意味着 1 号是 w 方式打开 foo.txt ,这时候就创建文件了,

    另外 ls 的输出会被缓冲,读取文件信息再到写入,这个过程文件本身还是空的,
    等真正写入文件大小变化的时候 ls 早已经读取完文件信息了,
    asilin
        4
    asilin  
       Apr 20, 2023   ❤️ 3
    所以这就是为什么不能使用“grep 有效信息 a.txt > a.txt”的方式进行原地文本内容替换的原因了。

    因为 bash 首先会将 a.txt 文件清空,然后再使用 grep 查询,那结果当然是 a.txt 文件被毁掉了。

    很多刚入门 Linux 的初学者,会经常性或者直觉性的犯一些这样的错误。

    类似的还有"ls a b c | grep" 这种命令,注意“|” 只接收标准输出,而不接收标准错误,如果要将管道前面的所有输出(包括错误信息)给到 grep 命令,那就得使用"|&"来代替"|"才能实现。

    诸如此类的知识,在 bash 的 man 文档和 info 文档中具有体现和介绍,所以推荐学习 bash 和 shell 最好看官方文档。
    julyclyde
        5
    julyclyde  
       Apr 20, 2023
    @AoEiuV020CN 跟输出缓冲没啥关系。“先打开后执行”是决定性的
    AoEiuV020CN
        6
    AoEiuV020CN  
       Apr 20, 2023
    @julyclyde #5 假设一个场景,ls 是一个一个遍历目录下所有文件,一个一个打印输出,同时没有缓冲,
    显然在遍历到 foo.txt 之前的的文件信息会被输出到 foo.txt 中,在读取 foo.txt 时就会有一定的内容在里面,
    julyclyde
        7
    julyclyde  
       Apr 20, 2023
    @AoEiuV020CN
    我不知道 close 之前,内容是否真的完成写入了
    AoEiuV020CN
        8
    AoEiuV020CN  
       Apr 20, 2023
    @julyclyde #7 这就是取决于缓冲了,没有输出缓冲的话 write 完成内容就真的完成写入了,
    有缓冲的话就是缓冲满了或者 close 或者 flush 才会真的写入,
    aloxaf
        9
    aloxaf  
       Apr 20, 2023
    你要知道,重定向是流式的,所以只能先打开文件,再执行命令。
    不需要流式的场景,倒是可以最后再写入文件,不过这样就得先缓存所有输出,比如 sponge 命令 cat a.txt | sponge -a a.txt
    flush9f
        10
    flush9f  
       Apr 20, 2023
    其实这个和重定向的缓存没有半毛钱关系,因为 ls 没有直接调用 getdents ,而是调用的 opendir ,然而 opendir 会提前分配内存缓存文件夹里所有的项,所以输出的文件大小自然是 0 ,因为 opendir 的时候它还啥都没写。想要验证很简单
    jot 10000 | xargs touch ; ls -l . . > foo.txt
    msg7086
        11
    msg7086  
       Apr 20, 2023
    1. bash 打开了 foo.txt 并获得了文件描述符
    2. bash 启动了 ls ,启动的时候把文件描述符塞进了 ls 的嘴里
    3. ls 列表,看到了 foo.txt
    4. ls 退出,foo.txt 关闭并刷出内容
    MrKrabs
        12
    MrKrabs  
       Apr 20, 2023
    posix_spawn_file_actions_addopen
    kaedeair
        13
    kaedeair  
       Apr 20, 2023
    这和 ps|grep xx 总是能看到一条结果的原理一样
    momocraft
        14
    momocraft  
       Apr 20, 2023
    猜测这样写 shell 比较省事

    为了不覆盖可能需要新开个临时文件, 启动程序, 等程序进程结束再 move 覆盖
    CodeCodeStudy
        15
    CodeCodeStudy  
    OP
       Apr 20, 2023
    @kaedeair #13 ps | grep xx 是 ps 先执行,所以 grep 至少看到一条结果是正常的,但是 ls -l > foo.txt 却是先执行了重定向生成空文件,然后再执行 ls -l ,然后将标准输出重定向到文件里,我不明白的是为什么先生成空文件
    tomychen
        16
    tomychen  
       Apr 20, 2023
    cmd > filename

    会先创建文件.
    然后再将标准输出写到文件

    [root@localhost shm]# ps -ef | /usr/bin/grep aabbaa
    root 4233 4148 0 06:10 pts/5 00:00:00 /usr/bin/grep aabbaa

    如果先执行了 ps 怎么会有 grep aabbaa?
    Alias4ck
        17
    Alias4ck  
       Apr 20, 2023
    建议了解一下重定向符号的 实现 #11 #2 都描述的很明确了 实在不行可以去做个操作系统实验

    https://pdos.csail.mit.edu/6.828/2019/labs/sh.html
    别人的实现
    https://github.com/zhayujie/xv6-riscv-fall19/blob/master/lab-02-shell/nsh.c
    asilin
        18
    asilin  
       Apr 20, 2023 via Android
    @CodeCodeStudy 是 grep 先执行哦,是不是有些反直觉,哈哈
    CapNemo
        19
    CapNemo  
       Apr 20, 2023
    创建文件

    打开文件

    创建子进程&绑定打开的文件描述符到子进程的输出

    ls 指令开始执行 -> 文件是空的

    ls 命令输出空结果到输出缓冲区

    ls 命令退出

    输出缓冲区被同步,ls 命令的输出被写入到文件 -> 文件现在不为空
    CodeCodeStudy
        20
    CodeCodeStudy  
    OP
       Apr 21, 2023
    @tomychen #16

    @asilin #18

    哦,对,不然 grep 出来的结果也不会有 ps 了
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2637 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 56ms · UTC 11:11 · PVG 19:11 · LAX 04:11 · JFK 07:11
    ♥ Do have faith in what you're doing.