📜  Shell 脚本——标准输入、输出和错误

📅  最后修改于: 2022-05-13 01:57:34.656000             🧑  作者: Mango

Shell 脚本——标准输入、输出和错误

在 Linux 应用程序上工作,我们有几种方法可以从外部获取信息并将其放入内部:命令行参数、环境变量、文件。所有这些来源都是合法且良好的。但它的大小是有限的。建立通信的另一种方式是标准流:输入流 ( stdin ) 用于从应用程序外部获取数据,输出流 ( stdout ) 用于将数据放在应用程序外部,错误将数据放在应用程序外部 ( stderr )。

sh 上的标准 Linux 应用程序流

每个流都像一个管道:它具有相同的缓冲区来写入和读取数据。此缓冲区可用于从一个应用程序读取,也可用于从另一个应用程序写入。读取时,占用的缓冲区大小将减少,写入时会增加。如果读取和写入的平均速率相等,那么通过流传递的数据可以是任意数量的字节长。

示例中运行的输入/输出流

让我们看一个描述它如何与之交互的“grep”应用程序的例子。

脚本主体:

#!/usr/bin/env bash
echo pattern | grep pattern
echo pattern | grep pattern

在控制台运行的脚本:

黄框中的脚本体,下一个输出

这次我们运行了 2 个命令:“cat stdio1.sh”——列出脚本主体,“./stdio1.sh”运行脚本。 “回声模式”形成连接到grep标准输入流的标准输出流。所以所有的数据都到了grep。 grep是一个应用程序,如果它匹配模式,它会从输入流写入输出流。第一个输入不匹配 - 没有预期的输出。脚本的第二个字符串“echo pattern | grep 模式”具有适当的输入 - 它已被传递到grep输出并在接下来的控制台中显示。

示例中运行的错误流

Stderr与 stdout 非常相似:只有一个区别——如果它占位,它将包含一个错误报告

脚本主体:

#!/usr/bin/env bash

ls > /tmp/log
cat /tmp/log

ls /not/existed_path > /tmp/log
cat /tmp/log
cat /tmp/log

rm /tmp/log

在控制台运行的脚本:

黄框中的脚本体,下一个输出

在这个脚本中,我们列出当前目录(“ls”)中的文件,并将标准输出重定向到文件(“/tmp/log”)。然后我们从读取文件的cat实用程序的标准输出流在控制台上显示此文件数据。接下来,我们尝试列出不存在的文件夹中的文件并将结果存储到文件中,并显示两次。但是没有效果。只有一个关于控制台中错误的输出。那是因为这个错误来自ls标准错误流。我们删除了文件以清除一些空间“ /tmp/log ”。

流重定向

可以操纵标准流:重定向它们或使用管道来处理它们。

脚本主体:

#!/usr/bin/env bash

ls /not/existed_path 2>/dev/null
ls * /not/existed_path > /tmp/log 2> /tmp/err_log
cat /tmp/log
rm /tmp/log /tmp/err_log

在控制台运行的脚本:

黄框中的脚本体,下一个输出

我们在此命令中使用“ > ”重定向操作。 /dev/null – 是一个流终止文件,用于删除任何输入而不进行处理。 “ 2> ”子句将标准输出流重定向到文件。现在可以通过调用“cat /tmp/log”“cat /tmp/err_log”来查看输出和错误。

丢弃输出

在某些情况下,删除所有应用程序输出(以节省存储空间或删除要分析的不重要的数据源)很有用。我们应该记住,我们不仅应该静音标准输出流,还应该静音标准错误流。让我们看一个例子来说明它是如何工作的。

脚本主体:

#!/usr/bin/env bash

ls . unexisted_file > /dev/null
ls . unexisted_file > /dev/null 2>&1

在控制台运行的脚本:

黄框中的脚本体,下一个输出

在两行脚本标准输出流中重定向到文件/dev/null 。该文件本身不是文件:而是具有文件接口的特殊 Linux 设备。它有一个可从 shell 获得的写入函数(什么都不做)。所以所有的输入都被丢弃了。但是脚本的第二个字符串与第一个字符串有区别:命令末尾的“ 2>&1 ”。这些符号告诉第二个标准流标准错误流 此命令需要永久指向第一个标准流(标准输出流)。这就是为什么我们在控制台中只看到一条错误消息 - 在脚本标准错误流的第二个命令字符串最终重定向到终止文件设备/dev/null

流水线流

修改流也很有用。能够运行由流连接的多个应用程序。

脚本主体:

#!/usr/bin/env bash

touch /tmp/a
ls /tmp/* 2> /dev/null | grep -w a
rm /tmp/a

在控制台运行的脚本:

黄框中的脚本体,下一个输出

这里我们收集列表并使用“ | ”将“ ls标准输出流设置为grep标准输入流。 ”。在“ 2> ”上,标准错误流被重定向到终止文件。 grep 的输出包含列表中与 grep 模式匹配的“ /tmp/a ”文件名。

这里文件

这里的文档是重定向选项,以信息原生方式填充应用程序的输入流:

application << delimiter
some useful text
delimiter

脚本主体:

#!/usr/bin/env bash

grep perfect << EOF
    PERFECT STORY
    past perfect
    is it Perfect?
EOF

在控制台运行的脚本

黄框中的脚本体,下一个输出

正如我们看到的那样,由“EOF”分隔的grep 标准输入流的 3 个文本字符串中只有第二个字符串与grep模式匹配:这就是它传递给grep 标准输出流并接下来显示在控制台中的原因。

结论

有几个标准实用程序可以处理流( catcutgrepsedtr ……)。您可以使用它或自己编写一个数据流处理管道,以满足您的需求。在引擎盖下,它将与stdinstdoutstderr一起使用。