• 十、文件描述符


    标准文件描述符

    1. 文件描述符是非负整数,可以唯一标识会话中打开的文件
    2. 每个进程一次最多能有9个文件描述符
    3. bash shell保留前三个文件描述符(0、1、2)
    文件描述符 缩写 描述
    0 STDIN 标准输入
    1 STDOUT 标准输出
    2 STDERR 标准错误

    STDIN

    标准输入描述符,终端默认标准输入是键盘

    可以使用重定向<指定文件替换标准输入文件描述符

    STDOUT

    标准输出描述符,终端默认是输出到显示器

    可以使用重定向>输出到指定文件

    STDERR

    标准错误输出描述符,默认输出到显示器

    不会随着STDOUT的重定向而改变,还是会输出到显示器

    [root@tzPC 15Unit]# ls -al badfile > test3
    ls: 无法访问badfile: 没有那个文件或目录

    重定向错误

    STDERR文件描述符为2,需要放在重定向符号前

    [root@tzPC 15Unit]# ls -al test batest test3 2>test4
    -rw-r--r--. 1 root root 0 8月  19 10:13 test3
    [root@tzPC 15Unit]# cat test4
    ls: 无法访问test: 没有那个文件或目录
    ls: 无法访问batest: 没有那个文件或目录

    重定向错误和数据

    [root@tzPC 15Unit]# ls -al test test2 test3 2>test6 1>test7
    [root@tzPC 15Unit]# cat test6
    ls: 无法访问test: 没有那个文件或目录
    ls: 无法访问test2: 没有那个文件或目录
    [root@tzPC 15Unit]# cat test7
    -rw-r--r--. 1 root root 0 8月  19 10:13 test3

    重定向错误和数据到同一个文件

    这种方式看起来没啥用,但是会优先显示错误信息

    [root@tzPC 15Unit]# ls -al test test3 test2 &> test7
    [root@tzPC 15Unit]# cat test7
    ls: 无法访问test: 没有那个文件或目录
    ls: 无法访问test2: 没有那个文件或目录
    -rw-r--r--. 1 root root 0 8月  19 10:13 test3

    可以看到test跟test2的错误信息会优先显示到开头

    脚本中重定向输出

    • 临时重定向
    • 永久重定向

    临时重定向

    效果

    [root@tzPC 15Unit]# bash test8.sh
    This is an error
    This is normal output
    [root@tzPC 15Unit]# bash test8.sh 2>test9
    This is normal output
    [root@tzPC 15Unit]# cat test9
    This is an error

    脚本

    [root@tzPC 15Unit]# cat test8.sh
    #!/bin/bash
    #testing STDERR messages
    echo "This is an error" >&2 #将这条错误信息重定向到文件描述符
    echo "This is normal output"

    可以看到脚本中先将错误信息重定向到错误文件描述符,再通过错误文件描述符重定向到文件test9

    错误信息是临时重定向到文件中的,脚本还是会输出错误信息。

    永久重定向

    使用exec命令会启动一个新的shell将STDOUT文件描述符重定向到文件

    脚本

    [root@tzPC 15Unit]# cat test10.sh
    #!/bin/bash
    #redirecting all output to a file
    exec 2>testerror
    echo "This is the start of the script"
    
    exec 1>testout
    echo "This is output should go to the testout file"
    echo "This is an error" >&2

    效果

    [root@tzPC 15Unit]# bash test10.sh
    This is the start of the script
    [root@tzPC 15Unit]# cat testout
    This is output should go to the testout file
    [root@tzPC 15Unit]# cat testerror
    This is an error

    可以看到exec 2>testerror会把重定向到错误描述符的语句重定向到文件,其他的会原样输出,exec 1>testout会将后面的语句都重定向输出到文件,除了错误重定向。

    错误信息永久的重定向到了文件,不会在脚本中输出。

    脚本中重定向输入

    使用exec命令

    格式

     exec 0< testfile

    脚本

    [root@tzPC 15Unit]# bash test12.sh
    Line #1: this is the first line.
    Line #1: this is the second line.
    [root@tzPC 15Unit]# cat test12.sh
    #!/bin/bash
    #redirecting file input
    exec 0<testfile
    count=1
    while read line
    do
            echo "Line #$count: $line"
            count=$[ $conug + 1 ]
    done

    创建自己的重定向

    shell中最多有9个文件描述符,3~8文件描述符均可用作输入或输出重定向。

    创建输出文件描述符

    使用exec命令来分配输出文件描述符,一旦分配一直有效,直到重新分配或关闭该文件描述符。

    效果

    [root@tzPC 15Unit]# bash test13.sh
    This should display on the monitor
    The this should be back on the monitor
    [root@tzPC 15Unit]# cat test13out
    and this should be stored in the file

    脚本

    [root@tzPC 15Unit]# cat test13.sh
    #!/bin/bash
    #using an alternative file descriptor
    exec 3>test13out
    #创建一个输出描述符,输出到文件test13out
    echo "This should display on the monitor"
    echo "and this should be stored in the file" >&3
    echo "The this should be back on the monitor"

    或者追加到文件

    exec 3>>test13out

    重定向文件描述符

    将原来输出的位置重定向到一个文件描述符,在用该文件描述符重定向回输出位置

    [root@tzPC 15Unit]# cat test14.sh
    #!/bin/bash
    #storing STDOUT, then coming back to it
    exec 3>&1
    #发送给文件描述符3的输出都将出现在显示器上
    exec 1>test14out
    #发送到文件描述符1的输出都将重定向到文件test14out
    echo "This should store in the output file"
    echo "along with this line"
    #以上两句是发送到显示器上的,最后会重定向到test14out文件
    exec 1>&3
    #发送到文件描述符1也就是到test14out文件的输出会重定向到文件描述符3,而3是重定向到显示器的,所以最后会在显示器上输出
    echo "Now things should be back to normal"
    
    [root@tzPC 15Unit]# bash test14.sh
    Now things should be back to normal

    创建输入文件描述符

    [root@tzPC 15Unit]# cat test15.sh
    #!/bin/bash
    #redirecting input file descriptors
    exec 6<&0
    #文件描述符0默认从键盘输入,保存给文件描述符6
    exec 0<testfile
    count=1
    while read line
    do
            echo "Line #$count: $line"
            count=$[ $count + 1 ]
    done
    exec 0<&6
    #此时文件描述符0定义为从6输入,6是从键盘输入,故下面代码生效
    read -p "Are you done now? " anser
    case $anser in
            Y|y) echo "Goodbye";;
            N|n) echo "Sorry,this is the end.";;
    esac
    [root@tzPC 15Unit]# bash test15.sh
    Line #1: this is the first line.
    Line #2: this is the second line.
    Are you done now? y
    Goodbye

    创建读写文件描述符

    原来testfile文件内容

    [root@tzPC 15Unit]# cat testfile
    this is the first line.
    this is the second line.
    this is the third line.

    运行脚本

    [root@tzPC 15Unit]# cat test16.sh
    #!/bin/bash
    #testing input/ouput file descriptor
    exec 3<>testfile
    #创建文件描述符3用于读写testfile文件
    read line <&3
    echo "Read: $line"
    #读取testfile文件第一行
    echo "This is a test line" >&3
    #此时将This is a test line写入第二行数据

    这里书里提到了一个内部指针,大致意思是当脚本向文件中写入数据时,read命令读了文件第一行数据,指针会指向第二行,这时第二行数据被写入,指针指向第三行,此时read命令读取第三行数据,内部指针就是一个读写顺序指向过程。

    此时testfile文件内容为

    [root@tzPC 15Unit]# cat testfile
    this is the first line.
    This is a test line
    ine.
    this is the third line.

    这里怎么跟我想的不一样??ine.是什么鬼?

    关闭文件描述符

    创建的文件描述符,shell会在脚本退出时自动关闭。

    手动关闭文件描述符3的语法如下

    exec 3>&-

    例子

    badtest.sh:行6: 3: 错误的文件描述符
    [root@tzPC 15Unit]# cat badtest.sh
    #!/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

    可以看到,关闭了文件描述符3,脚本在向他写入数据时会报错。

    列出打开的文件描述符

    lsof命令会列出整个Linux系统打开的所有文件描述符

    他会向非系统管理员用户提供linux系统信息

    位于/usr/sbin目录

    默认显示当前Linux系统上打开的每个文件的有关信息,包括后台运行的所有进程以及登录到系统的所有用户信息。

    最常用的参数

    -p:指定进程ID

    -d:指定要显示的文件描述符编号

    -a:筛选同时满足-p跟-d两个选项的结果

    特殊环境变量$$会显示当前进程ID

    [root@tzPC ~]# lsof -a -p $$ -d 0,1,2
    COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
    bash    42755 root    0u   CHR  136,2      0t0    5 /dev/pts/2
    bash    42755 root    1u   CHR  136,2      0t0    5 /dev/pts/2
    bash    42755 root    2u   CHR  136,2      0t0    5 /dev/pts/2

    各列的含义

    COMMAND 正在运行的命令名的前9个字符
    PID 进程的PID
    USER 进程属主的登录名
    FD 文件描述符号以及访问类型(r代表读,w代表写,u代表读写)
    TYPE 文件类型(CHR代表字符类型,BLK代表块型,DIR代表目录,REG代表常规文件)
    DEVICE 设备的设备号(主设备号和从设备号)
    SIZE 文件大小
    NODE 本地文件节点号
    NAME 文件名

     与STDIN、STDOUT和STDERR关联的文件类型为字符型,因为他们描述符都指向终端,所以文件名就是终端的设备名。

    [root@tzPC 15Unit]# cat test18.sh
    #!/bin/bash
    #testing lsof with file descriptors
    exec 3>test18file1
    exec 6>test18file2
    exec 7<testfile
    
    /usr/sbin/lsof -a -p $$ -d0,1,2,3,6,7
    [root@tzPC 15Unit]# bash test18.sh
    COMMAND   PID USER   FD   TYPE DEVICE SIZE/OFF     NODE NAME
    bash    75272 root    0u   CHR  136,2      0t0        5 /dev/pts/2
    bash    75272 root    1u   CHR  136,2      0t0        5 /dev/pts/2
    bash    75272 root    2u   CHR  136,2      0t0        5 /dev/pts/2
    bash    75272 root    3w   REG  253,0        0 35469258 /root/script/15Unit/test18file1
    bash    75272 root    6w   REG  253,0        0 35469259 /root/script/15Unit/test18file2
    bash    75272 root    7r   REG  253,0       73 35469233 /root/script/15Unit/testfile

    阻止命令输出

    黑洞文件/dev/null

    可以用来快速清除现有文件的数据,而不用删除在创建。

    或用来快速清除日志文件。

    [root@tzPC 15Unit]# ls -al badfile test16 2> /dev/null

    创建临时文件

    1. 临时文件存放目录/tmp
    2. linux启动时会自动删除/tmp目录下所有文件
    3. 任何用户都有权限读写/tmp目录中的文件

    创建本地临时文件

    mktemp命令可以在/tmp目录中创建一个唯一的临时文件

    文件权限为属主,除了root跟属主谁也不能访问

    使用mktemp命令创建临时文件需要在文件名末尾加上6个X

    [root@tzPC 15Unit]# mktemp testing.XXXXXX
    testing.SNK6dI
    [root@tzPC 15Unit]# mktemp testing.XXXXXX
    testing.O8M7X3
    [root@tzPC 15Unit]# mktemp testing.XXXXXX
    testing.sLT37i
    [root@tzPC 15Unit]# mktemp testing.XXXXXX
    testing.KPs2vn
    [root@tzPC 15Unit]# ll testing*
    -rw-------. 1 root root 0 8月  19 23:40 testing.KPs2vn
    -rw-------. 1 root root 0 8月  19 23:40 testing.O8M7X3
    -rw-------. 1 root root 0 8月  19 23:40 testing.sLT37i
    -rw-------. 1 root root 0 8月  19 23:39 testing.SNK6dI

    mktemp命令会用6个字节码替换6个X以此保证文件名唯一存在

    在脚本中使用需把文件名保存到变量中方便引用

    [root@tzPC 15Unit]# cat test19.sh
    #!/bin/bash
    #creating and using a temp file
    tempfile=$(mktemp test19.XXXXXX)
    exec 3>$tempfile
    echo "This script writes to temp file $tempfile"
    echo "This is the first line" >&3
    echo "This is the second line" >&3
    echo "This is the last line" >&3
    exec 3>&-
    echo "Done creating temp file.The contents are:"
    cat $tempfile
    rm -rf $tempfile 2>/dev/null

    在/tmp目录创建临时文件

    mktemp -t参数会在系统临时目录创建临时文件,同时返回创建临时文件的全路径

    [root@tzPC 15Unit]# mktemp -t test.XXXXXX
    /tmp/test.XFSQHD

    因为返回的全路径,可以在linux系统任何目录下引用该临时文件。

    创建临时目录

    mktemp -d参数用来创建临时目录。

    [root@tzPC 15Unit]# cat test21.sh
    #!/bin/bash
    #using a temporary directory
    tempdir=$(mktemp -d dir.XXXXXX)
    echo "创建的临时目录为:`pwd`$tempdir"
    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命令

    将输出同时发送到显示器跟文件

    [root@tzPC 15Unit]# date | tee testfile
    2020年 08月 20日 星期四 00:13:31 CST
    [root@tzPC 15Unit]# cat testfile
    2020年 08月 20日 星期四 00:13:31 CST

    tee -a将数据追加到文件

    [root@tzPC 15Unit]# who | tee -a testfile
    root     tty1         2020-08-16 22:20
    [root@tzPC 15Unit]# cat testfile
    2020年 08月 20日 星期四 00:13:31 CST
    root     tty1         2020-08-16 22:20

    脚本实战

    [root@tzPC 15Unit]# cat test23.sh
    #!/bin/bash
    #read file and create INSERT statements for MySQL
    outfile='members.sql'
    IFS=','
    while read lname fname address city state zip
    do
            cat >> $outfile <<EOF
            INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('$lname','$fname'.'$address','$city','$state','$zip');
    EOF
    
    #cat>>将cat命令的输出追加重定向到$outfile文件,cat命令的输入为后面的数据EOF标记了数据的起始跟结尾
    done < ${1}
    #${1}代表第一个命令行参数,指明要读取的文件

    members.csv文件内容如下

    [root@tzPC 15Unit]# cat members.csv
    Blum,Richard,123 Main St.,Chicago,IL,60601

    运行脚本,这里书中写成./test23 < members.csv 有误,应该如下写法

    [root@tzPC 15Unit]# bash test23.sh members.csv

    查看members.sql文件

    [root@tzPC 15Unit]# cat members.sql
            INSERT INTO members (lname,fname,address,city,state,zip) VALUES ('Blum','Richard'.'123 Main St.','Chicago','IL','60601');

      学习来自:《Linux命令行与Shell脚本大全 第3版》第15章

    今天的学习是为了以后的工作更加的轻松!
  • 相关阅读:
    简易四则运算
    对GitHub的认识
    对‘前辈’们的博文有感
    javascript中的JSON序列化与反序列化
    初步体验javascript try catch机制
    JQuery拾遗
    直面Javascript面试题算法思路
    ECMAScript5下Array的方法
    浅谈javascript中的数据类型和引用类型
    ECMAScript 5.1中对属性的操作
  • 原文地址:https://www.cnblogs.com/tz90/p/13528174.html
Copyright © 2020-2023  润新知