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

posted @ 2022-09-16 10:33  shigp1  阅读(1068)  评论(0编辑  收藏  举报