如何在shell脚本中将文本输出到屏幕和文件?


49

目前,我有一个Shell脚本,它将消息记录到这样的日志文件中:

log_file="/some/dir/log_file.log"
echo "some text" >> $log_file
do_some_command
echo "more text" >> $log_file
do_other_command

执行此脚本时,没有输出到屏幕,并且由于我是通过腻子连接到服务器的,因此我必须打开另一个连接并执行“ tail -f log_file_path.log”,因为我无法终止运行脚本,我想实时查看输出。

显然,我想要的是将文本消息打印在屏幕上并记录到文件中,但是我想用一行而不是两行来完成,其中之一没有重定向到文件。

如何实现呢?

Answers:


71

这有效:

command | tee -a "$log_file"

tee将输入保存到文件(用于-a添加而不是覆盖),并将输入也复制到标准输出。


8

您可以使用here-doc和。为其提供有效的,对POSIX友好的通用收集器模型。

. 8<<-\IOHERE /proc/self/fd/8

command
 
fn() { declaration ; } <<WHATEVER
# though a nested heredoc might be finicky
# about stdin depending on shell
WHATEVER
cat -u ./stdout | ./works.as >> expect.ed
IOHERE

当您打开heredoc时,您会用IOHERE输入令牌向外壳发出信号,表示该外壳应将其输入重定向到您指定的文件描述符,直到遇到限制符令牌的另一端为止。我环顾四周,但没有看到如上所示与heredoc运算符结合使用重定向fd号码的许多示例,尽管在POSIX基本shell命令指南中明确指定了它的用法。大多数人只是将其指向stdin并进行射击,但是我发现以这种方式采购脚本小程序可以使stdin不受限制,并且组成应用程序可以避免抱怨阻塞的I / O路径。

Heredoc的内容将流式传输到您指定的文件描述符,然后依次将其解释为shell代码并由。内置的,但并非没有指定的特定路径。。如果/ proc / self路径给您带来麻烦,请尝试/ dev / fd / n或/ proc / $$。顺便说一下,这种方法也适用于管道:

cat ./*.sh | . /dev/stdin

至少看起来像它一样不明智。当然,您可以使用sh进行相同的操作,但是。的目的是在当前的shell环境中执行,这可能是您想要的,并且,取决于您的shell,使用heredoc的可能性要大于使用thisdoc的可能性。使用标准的匿名管道。

无论如何,正如您可能已经注意到的那样,我仍然没有回答您的问题。但是,如果您考虑一下,以Heredoc将所有代码流式传输到.in的相同方式,它还为您提供了一个简单,简单的方法:

. 5<<EOIN /dev/fd/5 |\ 
    tee -a ./log.file | cat -u >$(tty)
script
 
more script
EOIN

因此,您在heredoc中执行的任何代码中的所有终端标准输出都是从中输出的。当然,可以很容易地从一根管子上拉开。我之所以加入无缓冲的cat调用,是因为我不清楚当前的stdout方向,但是它可能是多余的(几乎可以肯定是写的那样),并且管道可能会在tee处结束。

您可能还会在第二个示例中质疑缺少的反斜杠引号。在您进入之前,这部分很重要,需要先了解一下,并且可能会给您一些关于如何使用它的想法。带引号的heredoc限制器(到目前为止,我们已经使用IOHERE和EOIN,而第一个带反斜杠的引号,尽管“单引号”或“双引号”具有相同的作用)将禁止外壳程序在shell上执行任何参数扩展内容,但未引用限制符将使其内容易于扩展。当您的Heredoc是时的结果。引人注目的是:

. 3<<HD ${fdpath}/3
: \${vars=$(printf '${var%s=$((%s*2))},' `seq 1 100`)} 
HD
echo $vars
> 4,8,12 
echo $var1 $var51
> 4 104

因为我没有引用Heredoc限制器,所以在将结果读入文件之前,shell会在读取内容时扩展内容。执行。这实质上导致命令被解析两次-无论如何都是可扩展的。因为我用反斜杠引用了$ vars参数扩展,所以外壳程序在第一次通过时就忽略了它的声明,而只去除了反斜杠,因此当时,整个printf扩展的内容都可以用null求值。在第二遍获得脚本。

该功能基本上正是危险的eval shell内置程序可以提供的功能,即使在引证文件中引用比使用eval更容易处理,并且同样具有危险性。除非您仔细计划,否则最好还是习惯地引用“ EOF”限制器。只是说。

编辑:嗯,我回头看这个,以为这有点过分了。如果您需要做的只是将多个输出连接到一个管道中,那么最简单的方法就是使用:

{ or ( command ) list ; } | tee -a ea.sy >>pea.sy

curlies将尝试在当前shell中运行内容,而parens会自动分出。不过,任何人都可以告诉您,至少在我看来,。Heredoc解决方案是更有价值的信息,尤其是如果您想了解Shell的实际工作方式时。

玩得开心!


3

我在寻找修改安装脚本以编写安装日志时找到了这个答案。

我的脚本已经充满了echo语句,例如:

echo "Found OLD run script $oldrunscriptname"
echo "Please run OLD tmunsetup script first!"

而且我不想让tee语句运行它(或另一个脚本来使用tee调用现有脚本),所以我这样写:

#!/bin/bash
# A Shell subroutine to echo to screen and a log file

LOGFILE="junklog"

echolog()
(
echo $1
echo $1 >> $LOGFILE
)


echo "Going"
echolog "tojunk"

# eof

因此,现在在我的原始脚本中,我只需将“ echo”更改为“ echolog”即可在日志文件中输出。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.