[Linux] 盘点I/O 重定向符与使用例

前言


虽然笔者工作多年,常年接触Shell,但也对I/O重定向符只有最基本的了解,所以本文旨在梳理工作中没接触到的I/O重定向符知识。
如果你已经对I/O重定向符非常了解,也不妨看看本文。也许会有新的发现。
如果你对I/O重定向不太了解,那么推荐你仔细看完本文,你将对I/O重定向有最基本的了解。


参考


本文里系统层面的定义都参照自 Open Group的POSIX-2017 (IEEE Std 1003.1™-2017)标准


I/O 重定向是什么?


I/O redirection, 译做 I/O 重定向,是shell command language里的概念。如其名,在shell commad language里可以把程序的输入输出的位置重新指定。

下面我们来通过例子来看一下什么是重定向,懂的读者可以跳到下一节。
执行下列命令之后可以看到本该输出到命令行的”Text …“被输出到文件里,并通过cat命令打印内容到终端。

echo "Test Redirection Operator '>>' Shell Command Language" >> ~/echoOutput.txt
ls -l ~ | grep echoOutput.txt
cat ~/echoOutput.txt
rm ~/echoOutput.txt

Shell 与 Shell Command Language


笔者曾一度对cmd.exe、命令行、终端、Shell这些名词的具体定义感到非常恼火,笔者主观感受到这些词在日常生活中被误用地非常严重。
所以笔者将在本节澄清两个概念”Shell“与”Shell Command Language“。


什么是Shell?


Shell,根据Open Group的定义,Shell是一个用于解释字符串输入到具体命令并执行的程序。也被称为Shell命令语言解释器(Shell Command Language Interpreter)。通常Shell是通过终端(Terminal)或文本文件来接收字符串输入的。


什么是Shell Command Language?


Shell Command Language,在Open Group没有对于该词条给出明确的定义,通常我们在终端或者Shell脚本里写的就是Shell Command Language。和别的语言一样,由开发者键入的源代码都是文本。执行的时候需要经过翻译,这个翻译员就是上一节提到的Shell (Command Language Interpreter)

Shell命令语言,也许”Shell脚本语言“更加常听见,被设计成一种脚本语言,也就是“解释型语言”。与JavaScript等脚本语言类似,不需要像C语言等必须经过编译才能执行。

I/O 重定向符


I/O redirection Operator, 译做I/O 重定向符,下面列举POSIX-2017标准里定义的

No. 操作符 说明
1 < 输入流重定向,FD未指定时默认使用0 (standard input)。
2 > 输出流重定向,文件不存在时会被创建,以覆盖模式写入,FD未指定时默认使用1 (standard output)。
3 >| 与>类似,仅当因noclobber flag而使>操作符无法覆盖写入时,使用>|操作符可以为单独的文件覆写noclobber flag,使得该文件能被覆盖写入。noclobber flag通过set -C设置
4 >> 输出流重定向,文件不存在时会被创建,以追加模式写入,默认FD为1。
5 << 允许接下来的到标志之前的行被shell读入作为命令的input。
6 <<- 与<<类似,不过输入行的前置<tab>字符会被无视。
7 <& 将输入文件合并
8 >& 将输出文件合并
9 <> 打开一个文件并分配给指定FD,未指定时默认FD为1。

I/O 重定向符使用例


本节将列举的几个重定向符基本使用例,以供参考。


例1:用<指定文件作为输入。


humao% echo "testInput" > testInput.txt
humao% cat testInput.txt 
testInput
humao% cat <testInput.txt 
testInput
humao% rm testInput.txt
humao%

例2:用>指定文件作为输出,并覆盖原文件。


humao% echo "Original Text" > testOutput.txt
humao% cat testOutput.txt 
Original Text
humao% echo "New Text" > testOutput.txt
humao% cat testOutput.txt 
New Text
humao% rm testOutput.txt 
humao% 

例3:用>|指定文件作为输出,并突破覆盖写入限制。


humao% set -C
humao% echo "Original Text" > testOutput.txt
testOutput.txt
humao% cat testOutput.txt 
Original Text
humao% echo "New Text" > testOutput.txt
zsh: file exists: testOutput.txt
humao% cat testOutput.txt  
Original Text
humao% echo "New Text" >| testOutput.txt 
humao% cat testOutput.txt 
New Text
humao% rm testOutput.txt 
humao% 

例4:用>>指定文件作为输出,并在原文件追加写入。


humao% echo "Original Text" > testOutput.txt
humao% cat testOutput.txt 
Original Text
humao% echo "Additional Text" >> testOutput.txt
humao% cat testOutput.txt 
Original Text
Additional Text
humao% rm testOutput.txt 
humao% 

例5:用<<指定接下来几行作为输入。


需要注意的是anyTokenForEndInputing可以任意的token,用于标识输入到此结束。

humao% cat <<anyTokenForEndInputing
heredoc> Line1
heredoc> Line2
heredoc> Line3
heredoc> anyTokenForEndInputing
Line1
Line2
Line3
humao% 

例6:用<<-指定接下来几行作为输入,并无视行前导的tab字符。


注意:笔者使用的是zsh,在bash下未能成功加入tab字符。

humao% cat <<-anyTokenForEndInputing
heredocd>       Line1
heredocd> Line2
heredocd>       Line3
heredocd> Line4
heredocd>       anyTokenForEndInputing
Line1
Line2
Line3
Line4
humao% 

例7:<> 与 <&的使用例


这个例子稍微复杂一点。笔者简单说明一下本节在干什么。

  1. 创建input.txt并输入一行文字”1234567890“。
  2. 生成一个shell脚本,并加入两行指令
  3. 指令1:read指令读取5个字符,通过 <&3 重定向3号FD到read的标准输出上。
  4. 指令2:echo指令输出read的结果到终端。
  5. 为脚本增加执行权限。
  6. 执行脚本。并通过<>重定向符为脚本程序增加一个可读写的文件(input.txt)到3号FD上。
  7. 删除测试文件。
humao% echo 1234567890 > input.txt
humao% cat input.txt 
1234567890
humao% echo "read -n 5 word1 <&3" > testDuplicateRedirectionOperator.sh
humao% echo "echo \$word1" >> testDuplicateRedirectionOperator.sh 
humao% cat testDuplicateRedirectionOperator.sh                   
read -n 5 word1 <&3
echo $word1
humao% chmod u+x testDuplicateRedirectionOperator.sh
humao% ./testDuplicateRedirectionOperator.sh 3<>input.txt 
12345
humao% ls
input.txt				testDuplicateRedirectionOperator.sh
humao% rm *.*

这一例子比较难,读者需要知道脚本运行时,对于OS来说,也是一个进程,也有PID。
执行read指令的时候,read是作为子进程会继承父进程的FD,所以3号FD也能被read子进程所用。
如果能理解本节说明你对重定向与进程的关系都有一定的理解。

例8:用>&合并stderr和stdout。


本节你可以看到stderr通过>&重定向符与stdout合并的例子。

humao% mkdir cdBandedFolder
humao% chmod u-x cdBandedFolder
humao% cd cdBandedFolder 
cd: permission denied: cdBandedFolder
humao% cd cdBandedFolder 2>/dev/null
humao% cd cdBandedFolder 2>&1
cd: permission denied: cdBandedFolder
humao% rmdir cdBandedFolder

结语


对于在终端操作多的人来说,了解I/O重定向是必要的,准确来说,是理解进程的I/O是很重要的,你需要知道你的进程的输入和输出都到哪里去了,并且知道如何控制他们的去向。了解这些基本原理之后,你就能知道比如Eclipse、VS内的Console输出的是怎么来的了。你对自己程序整体性把握将上升一个台阶。


如果你觉得本文对你有启发或帮助,请不要吝惜你的点赞和转发~ ,
如果你觉得本文有错误的地方,也请在评论区指出,笔者会虚心接受并修改错误的地方~。

发布了24 篇原创文章 · 获赞 24 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/ToraNe/article/details/102894194