• 解密 Shell 重定向:>、>1、2>、&>、>&、2>&1、1>&2 、/dev/null、>>、>>2、&>>


    《大数据平台架构与原型实现:数据中台建设实战》博主历时三年精心创作的《大数据平台架构与原型实现:数据中台建设实战》一书现已由知名IT图书品牌电子工业出版社博文视点出版发行,点击《重磅推荐:建大数据平台太难了!给我发个工程原型吧!》了解图书详情,京东购书链接:https://item.jd.com/12677623.html,扫描左侧二维码进入京东手机购书页面。

    Shell 重定向这部分知识比较琐碎,符号很多,各种操作符又有多种“变种”,所以会让人感觉很凌乱。本文试图对这部分内容做一些规范化的梳理,主要以 GNU 官方文档为准。

    1. 最佳实践


    为了让本文更加实用,我们先把最重要的一些结论和建议放在前面,至于为什么要这样做,可以再仔细阅读本文后续的介绍。

    • 永远首选 &> 实现正常信息和错误信息的合并重定向
    • 永远首选 &> /dev/null 丢弃所有输出信息
    • 几乎没有必要使用0122>2>&11>&2这些操作符
    • >1> 的简写形式,>这个“坑”给了1>2>就没有简写形式了

    实际上,关于重定向,我们记住&>这一个操作符就足够了,在实际应用中,我们几乎不需要在命令行中使用到012这些标识符,以及它们和&>之间的各种组合。但是,当出现了由它们组成的各种组合时,我们还是得要知道具体的含义,这就是本文后续要介绍的内容了。

    2. 操作符汇总


    首先,有必要“温习”一个基础知识点:在Linux上一切皆文件!所有的设备都可以使用一个文件来描述或代表,这一点在重定向操作中体现的尤为明显,例如:012/dev/null 都是文件,其中:0代表文件/dev/stdin1代表文件/dev/stdout2代表文件/dev/stderr。而这些文件又分别代表了某种设备:/dev/stdin为标准输入设备,也就是键盘,所以:0代表 [ 标准输入 ], 默认是键盘,1即代表[ 标准输出 ] ,默认是屏幕,2代表 [ 标准错误 ] ,默认也是屏幕,012的官方称谓是:file descriptor - 文件描述符。以下是与重定向有关的符号汇总:

    符号示例说明
    0N/A代表:[ 标准输入 ],对应设备文件:/dev/stdin,默认指向:键盘
    1N/A代表:[ 标准输出 ],对应设备文件:/dev/stdout,默认指向:屏幕
    2N/A代表:[ 标准错误 ],对应设备文件:/dev/stderr,默认指向:屏幕
    /dev/nullN/A一个特殊文件,代表一个空设备,重定向到该文件就意味着:不输出任何信息
    >command > file标准输出重定向
    2>command 2> file标准错误重定向
    >>command >> file标准输出重定向 (append模式)
    <command < input标准输入重定向
    >&command >& file合并输出重定向
    &>command &> file合并输出重定向
    &>>command &>> file合并输出重定向 (append模式)
    2>&1command 2>&1合并输出重定向 (将[ 标准错误 ] 输出到 [ 标准输出 ] 指向的目标,实现正常信息和错误信息合并输出至一处)
    1 >& 2command 1>&2合并输出重定向 (将 [ 标准输出 ] 合并到 [ 标准错误 ] 输出指向的目标,实现正常信息和错误信息合并输出至一处)
    2&>1N/A错误代码,不存在该重定向操作符
    1&>2N/A错误代码,不存在该重定向操作符

    注意:文件标识符012和重定向操作符<之间是不能有空格的,通常的解释是:1>2>是一个整体(整体作为一个符号),其实可以反过来看,当012<之间出现空格时,012就会被优先解释为:当前文件夹下名为012的文件!所以中间不能有空格!

    3. 环境准备


    # always remove the file "a-non-existing-file.txt" no matter if it exists!
    rm -f a-non-existing-file.txt
    # always create the file "an-existing-file.txt"
    echo 'hello world!' > an-existing-file.txt
    cat an-existing-file.txt # 输出:hello world!
    cat a-non-existing-file.txt # 输出:cat: a-non-existing-file.txt: No such file or directory
    cat an-existing-file.txt a-non-existing-file.txt # 输出:hello world! + cat: a-non-existing-file.txt: No such file or directory
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4. [ 标准输出 ] 重定向


    下面是一组“平平无奇”的标准输出重定向测试,记住一点:1>>一样的,>1>的简化形式,1>是一个整体,中间不得有空格。

    # Test Case 1: 标准输出重定向 [ > ]
    
    # 清空 output.txt 文件
    > output.txt
    # 将标准输出重定向到 output.txt 文件
    cat an-existing-file.txt > output.txt 
    # 查看输出
    cat output.txt # 输出:hello world!
    
    # Test Case 2: 标准输出重定向 [ 1> ]
    
    > output.txt
    cat an-existing-file.txt 1> output.txt
    cat output.txt # 输出:hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    5. [ 标准错误 ] 重定向


    下面的这组测试很好地验证了标准错误与标准输出之间的差异,由于我们只是将标准错误重定向到了output.txt文件,所以只有错误信息才能被输出到该文件中,正常的命令输出信息(如cat an-existing-file.txt时输出的hello world!)是不会输出到output.txt文件中的。还有一点可以补充的是:既然>这个“坑”给了1>,所以2>就没有简写形式了。

    # Test Case 3: 标准错误重定向 [ 2> ] 
    
    > output.txt
    cat an-existing-file.txt 2> output.txt
    cat output.txt # 输出:空(无内容)
    
    > output.txt
    cat a-non-existing-file.txt 2> output.txt
    cat output.txt # 输出:cat: a-non-existing-file.txt: No such file or directory
    
    > output.txt
    cat an-existing-file.txt a-non-existing-file.txt 2> output.txt
    cat output.txt # 输出:cat: a-non-existing-file.txt: No such file or directory
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    6. [ 标准输出 ] [ 标准错误 ] 重定向到同一文件


    注意:本节讨论的这些操作全部都是”错误“示范,执行结果也很难解释,本节内容主要提醒读者不要犯本节示例中出现的这些错误,同时为下一节的合并重定向做铺垫。

    显然,很多时候,我们需要把所有的信息(正常的输出和错误输出)统一输出到文件中,如果是基于上面的知识积累,我们可能想的一种做法是:将标准输出和标准错误都重定向到一个文件,就像下面这样:

    # Test Case 4: [ 标准输出 ] 和 [ 标准错误 ] 分别重定向到同一文件,信息会相互覆盖
    
    > output.txt
    cat an-existing-file.txt a-non-existing-file.txt 1> output.txt 2> output.txt
    cat output.txt # 输出:cat: a-non-existing-file.txt: No such file or directory
    
    # Test Case 5: [ 标准输出 ] 和 [ 标准错误 ] 分别重定向到同一文件,信息会相互覆盖
    > output.txt
    cat a-non-existing-file.txt an-existing-file.txt 1>output.txt 2>output.txt
    cat output.txt # 输出:hello world! + isting-file.txt: No such file or directory
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    上述想法是对的,但却忽略了一个技术细节,那就是:1> output.txt2> output.txt 是两次独立的重定向操作,从测试结果上分析看,两次重定向操作1> output.txt 2> output.txt 应该都是”生效“的,但是由于它们都向同一个文件写入数据,出现了覆盖对方数据的问题,所以这种方式完全不可取!也没有必要深究这里面的覆盖逻辑是什么,总之,这不是一种正确的做法,我们实际需要的是:[ 标准输出 ] + [ 标准错误 ] 合并重定向

    7. [ 标准输出 ] + [ 标准错误 ] 合并重定向


    为了实现[ 标准输出 ] + [ 标准错误 ] 合并重定向,Shell引入了专职的操作符:&>>&2>&1 ,它们能将标准输出和标准错误合并到输出到指定的位置(不存在上述相互覆盖的问题)。

    7.1. &> 是首选,&>>& 两种形式等价 ,但有极微小的差异


    # Test Case 6: [ 标准输出 + 标准错误 ] 合并重定向 [ &> ] 
    > output.txt
    cat an-existing-file.txt a-non-existing-file.txt &> output.txt
    cat output.txt # 输出:hello world! + cat: a-non-existing-file.txt: No such file or directory
    
    # Test Case 7: [ 标准输出 + 标准错误 ] 合并重定向 [ >& ] 
    > output.txt
    cat an-existing-file.txt a-non-existing-file.txt >& output.txt
    cat output.txt # 输出:hello world! + cat: a-non-existing-file.txt: No such file or directory
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    上述测试用例表明&>>& 可相互替换,但是,在一种特殊情况下,两者会有差异,即:>& 后面不可接数字或连字符,否则会报错,也就是说:当我们想要重定向的文件名刚好是个数字或连字符时,就只能使用&> !就是因为这样一种很特殊的情况,使得&>的适用性高出了>& “一丢丢”,所以GNU官方文档也因此建议用户优先使用&> !以下是测试用例:

    # Test Case 8: [ 标准输出 + 标准错误 ] 合并重定向 [ &> ] 
    > 3
    cat an-existing-file.txt a-non-existing-file.txt &>3
    cat output.txt # 输出:hello world! + cat: a-non-existing-file.txt: No such file or directory
    
    # Test Case 9: [ 标准输出 + 标准错误 ] 合并重定向 [ >& ] 
    > 3
    cat an-existing-file.txt a-non-existing-file.txt >&3 # 报错:-bash: 3: Bad file descriptor
    cat output.txt # 输出:hello world!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    7.2. &>>& 可视作 2>&11>&2 的简化形式,但它们并不完全等价


    这里一定要结合重定向的目标来看。通常&>>& 直接出现在命令和重定向文件之间,语法简洁,“重定向”的示意性很强,也没有任何歧义。但是,单独的 2>&1 操作短语只是说:将[ 标准错误 ] 输出合并到 [ 标准输出 ] 指向的目标,而此时 [ 标准输出 ] 指向的是什么并没有给用户提供显式设置的方式,而是要根据当时的上下文判定(可能是文件,也可能是屏幕),这就会导致:由于2>&1 出现的时机(在命令行中的位置)不同,产生的结果也将不同!这是 2>&1 要比 &>>& 难以“拿捏”的地方,下面的例子(也是一个非常Tricky的例子)很好地诠释了这一点:

    # Test Case 10: [ 标准错误 ]合并重定向到[ 标准输出 ]: [ 2>&1 ] 
    > output.txt
    # [ 标准输出 ]先被重定向到了文件,而后[ 标准错误 ]被合并重定向到了[ 标准输出 ]所指向的位置,此时也是文件
    # 所以最终[ 标准错误 ]和[ 标准输出 ]都将输出到文件
    cat an-existing-file.txt a-non-existing-file.txt > output.txt 2>&1
    cat output.txt # 输出:hello world! + cat: a-non-existing-file.txt: No such file or directory
    
    # Test Case 11: [ 标准错误 ]合并重定向到[ 标准输出 ]: [ 2>&1 ] 
    > output.txt
    # [ 标准错误 ]先被合并重定向到了[ 标准输出 ],注意,此时的[ 标准输出 ]还是屏幕,所以[ 标准错误 ]实际是被定向到了屏幕,
    # 而后[ 标准输出 ]被重定向到了文件(注意:> 就是 1>),但是[ 标准错误 ]没有再改动,所以最终错误信息输出到了屏幕,正常信息保存进了文件
    cat an-existing-file.txt a-non-existing-file.txt 2>&1 > output.txt
    cat output.txt # 输出:hello world!
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在上述两个对比测试中可以看到:由于 2>&1 出现的位置不同,导致了完全不同的重定向结果,具体的解释参考代码中的注释。

    理解了 2>&1 就不难理解 1>&2了,它将 [ 标准输出 ] 合并重定向到了 [ 标准错误 ] 指向的目标,同样,此时 [ 标准输出 ] 指向的是什么需要根据当时的上下文判定。

    7.3. 没有 2&>11&>2 这种形式,如果错误地使用了它,会导致”怪异“行为


    对,没有!虽然有 &>,但就是没有 2&>1,如果你错误地使用了它,命令行可能不会报错,但执行结果会和预期严重不符,看上去行为非常”怪异“!以下又是一个非常Tricky的测试用例:

    # Test Case 12: [ 标准输出 + 标准错误 ] 合并重定向 [ 2&>1 ] 
    > output.txt
    cat an-existing-file.txt a-non-existing-file.txt > output.txt 2&>1
    cat output.txt # 输出:空(无内容)
    cat 1 # 输出:hello world! + cat: a-non-existing-file.txt: No such file or directory + cat: 2: No such file or directory
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行后:控制台和output.txt均无任何输出!但是会在当前目录生成一个名为1的文件,生成内容为:

    hello world!
    cat: a-non-existing-file.txt: No such file or directory
    cat: 2: No such file or directory
    
    • 1
    • 2
    • 3

    我们来分析一下导致上述怪异输出的原因:首先,[ 标准输出 ]先被重定向到了文件,然后2被解释为了一个文件,给到cat去读取,但由于当前目录并没有这个文件,故cat命令(将会)报错:cat: 2: No such file or directory,然后&>1被解释为:[ 标准输出 + 标准错误 ] 合并重定向到了文件 1,如此一来,整个输出内容就可以解释通了:

    1. 所有的输出都被重定向了文件 1
    2. cat an-existing-file.txt时,标准输出了hello world!
    3. cat a-non-existing-file.txt时,标准错误输出了cat: a-non-existing-file.txt: No such file or directory
    4. cat 2时,标准错误输出了cat: 2: No such file or directory

    参考资料

    https://www.gnu.org/software/bash/manual/html_node/Redirections.html
    https://www.redhat.com/sysadmin/linux-shell-redirection-pipelining

  • 相关阅读:
    入耳式无线蓝牙耳机哪款好?无线入耳蓝牙耳机推荐
    VSCode查询包中的内容设置
    Ubuntu 支持小米四足机器人“铁蛋”;GitHub 首席运营官离职;JetBrains 为 Kotlin 推出跨平台 UI 框架 | 开源日报
    springboot+mysql大学生就业管理系统-计算机毕业设计源码85553
    【重点突破】—— Typescript走进类型的世界
    股票多因子模型之截面回归
    差动驱动机器人轨迹-CoCube
    数字森林:无人机航测技术在林业调查中的应用
    ASPICE与ISO 21434:汽车软件与网络安全标准的协同与互补
    pandas 学习 第15篇:分组 groupby
  • 原文地址:https://blog.csdn.net/bluishglc/article/details/132716571