使用重定向呈现数据

输入和输出信息

1713862013324.png

Linux特殊的文件描述符:

文件描述符 缩写 描述
0 STDIN 标准输入文件
1 STDOUT 标准输出文件
2 STDERR 标准错误文件
  • 这三个特殊文件描述符会处理脚本的输入和输出。shell用它们将shell默认的输入和输出导向到相应的位置。
  1. STDIN(标准输入)

    对终端界面来说,标准输入文件对应的资源是键盘。

    当在命令行上只输入 cat 命令时,它会从文件描述符 0 接收输入。输入一行内容,cat 命令就会显示出一行完全相同的内容。

    ubuntu@sh:~$ cat
    Hello World
    Hello World
  2. STDOUT(标准输出)

    程序会将正确信息导向文件描述符 1,默认情况下文件描述符 1 和标准输出文件关联,信息最终会被输出到显示器中。

    ubuntu@sh:~$ date > test3
    ubuntu@sh:~$ cat test3
    2023年 03月 26日 星期日 19:23:54 PDT
  3. STDERR(标准错误)

    程序会将错误信息导向文件描述符2,默认情况下文件描述符2和标准错误文件关联,信息最终同样会被输出到显示器中。

    ubuntu@sh:~$ ls badfile >test4
    ls: 无法访问 'badfile': 没有那个文件或目录 (No such file or directory)
    ubuntu@sh:~$ cat test4
    # 标准错误重定向使用的是'2>'
    ubuntu@sh:~$ ls badfile 2>test4
    ubuntu@sh:~$ cat test4
    ls: 无法访问 'badfile': 没有那个文件或目录 (No such file or directory)
    ubuntu@sh:~$

输入重定向

输入重定向和输出重定向正好相反。输入重定向将文件的内容重定向到命令,而非将命令的输出重定向到文件。输入重定向符号是小于号(<)

ubuntu@sh:~$ cat test6
Sun Mar 26 19:23:54 PDT 2023
ubuntu   tty1         2023-03-26 19:01
ubuntu   pts/0        2023-03-26 19:01 (192.168.13.1)
ubuntu@sh:~$ wc < test6
  3  15 122
ubuntu@sh:~$

wc命令可以对对数据中的文本进行计数。默认情况下,它会输出3个值:文本的行数,文本的词数,文本的字节数。

输出重定向

ubuntu@sh:~$ date > test6
ubuntu@sh:~$ ls -l test6
-rw-rw-r-- 1 ubuntu ubuntu 29 Mar 26 19:23 test6
ubuntu@sh:~$ cat test6
Sun Mar 26 19:23:54 PDT 2023
ubuntu@sh:~$

作业1:理解并执行上述输入重定向、输出重定向的使用案例。

重定向操作符创建了一个文件 test6(通过默认的 umask设置),并将 date命令的输出重定向到该文件中。如果输出文件已经存在了,重定向操作符会用新的文件数据覆盖已有文件。

有时,可能并不想覆盖文件原有内容,而是想要将命令的输出追加到已有文件中,比如正在创建一个记录系统上某个操作的日志文件。在这种情况下,可以用双大于号 (>>)来追加数据。

ubuntu@sh:~$ who >> test6
ubuntu@sh:~$ cat test6
Sun Mar 26 19:23:54 PDT 2023
ubuntu   tty1         2023-03-26 19:01
ubuntu   pts/0        2023-03-26 19:01 (192.168.13.1)
ubuntu@sh:~$

test6文件在原来who命令输出的基础上又加上了date命令的输出

错误重定向

  • 只重定向错误信息

    STDERR文件描述符被设成2。可以选择只重定向错误消息,将该文件描述符值放在重定向符号前。

    ubuntu@sh:~$ ls -al badfile 2> test3
    ubuntu@sh:~$ cat test3
    ls: 无法访问 'badfile': 没有那个文件或目录 (No such file or directory)
    ubuntu@sh:~$
  • 重定向错误和正常输出

    如果想重定向错误和正常输出,必须用两个重定向符号。需要在符号前面放上待重定向数据所对应的文件描述符,然后指向用于保存数据的输出文件。

    ubuntu@sh:~$ ls -al test3 test6 badtest 2>test7 1>test8
    ubuntu@sh:~$ cat test7
    ls: 无法访问 'badtest': 没有那个文件或目录 (No such file or directory)
    ubuntu@sh:~$ cat test8
    -rw-rw-r-- 1 ubuntu ubuntu  55 Mar 26 20:24 test3
    -rw-rw-r-- 1 ubuntu ubuntu 122 Mar 26 19:27 test6
    ubuntu@sh:~$

    shell利用1>符号将ls命令的正常输出重定向到了test8文件,而这些输出本该是进入STDOUT的。所有本该输出到STDERR的错误消息通过2>符号被重定向到了test7文件。

    可以用这种方法将脚本的正常输出和脚本生成的错误消息分离开来。这样就可以轻松地识别出错误信息。另外也可以将STDERRSTDOUT的输出重定向到同一个输出文件。为此bash shell提供了特殊的重定向符号&>

    ubuntu@sh:~$ ls -al badtest test3 test6 badtest &> test7
    ubuntu@sh:~$ cat test7
    ls: 无法访问 'badtest': 没有那个文件或目录 (No such file or directory)
    ls: 无法访问 'badtest': 没有那个文件或目录 (No such file or directory)
    -rw-rw-r-- 1 ubuntu ubuntu  55 Mar 26 20:24 test3
    -rw-rw-r-- 1 ubuntu ubuntu 122 Mar 26 19:27 test6

在脚本中重定向输出

有两种方法来在脚本中重定向输出:

  1. 临时重定向行输出
  2. 永久重定向脚本中的所有命令

临时重定向

如果有意在脚本中生成错误消息,可以将单独的一行输出重定向到STDERR。所需要做的是使用输出重定向符>来将输出信息重定向到STDERR文件。在重定向到文件描述符时,必须在文件描述符数字之前加一个&:

ubuntu@sh:~$ cat test.sh
#!/bin/bash

echo "这是一个错误信息" >&2
echo "这是正常输出"

ubuntu@sh:~$ bash test.sh 2> test9
这是正常输出
ubuntu@sh:~$ cat test9
这是一个错误信息

作业2:理解并执行脚本中实现临时输出重定向的方法。

永久重定向

如果脚本中有大量数据需要重定向,那么重定向每一行 echo 语句就会很繁琐。可以使用 exec 命令告诉 shell 在脚本执行期间重定向某个特定文件描述符。

#!/bin/bash
# 演示永久重定向:将脚本内所有标准输出重定向到标准错误
exec 1>&2

echo "[运行中] 这是一个将所有输出重定向到外部文件的测试"
echo "[运行中] 正在处理数据..."
echo "[完成] 所有输出已成功发送到重定向目标,无需逐行手动重定向"
ubuntu@sh:~$ bash test.sh
[运行中] 这是一个将所有输出重定向到外部文件的测试
[运行中] 正在处理数据...
[完成] 所有输出已成功发送到重定向目标,无需逐行手动重定向
  • 脚本执行过程中切换重定向目标:

    #!/bin/bash
    # 演示在脚本执行过程中动态重定向输出到不同位置
    
    # 1. 首先将错误信息重定向到 testerror 文件
    exec 2>testerror
    echo "[开始] 脚本启动,准备执行测试"
    
    # 2. 将后续的所有标准输出重定向到 testout 文件
    echo "[配置] 现在开始将所有标准输出导向至 testout 文件"
    exec 1>testout
    
    echo "[输出] 这行文字会被保存到 testout 文件中"
    echo "[输出] 正在生成报告数据..."
    echo "[输出] 但这行信息会被导向到标准错误(testerror 文件)" >&2

作业1:理解并执行上述脚本内实现永久输出重定向的方法。

在脚本中重定向输入

可以使用与脚本中重定向STDOUT和STDERR相同的方法来将STDIN从键盘重定向到其他位置。exec命令允许将STDIN重定向到Linux系统上的文件中。这个重定向只要在脚本需要输入时就会作用

#!/bin/bash
# 演示在脚本中重定向文件输入
# 将标准输入 STDIN 重定向到外部文件 testfile
exec 0< testfile

count=1
# 现在 read 命令将从 testfile 逐行读取内容
while read line
do
    echo "[读取中] 第 $count 行内容为: $line"
    count=$((count + 1))
done

创建自定义的文件描述符

创建并打开输出文件描述符

在脚本中重定向输入和输出时,并不局限于这 3 个默认的文件描述符。在 shell 中最多可以有 9 个打开的文件描述符(3~8 均可用)。

#!/bin/bash
# 演示分配自定义文件描述符 3 为输出流
exec 3>test3out

echo "[提示] 这条消息将直接显示在屏幕终端上"
echo "[文件写入] 而这条消息将被存储到自定义文件 test3out 中" >&3
echo "[提示] 完成写入,现在回到屏幕输出"

创建并打开输入文件描述符

同样可以重定向自定义的输入文件描述符。一种常见的技巧是:在重定向到文件之前,先将默认的 STDIN (0) 备份到另一个空闲的文件描述符中,处理完文件后再将其恢复回来。

#!/bin/bash
# 演示使用自定义文件描述符 6 备份并恢复标准输入

# 1. 将自定义描述符 6 设为标准输入(备份)
exec 6<&0

# 2. 将标准输入重定向到 testfile 执行批量读取
exec 0< testfile
count=1
while read line
do
    echo "[读取中] 第 $count 行: $line"
    count=$((count + 1))
done

# 3. 恢复标准输入为键盘,通过 6 号描述符导回
exec 0<&6
read -p "[交互] 您已完成文件读取,是否结束程序? (y/n) " answer
case $answer in
Y|y) echo "Goodbye";;
N|n) echo "Sorry, this is the end.";;
esac

作业2:脚本内创建输入文件描述符并实现输入重定向的代码。

组织命令输出

有时候,可能不想显示脚本的输出。这在将脚本作为后台进程运行时很常见。可以将STDERR重定向到一个叫作null文件的特殊/文件。shell输出到null文件的任何数据都不会保存,全部都被丢掉了。在Linux系统上null文件的标准位置是/dev/null。你重定向到该位置的任何数据都会被丢掉,不会显示。

ubuntu@sh:~$ ls -al > /dev/null
ubuntu@sh:~$
ubuntu@sh:~$ ls -al badfile 2> /dev/null
ubuntu@sh:~$