Linux shell 输入输出,重定向
一、文件描述符
Linux系统将每个对象当作文件处理。这包括输入和输出进程。Linux用文件描述符(file descriptor )来标识每个文件对象。文件描述符是一个非负整数,可以唯一标识会话中打开的文件。每个进程一次最多可以有九个文件描述符。 出于特殊目的,bash shell保留了前三个文件描述符(0 、1 和2 )。
STDIN
STDIN 文件描述符代表shell的标准输入。在使用输入重定向符号(< )时,Linux会用重定向指定的文件来替换标准输入文件描述符。
STDOUT
STDOUT 文件描述符代表shell的标准输出。shell的所有输出被导向到标准输出中。可使用输出重定向(>)将输出重定向到指定的文件中,会清除文件原来的内容。使用追加符(>>)将输出追加到指定的文件。
STDERR
shell通过特殊的STDERR文件描述符来处理错误消息。STDERR文件描述符代表shell的标准错误输出。重定向错误消息,可将该文件描述符值放在重定向符号前。该值必须紧紧地放在重定向符号前,否则不会工作。
将标准输出重定向到output.txt,将标准错误重定向到error。
&>将标准输出和标准错误重定向到output.log中。错误消息有更高的优先级,所以错误展示在第一行。
二、脚本使用重定向
要将输出重定向到文件描述符,需要在描述符数字前加&。
echo "This is an error message" >&2
例子:
#!/bin/bash
echo "This is a error message" >&2
echo "This is a normal message"
如果有大量数据需要重定向,重定向每个echo会麻烦。用exec命令告诉shell在脚本执行期间重定向某个特定文件描述符。
#!/bin/bash
exec 1>testout
echo 123
echo Hello Tom
echo 345
注意exec 1>testout中1>和testout不要有空格。
#!/bin/bash
exec 2>testerr
echo "This is the start of the script"
echo "now redirecting all output to another location"
exec 1>testout
echo "This output should go to the testout file"
echo "but this should go to the testerror file" >&2
你可以使用与脚本中重定向STDOUT 和STDERR 相同的方法来将STDIN从键盘重定向到其他位置。exec 命令允许你将STDIN 重定向到Linux系统上的文件中:
exec 0< testfile
这个命令会告诉shell它应该从文件testfile 中获得输入,而不是STDIN。
#!/bin/bash
exec 0<$HOME/test
while read line
do
echo file line:$line
done
三、自定义重定向
在shell中最多可以有9个打开的文件描述符。其他6个从3~8 的文件描述符均可用作输入或输出重定向。可以将这些文件描述符中的任意一个分配给文件,然后在脚本中使用它们。
创建输出文件描述符
可以用exec命令来给输出分配文件描述符。和标准的文件描述符一样,一旦将另一个文件描述符分配给一个文件,这个重定向就会一直有效,直到你重新分配。
例子:
#!/bin/bash
exec 3>test13out
echo "This should display on the monitor"
echo "and this should be stored in the file" >&3
echo "Then this should be back on the monitor"
exec 3>>test13out
将输出追加到文件test13out。
恢复重定向的文件描述符
#!/bin/bash
exec 3>&1
exec 1>testout4
echo "Hello,Tom"
echo 123
echo 234 >&3
exec 1>&3
echo "now,standard output"
将文件描述符3重定向到标准输出,将标准输出描述符1重定向到testout4文件,因此接下来的两个echo输出到testout4;接下来的echo输出到文件描述符3,又因为文件描述符3重定向到标准输出,因此输出到控制台;再将文件描述符重定向到标准输出,因此将文件描述符1有testout4重定向到了标准输出。
创建输入文件描述符
#!/bin/bash
exec 4<&0
exec 0<$HOME/test
while read item
do
echo $item
done
exec 0<&4
echo "测试输入"
read -p "Enter input" name
echo
echo "输出:$name"
在重定向到文件之前,先将STDIN文件描述符保存到文件描述符4,然后在读取完文件之后再将STDIN恢复到它原来的位置。
创建读写文件描述符
文件testfile:
This is the first line.
This is the second line.
This is the third line.
例子:
#!/bin/bash
exec 3<> $HOME/testfile
read line <&3
echo "read line:$line"
echo "This is a test line">&3
用文件描述符3同时读写$HOME/testfile文件,shell会维护一个内部指针,指明在文件中的当前位置。任何读或写都会从文件指针上次的位置开始。因此发生了数据覆盖。
关闭文件描述符
如果你创建了新的输入或输出文件描述符,shell会在脚本退出时自动关闭它们。然而在有些情况下,你需要在脚本结束前手动关闭文件描述符。要关闭文件描述符,将它重定向到特殊符号&- 。
exec 3>&-
例子:
#!/bin/bash
exec 3>testchong
echo 1234567 >&3
exec 3>&-
echo 123 >&3
#!/bin/bash
exec 3>testchong
echo 1234567 >&3
exec 3>&-
exec 3>test123
echo 123 >&3
关闭文件描述符并重新打开,并发送数据则会覆盖原来的文件。
查看打开的文件描述符
lsof 命令会列出整个Linux系统打开的所有文件描述符。
lsof -a -p $$ -d 0,1,2
-a 选项用来对其他两个选项的结果执行布尔AND运算。-p指定进程ID(PID)。-d指定要显示的文件描述符编号。
各列意义:
COMMAND:命令名前9个字符
PID:进程的id
USER:进程所有者
FD:文件描述符号以及访问类型( r 代表读,w 代表 写,u 代表读写)
TYPE:文件类型,CHR 代表字符型,BLK 代表块 型,DIR 代表目录,REG 代表常规文件
DEVICE:设备的设备号(主设备号和从设备号)
SIZE:文件的大小
NODE:索引节点(文件在磁盘上的标识)
NAME:打开文件的确切名称
#!/bin/bash
exec 3> test18file1
exec 6> test18file2
exec 7< testfile
lsof -a -p $$ -d0,1,2,3,6,7
$$显示当前进程的pid。
丢弃命令输出
null文件跟它的名字很像,文件里什么都没有。shell输出到null文件的任何数据都不会保存,全部都被丢掉了。
ls -al > /dev/null
由于/dev/null文件不含有任何内容,通常用它来快速清除现有文件中的数据,而不用先删除文件再重新创建。
四、创建临时文件
mktemp 命令可以在/tmp目录中创建一个唯一的临时文件。 shell会创建这个文件,但不用默认的umask值。它会将文件的读和写权限分配给文件的属主,并将你设成文件的属主。一旦创建了文件,你就在脚本中有了完整的读写权限,但其他人没法访问它(当然,root用户除外)。
要用mktemp命令在本地目录中创建一个临时文件,你只要指定一个文件名模板就行了。模板可以包含任意文本文件名,在文件名末尾加上6个X就行了。
mktemp test.XXXXXX
#!/bin/bash
temp_file=$(mktemp chong.XXXXXX)
exec 3>$temp_file
echo "This is the first line" >&3
echo "This is the second line." >&3
echo "This is the last line." >&3
exec 3>&-
cat $temp_file
rm -rf $temp_file > /dev/null
使用mktemp -t会强制在/tmp目录创建文件。
mktemp -t test.XXXXXX
#!/bin/bash
temp_file=$(mktemp -t chong.XXXXXX)
echo "temp file:$temp_file"
exec 3>$temp_file
echo "This is the first line" >&3
echo "This is the second line." >&3
echo "This is the last line." >&3
exec 3>&-
cat $temp_file
rm -rf $temp_file > /dev/null
mktemp -d创建临时目录。
#!/bin/bash
tempdir=$(mktemp -d dir.XXXXXX)
cd $tempdir
tempfile1=$(mktemp temp.XXXXXX)
tempfile2=$(mktemp temp.XXXXXX)
exec 7> $tempfile1
exec 8> $tempfile2
echo "Sending data to directory $tempdir"
echo "This is a test line of data for $tempfile1" >&7
echo "This is a test line of data for $tempfile2" >&8
将输出同时发送到显示器和日志文件
tee 命令相当于管道的一个T型接头。它将从 STDIN 过来的数据同时发往两处。一处是STDOUT ,另一处是tee 命令行所指定的文件名:
tee filename
默认情况下,tee 命令会在每次使用时覆盖输出文件内容。
可使用-a追加文件:
#!/bin/bash
tempfile=$(mktemp test.XXXXXX)
echo "temp file:$tempfile"
echo "This is the start of the test" | tee $tempfile
echo "This is the second line of the test" | tee -a $tempfile
echo "This is the end of the test" | tee -a $tempfile