微前端架构如何改变企业的开发模式与效率提升
1985
2022-09-23
linux输出错误重定向 总结_qingshanli1988-ChinaUnix博客
Linux Shell 环境中支持输入输出重定向,用符号 < 和 > 来表示。0、1和2分别表示标准输入、标准输出和标准错误信息输出,可以用来指定需要重定向的标准输 入或输出,比如 2>a.txt 表示将错误信息输出到文件a.txt中。
同时,还可以在这三个标准输入输出之间实现重定向,比如将 错误信息重定向到标准输出,可以用 2>1来实现。
Linux下还有一个特殊的文件/dev/null,它就像一个无底洞,所有重定向到它的信息都会消失得无影无踪。这一点非常有用,当我们不需要回显程序的所有信息时,就可以将输出重定向到/dev/null。
如 果想要正常输出和错误信息都不显示,则要把标准输出和标准错误都重定向到/dev/null, 例如:
# ls 1>/dev/null 2>/dev/null
还有一种做法是将错误重定向到标准输出,然后再重定向到 /dev/null,例如:
# ls >/dev/null 2>1
注意:此处的顺序不能更改,否则 达不到想要的效果,此时先将标准输出重定向到 /dev/null,然后将标准错误重定向到标准输出,由于标准输出已经重定向到了/dev/null,因此标准错误也会重定向到/dev/null,于是一切静悄悄:-)
以上:Chapter 16. I/O 重定向
默认情况下始终有3个"文件"处于打开状态, stdin (键盘), stdout (屏幕), and stderr (错误消息输出到屏幕上). 这3个文件和其他打开的文件都可以被重定向. 对于重定向简单的解释就是捕捉一个文件, 命令, 程序, 脚本, 或者甚至是脚本中的代码块(参见 Example 3-1 和 Example 3-2)的输出, 然后将这些输出作为输入发送到另一个文件, 命令, 程序, 或脚本中.
每个打开的文件都会被分配一个文件描述符.[1]stdin, stdout, 和stderr的文件描述符分别是0, 1, 和 2. 对于正在打开的额外文件, 保留了描述符3到9. 在某些时候将这些格外的文件描述符分配给stdin, stdout, 或者是stderr作为临时的副本链接是非常有用的. [2] 在经过复杂的重定向和刷新之后需要把它们恢复成正常的样子 (参见 Example 16-1).
1 COMMAND_OUTPUT > 2 # 重定向stdout到一个文件. 3 # 如果没有这个文件就创建, 否则就覆盖. 4 5 ls -lR > dir-tree.list 6 # 创建一个包含目录树列表的文件. 7 8 : > filename 9 # > 会把文件"filename"截断为0长度. 10 # 如果文件不存在, 那么就创建一个0长度的文件(与'touch'的效果相同). 11 # : 是一个占位符, 不产生任何输出. 12 13 > filename 14 # > 会把文件"filename"截断为0长度. 15 # 如果文件不存在, 那么就创建一个0长度的文件(与'touch'的效果相同). 16 # (与上边的": >"效果相同, 但是在某些shell下可能不能工作.) 17 18 COMMAND_OUTPUT >> 19 # 重定向stdout到一个文件. 20 # 如果文件不存在, 那么就创建它, 如果存在, 那么就追加到文件后边. 21 22 23 # 单行重定向命令(只会影响它们所在的行): 24 # -------------------------------------------------------------------- 25 26 1>filename 27 # 重定向stdout到文件"filename". 28 1>>filename 29 # 重定向并追加stdout到文件"filename". 30 2>filename 31 # 重定向stderr到文件"filename". 32 2>>filename 33 # 重定向并追加stderr到文件"filename". 34 >filename 35 # 将stdout和stderr都重定向到文件"filename". 36 37 #============================================================================== 38 # 重定向stdout, 一次一行. 39 LOGFILE=script.log 40 41 echo "This statement is sent to the log file, "$LOGFILE"." 1>$LOGFILE 42 echo "This statement is appended to "$LOGFILE"." 1>>$LOGFILE 43 echo "This statement is also appended to "$LOGFILE"." 1>>$LOGFILE 44 echo "This statement is echoed to stdout, and will not appear in "$LOGFILE"." 45 # 每行过后, 这些重定向命令会自动"reset". 46 47 48 49 # 重定向stderr, 一次一行. 50 ERRORFILE=script.errors 51 52 bad_command1 2>$ERRORFILE # 错误消息发到$ERRORFILE中. 53 bad_command2 2>>$ERRORFILE # 错误消息添加到$ERRORFILE中. 54 bad_command3 # 错误消息echo到stderr, 55 # 并且不出现在$ERRORFILE中. 56 # 每行过后, 这些重定向命令也会自动"reset". 57 #============================================================================== 58 59 60 61 2>1 62 # 重定向stderr到stdout. 63 # 得到的错误消息与stdout一样, 发送到一个地方. 64 65 i>j 66 # 重定向文件描述符i 到 j. 67 # 指向i文件的所有输出都发送到j中去. 68 69 >j 70 # 默认的, 重定向文件描述符1(stdout)到 j. 71 # 所有传递到stdout的输出都送到j中去. 72 73 0< FILENAME 74 < FILENAME 75 # 从文件中接受输入. 76 # 与">"是成对命令, 并且通常都是结合使用. 77 # 78 # grep search-word 79 80 81 [j]<>filename 82 # 为了读写"filename", 把文件"filename"打开, 并且分配文件描述符"j"给它. 83 # 如果文件"filename"不存在, 那么就创建它. 84 # 如果文件描述符"j"没指定, 那默认是fd 0, stdin. 85 # 86 # 这种应用通常是为了写到一个文件中指定的地方. 87 echo 1234567890 > File # 写字符串到"File". 88 exec 3<> File # 打开"File"并且给它分配fd 3. 89 read -n 4 <3 # 只读4个字符. 90 echo -n . >3 # 写一个小数点. 91 exec 3>- # 关闭fd 3. 92 cat File # ==> 1234.67890 93 # 随机存储. 94 95 96 97 | 98 # 管道. 99 # 通用目的的处理和命令链工具. 100 # 与">"很相似, 但是实际上更通用. 101 # 对于想将命令, 脚本, 文件和程序串连起来的时候很有用. 102 cat *.txt | sort | uniq > result-file 103 # 对所有的.txt文件的输出进行排序, 并且删除重复行, 104 # 最后将结果保存到"result-file"中.可以将输入输出重定向和(或)管道的多个实例结合到一起写在一行上.1 command < input-file > output-file 2 3 command1 | command2 | command3 > output-file参见 Example 12-28 和 Example A-15. 可以将多个输出流重定向到一个文件上.1 ls -yz >> command.log 2>1 2 # 将错误选项"yz"的结果放到文件"command.log"中. 3 # 因为stderr被重定向到这个文件中, 4 # 所有的错误消息也就都指向那里了. 5 6 # 注意, 下边这个例子就不会给出相同的结果. 7 ls -yz 2>1 >> command.log 8 # 输出一个错误消息, 但是并不写到文件中. 9 10 # 如果将stdout和stderr都重定向, 11 # 命令的顺序会有些不同.关闭文件描述符n<-关闭输入文件描述符n.0<-, <-关闭stdin.n>-关闭输出文件描述符n.1>-, >-关闭stdout.子进程继承了打开的文件描述符. 这就是为什么管道可以工作. 如果想阻止fd被继承, 那么可以关掉它.1 # 只重定向stderr到一个管道. 2 3 exec 3>1 # 保存当前stdout的"值". 4 ls -l 2>1 >3 3>- | grep bad 3>- # 对'grep'关闭fd 3(但不关闭'ls'). 5 # ^^^^ ^^^^ 6 exec 3>- # 现在对于剩余的脚本关闭它. 7 8 # Thanks, S.C.如果想了解关于I/O重定向更多的细节参见 附 录 E.16.1. 使用execexec Example 16-1. 使用exec重定向标准输入1 #!/bin/bash 2 # 使用'exec'重定向标准输入. 3 4 5 exec 6<0 # 将文件描述符#6与stdin链接起来. 6 # 保存了stdin. 7 8 exec < data-file # stdin被文件"data-file"所代替. 9 10 read a1 # 读取文件"data-file"的第一行. 11 read a2 # 读取文件"data-file"的第二行. 12 13 echo 14 echo "Following lines read from file." 15 echo "-------------------------------" 16 echo $a1 17 echo $a2 18 19 echo; echo; echo 20 21 exec 0<6 6<- 22 # 现在将stdin从fd #6中恢复, 因为刚才我们把stdin重定向到#6了, 23 # 然后关闭fd #6 ( 6<- ), 好让这个描述符继续被其他进程所使用. 24 # 25 # <6 6<- 这么做也可以. 26 27 echo -n "Enter data " 28 read b1 # 现在"read"已经恢复正常了, 就是从stdin中读取. 29 echo "Input read from stdin." 30 echo "----------------------" 31 echo "b1 = $b1" 32 33 echo 34 35 exit 0同样的, exec >filename 命令将会把stdout重定向到一个指定的文件中. 这样所有的命令输出就都会发向那个指定的文件, 而不是stdout.Example 16-2. 使用exec来重定向stdout1 #!/bin/bash 2 # reassign-stdout.sh 3 4 LOGFILE=logfile.txt 5 6 exec 6>1 # 将fd #6与stdout相连接. 7 # 保存stdout. 8 9 exec > $LOGFILE # stdout就被文件"logfile.txt"所代替了. 10 11 # ----------------------------------------------------------- # 12 # 在这块中所有命令的输出就都发向文件 $LOGFILE. 13 14 echo -n "Logfile: " 15 date 16 echo "-------------------------------------" 17 echo 18 19 echo "Output of "ls -al" command" 20 echo 21 ls -al 22 echo; echo 23 echo "Output of "df" command" 24 echo 25 df 26 27 # ----------------------------------------------------------- # 28 29 exec 1>6 6>- # 恢复stdout, 然后关闭文件描述符#6. 30 31 echo 32 echo "== stdout now restored to default == " 33 echo 34 ls -al 35 echo 36 37 exit 0Example 16-3. 使用exec在同一脚本中重定向stdin和stdout1 #!/bin/bash 2 # upperconv.sh 3 # 将一个指定的输入文件转换为大写. 4 5 E_FILE_ACCESS=70 6 E_WRONG_ARGS=71 7 8 if [ ! -r "$1" ] # 判断指定的输入文件是否可读? 9 then 10 echo "Can't read from input file!" 11 echo "Usage: $0 input-file output-file" 12 exit $E_FILE_ACCESS 13 fi # 即使输入文件($1)没被指定 14 # 也还是会以相同的错误退出(为什么?). 15 16 if [ -z "$2" ] 17 then 18 echo "Need to specify output file." 19 echo "Usage: $0 input-file output-file" 20 exit $E_WRONG_ARGS 21 fi 22 23 24 exec 4<0 25 exec < $1 # 将会从输入文件中读取. 26 27 exec 7>1 28 exec > $2 # 将写到输出文件中. 29 # 假设输出文件是可写的(添加检查?). 30 31 # ----------------------------------------------- 32 cat - | tr a-z A-Z # 转换为大写. 33 # ^^^^^ # 从stdin中读取.Reads from stdin. 34 # ^^^^^^^^^^ # 写到stdout上. 35 # 然而, stdin和stdout都被重定向了. 36 # ----------------------------------------------- 37 38 exec 1>7 7>- # 恢复 stout. 39 exec 0<4 4<- # 恢复 stdin. 40 41 # 恢复之后, 下边这行代码将会如期望的一样打印到stdout上. 42 echo "File "$1" written to "$2" as uppercase conversion." 43 44 exit 0I/O重定向是一种避免可怕的子 shell中不可存取变量问题的方法.Example 16-4. 避免子shell1 #!/bin/bash 2 # avoid-subshell.sh 3 # Matthew Walker提出的建议. 4 5 Lines=0 6 7 echo 8 9 cat myfile.txt | while read line; # (译者注: 管道会产生子shell) 10 do { 11 echo $line 12 (( Lines )); # 增加这个变量的值 13 # 但是外部循环却不能存取. 14 # 子shell问题. 15 } 16 done 17 18 echo "Number of lines read = $Lines" # 0 19 # 错误! 20 21 echo "------------------------" 22 23 24 exec 3<> myfile.txt 25 while read line <3 26 do { 27 echo "$line" 28 (( Lines )); # 增加这个变量的值 29 # 现在外部循环就可以存取了. 30 # 没有子shell, 现在就没问题了. 31 } 32 done 33 exec 3>- 34 35 echo "Number of lines read = $Lines" # 8 36 37 echo 38 39 exit 0 40 41 # 下边这些行是脚本的结果, 脚本是不会走到这里的. 42 43 $ cat myfile.txt 44 45 Line 1. 46 Line 2. 47 Line 3. 48 Line 4. 49 Line 5. 50 Line 6. 51 Line 7. 52 Line 8.工作中需要构造一个数据传输工具,用来抓取ftp前端机的web日志并直接写入HDFS,该工具直接作为hadoop mapreduce任务定时执行,所以最好不使用文件脚本以免去分发文件的繁琐,能一句命令搞定最好。如何抓取ftp前端机的web日志并写入HDFS? 管道(pipe),命令如下:wget $SRC_URL --limit-rate=%s --tries=1 -O - | hadoop fs -put - $DESTPATH一行命令,可以直接当成-mapper的参数,不需要额外写文件脚本可惜这个方法有问题,使用管道的话,整个命令的返回值是后半部分(hadoop fs -put - $DESTPATH)的返回值当wget没有得到抓到任何内容返回失败时,整个命令返回0,错误没有捕捉到,如何解决?1. PIPESTATUSPIPESTATUS是一个环境变量,数组类型,保存了PIPE中所有命令的返回值PIPE命令成功结束后,判断PIPESTATUS[0]是否为零即可2. 那怎么将这些逻辑整合到一条shell命令中? 正确解法(之一)需要使用到() exit,先看看这些(), , ||的标准行为:( list )Placing a list of commands between parentheses causes a subshell to be created, and each of the commands in list to be executed in that subshell. Since the list is executed in a subshell, variable assignments do not remain in effect after the subshell completes.expression1 expression2 True if both expression1 and expression2 are true.expression1 || expression2 True if either expression1 or expression2 is true. The and || commands do not execute expression2 if the value of expression1 is sufficient to determine the return value of the entire conditional expression.最后我的解决方案:wget $SRC_URL --limit-rate=%s --tries=1 -O - | hadoop fs -put - $DESTPATH ( exit ${PIPESTATUS[0]} )其实在找到这个正确方法之前,还是走了不少弯路:wget $SRC_URL --limit-rate=%s --tries=1 -O - | hadoop fs -put - $DESTPATH exit ${PIPESTATUS[0]}exit命令会直接退出当前shell,慎用!wget $SRC_URL --limit-rate=%s --tries=1 -O - | hadoop fs -put - $DESTPATH ( exit ${PIPESTATUS[0]} )()的功能是新启动一个shell来运行括号里面的内容,这样exit的是()产生的shell,同时捕捉到了PIPESTATUS[0]最后为了代码可读性(C 传染过来的毛病-_-!!), 擅自加了个括号:( wget $SRC_URL --limit-rate=%s --tries=1 -O - | hadoop fs -put - $DESTPATH ) ( exit ${PIPESTATUS[0]} )这样反而又错了,由于左边的管道命令被封在一个新的shell里面,PIPESTATUS环境变量已经追踪不到里面的管道命令了!!标准的画蛇添足!问题几经曲折之后终于解决,对shell编程知识的积累也多了一点!PS:感觉shell确实是一种严重依赖"技巧",并把取巧当成其设计理念的工具,而不是语言shell不像一种完备的现代程序语言,拥有严格的语法和语义,严格直接意味着不容易出错,也不容易让用户误用,这其实是很重要的,也是我倾向使用的脚本环境反观shell,很多本来应该属于语法的东西, 是通过某种方式取巧模拟出来的,比如很多关键字其实是个程序所以在使用shell时,就需要额外小心,随时可能踩到地雷,当然不排除有高手将它用的出神入化,到了写出的脚本一般人都看不懂的地步。开发实际的项目时,shell编程的使用最好限制在很小的范围内,比如仅仅是用来包装一下主程序和工具程序,最好不要用它来做功能实现PS2:也许Python加上强大的系统管理工具库可以创造一个比较完美的可编程shell来,比如ipython,不过初试之后感觉还没达到我的期望,也可能用的太少还没上手
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~