主要内容
管道和重定向
在shell中,通过管道,可以使一条命令的输出结果作为另一条命令的输入数据,管道操作符为“|”(竖线)。一般的shell都包含三个标准文件描述符:标准输入(0)、标准输出(1)以及标准错误(2)。而我们可以把这三个标准文件描述符的操作进行重定向,例如把输出到屏幕(标准输出)的结果重定向到磁盘中的普通文件。又如把标准错误重定向到系统中的黑洞中去(/dev/null)。
管道
管道用于把一个命令的输出作为另一个命令的输入,管道在linux系统的使用过程中十分常见。
例如,找出最新创建的文件:
1 |
[centos7-server ~]# ls -t | head -n 1 |
上面的命令中,ls命令的输出会通过管道操作符传输到head命令,head命令把这些数据作为输入来啊处理。
临时重定向
重定向分为临时重定向和永久重定向,临时重定向只影响当前执行重定向的语句,而永久重定向则影响当前执行的脚本。
重定向标准输出
1 |
$ echo "hello linux" > hello.file |
上面的例子中使用了“>“操作符,它的作用是把数据重定向到指定文件,如果文件已经存在,则覆盖这个文件。如果希望改变该操作符的这个默认行为,可以使用下面的指令:
1 |
set -o noclobber |
之后任何重定向到已存在文件的行为都会失败,会输出错误信息:cannot overwrite existing file
可以使用下面的命令进行还原:
1 |
set +o noclobber |
重定向的另一个操作符是”>>“, 它和上面的操作符的唯一不同是:它不会覆盖已经存在的文件,而是把内容追加到文件的末尾。
1 2 3 |
ps aux >> task.file #排序ps的输出结果并匹配没有nginx的行,最后重定向到task.file文件中 ps aux | sort | grep -v nginx >> task.file |
除了把标准输出重定向到文件,我们还可以重定向到其他地方,例如标准错误输出:
1 |
echo "This is an error message" >&2 |
注意重定向符号(>)和&之间不能有空格。
重定向标准错误输出
标准错误是我们在操作shell的时候返回的错误信息,默认情况下,shell会直接把这些错误消息直接显示出来。在使用”>“或”>>“操作符的时候,它默认操作的是标准输出,我们可以在这两个符号前面添加标准错误描述符来改变这个行为。
例如,假设not_exist是一个不存在的文件夹,下面的操作会返回错误信息:
1 2 |
#列出当前文件夹和根目录下not_exist文件夹的内容 ls -l . /not_exist >> result.txt 2>./error.txt |
上面的命令可以拆分为两条命令理解:ls -l . 和 ls -l /not_exist。
第一条命令输出的内容会被正确重定向到result.txt文件中,由于/not_exist是一个不存在的目录,我们又另外设置了错误信息的保存位置,因此错误信息被重定向到error.txt。
下面是另外一种常见的重定向操作:
1 |
ls -l . /not_exist >> result.txt 2>&1 |
意思是:将标准输出重定向到文件result.txt,然后将标准错误输出重定向到与标准输出相同的地方。
重定向所有输出
除了可以分别重定向标准输出和错误输出外,还可以同时重定向它们,使用&>
即可。例如:
1 2 3 4 5 6 |
[normal@centos7-server tmp]$ ls -al test test2 test3 badtest &> test7 [normal@centos7-server tmp]$ cat test7 ls: cannot access test: No such file or directory ls: cannot access test2: No such file or directory ls: cannot access test3: No such file or directory ls: cannot access badtest: No such file or directory |
重定向标准输入
这种重定向相比起前面的两种,较为少用,因为在终端中,一般的命令都可以使用文件名作为参数,而不需要把文件的内容传递给这个程序,以grep为例子:
grep是一个文本查找工具,它的参数通常是grep <要查找的字符串> <目标文件名>,而不是grep <要查找的字符串> <目标字符串>
1 2 3 4 5 |
$ ps aux >> task.txt #通常的使用方法 grep nginx task.txt #重定向输入的方法,多了一个<符号 grep nginx < task.txt |
又如:
1 2 3 |
more task.txt #而不是 more < task.txt |
虽然可以这样做,但是显得有点多余。
另一种常见的输入重定向是:
1 2 3 4 5 6 |
[centos7-server ~]# wc << EOF > test string 1 > test string 2 > test string 3 > EOF 3 9 42 |
永久从定向
永久重定向需要使用exec命令。
重定向标准输出
下面是使用exec重定向标准输出的例子:
1 |
exec 1>testout.txt |
把标准输出重定向到testout.txt文件,当执行完这条命令之后,后续的所有输出都会被发送到testout.txt,屏幕上面不会有任何输出,除了错误输出外。
标准错误的重定向基本和标准输出的一样,只是文件描述符不一样:
1 |
exec 2>error.txt |
重定向标准输入
通常可以把文件绑定到标准输入,这样可以直接从文件读取内容,然后输出到屏幕。
1 2 3 4 5 6 7 8 |
#!/bin/bash exec 0<data1.txt while read data do echo "content read from file:$data" done |
标准输入绑定到文件data1.txt后,read命令就直接从这个文件中读取内容,然后输出到屏幕。
使用其他文件描述符
在Linux系统中,我们可以打开的文件描述符有9个,除了三个基本的:0,1,2。我们还可以使用的描述符是3~8。
例如我们可以把其他文件描述符绑定到文件,标准输出等等。
1 2 3 4 5 |
#!/bin/bash exec 3>out3.txt echo "this output should go to out3.txt" >&3 |
运行结果:
1 2 3 |
[normal@centos7-server tmp]$ ./redirect2.sh [normal@centos7-server tmp]$ cat out3.txt this output should go to out3.txt |
描述符之间的绑定和还原
通过额外可用的文件描述符(3~8),可以使描述符之间互相绑定和还原。
例子:
1 2 3 4 5 6 7 8 9 10 11 |
#!/bin/bash exec 3>&1 #save output descriptor to descriptor 3 exec 1>testout.txt #redirect output to a file echo "This line should go in to a file" exec 1>&3 #restore 1 from 3 echo "Now everyting back to normal" |
执行结果:
1 2 3 4 |
[normal@centos7-server tmp]$ ./redirect3.sh Now everyting back to normal [normal@centos7-server tmp]$ cat testout.txt This line should go in to a file |
读写一体的描述符
我们甚至可以同时把输入和输出重定向到同一个文件描述符,但当我们这样做的时候,最好清楚这样做的后果。
1 2 3 4 5 6 7 8 9 |
#!/bin/bash exec 3<>testfile #read from 3 read line <&3 #write to 3 echo "The line is: $line" >&3 |
testfile文件的内容如下:
1 2 3 |
This is the first line. This is the second line. This is the third line. |
当脚本运行结束,testfile的内容是:
1 2 3 4 5 |
[normal@centos7-server tmp]$ ./redirect4.sh [normal@centos7-server tmp]$ cat testfile This is the first line. The line is: This is the first line. third line. |
最后一行只剩下third line。其他的被覆盖了。
造成这种现象的原因是:文件内部有一个指针,这个指针记录着当前文件的读写位置,当脚本中的read结束(读完第一行),这个指针就处于第二行的开头,所以使用echo输出的时候,从这个位置开始写入数据,写入的这些数据会覆盖原来的数据,因此会开到第三行的数据也被覆盖了。注意:换行符(’\n’)虽然看不见,但是它同样会被覆盖。
看下面的演示:
1 2 |
this is the second line.This is the third line. #原始数据 The line is: This is the first line.'\n'third line. #被替换后的数据 |
第一行的第二个This前面实际上有一个看不到的换行符,为了方便对比,把它和第二行数据放到同一行中。
third line. 这些字符实际上是遇到被替换后的数据中的换行符后才换行的。
关闭文件描述符
当我们打开了新的文件描述符,当shell执行完脚本结束的时候会自动关闭这些描述符。如果希望在脚本结束前关闭这些描述符,可以使用&-
组合符号。通过把打开的描述符重定向到这个组合符号即可关闭它们。
1 2 3 4 5 6 |
#!/bin/bash # testing closing file descriptors exec 3> test17file echo "This is a test line of data" >&3 exec 3>&- #关闭描述符 echo "This won't work" >&3 |
一点问题
在使用重定向的时候,千万不能使用相同的文件,原因在于,后运行的命令中使用的文件会把先运行命令所使用的同名文件覆盖。
举个例子:
1 |
cat mydata.txt | sort | mydata.txt |
命令运行之后mydata.txt会变成一个空的文件,因为在读取文件之前就已经覆盖了这个文件的内容。
转载请注明:Pure nonsense » shell程序设计(一)管道和重定向