从我的脚本输出中,我想捕获所有带有错误消息的日志数据,并将它们全部重定向到日志文件。

我的脚本如下:

#!/bin/bash
(
echo " `date` : part 1 - start "
ssh -f admin@server.com 'bash /www/htdocs/server.com/scripts/part1.sh logout exit'
echo " `date` : sleep 120"
sleep 120
echo " `date` : part 2 - start"
ssh admin@server.com 'bash /www/htdocs/server.com/scripts/part2.sh logout exit'
echo " `date` : part 3 - start"
ssh admin@server.com 'bash /www/htdocs/server.com/scripts/part3.sh logout exit'
echo " `date` : END"
) | tee -a /home/scripts/cron/logs


我想查看文件/home/scripts/cron/logs中的所有动作

但是我仅查看我在echo命令后输入的内容。

如何检入SSH命令成功的日志?

我需要收集所有日志。我需要它来监视脚本中每个命令的结果,以便更好地分析脚本失败时的情况。

评论

您也可以使用“脚本”执行此操作。看到这篇文章:[这里] [1] [1]:stackoverflow.com/questions/5985060 / ...

您必须谨慎使用errexit-我注意到它被忽略或错误不会退出脚本。如果脚本中包含以下内容:command ||命令失败时的dosmthg脚本不会在设置errexit的情况下立即退出,而只是运行dosmthg并继续执行脚本

#1 楼

通常,我会在每个脚本的开头加上以下内容(特别是如果它将作为守护程序运行):

#!/bin/bash
exec 3>&1 4>&2
trap 'exec 2>&4 1>&3' 0 1 2 3
exec 1>log.out 2>&1
# Everything below will go to the file 'log.out':


说明:



exec 3>&1 4>&2

保存文件描述符,以便可以将它们恢复到重定向之前的状态,或用于在重定向后将其自身输出到以前的状态。


trap 'exec 2>&4 1>&3' 0 1 2 3

恢复特定信号的文件描述符。通常不需要,因为应该在子外壳程序退出时将其还原。


exec 1>log.out 2>&1

stdout重定向到文件log.out,然后将stderr重定向到stdout。请注意,当您希望它们转到同一文件时,顺序很重要。在将stdout重定向到stderr之前,必须先重定向stdout。从那时起,要查看控制台上的输出(也许),您可以简单地重定向到&3。例如,在执行上述第3行之前,

echo "$(date) : part 1 - start" >&3


将转到stdout所指向的位置(大概是控制台)。

评论


<< niceroot,谢谢!我在脚本的开头添加了三行(exec,trap,exec),并且能够同时获取stdout和stderr。但是一个问题:我已经看到“文件描述符3(pipe:XXX)在some_command上泄漏” ...请让我知道如何避免出现这些错误。我在自定义面板中使用基于busybox的sh。

– Kumar
2011-11-16 15:52

伟大的功夫在这里! -更好的方法是使用陷阱'exec 2>&4 1>&3'0 1 2 3 RETURN。每次使用.shell执行shell函数或脚本时,RETURN伪sigspec恢复文件描述符。或源内置命令执行完毕。由于这个原因(以及其他原因),我在脚本的最后两行中添加:return&exit 0-仅需2美分,@ nicerobot恭喜您!

– DavAlPi
2015年5月5日15:25

通过shell的全局变量记录shell脚本的细节很多。我们可以在shell脚本中模拟类似的日志记录:cuberace.com/2016/03/efficiency-logging-mechnism-in-shell.html文章详细介绍了诸如INFO,DEBUG和ERROR的日志级别。跟踪详细信息,例如脚本入口,脚本出口,函数入口,函数出口。

– Piyush Chordia
16 Mar 10 '16 at 4:56

@PiyushChordia链接修复:cubicrace.com/2016/03/efficiency-logging-mechnism-in-shell.html

–警惕
18年5月17日下午4:27

陷阱'exec 2>&4 1>&3'0 1 2 3 15会更好吗?它处理SIGTERM

–丹尼尔·林(Yaniel Lin)
20-2-21在1:42

#2 楼

要将ssh输出获取到日志文件,必须将stderr重定向到stdout。您可以通过在bash脚本后附加2>&1来做到这一点。

它应如下所示:

#!/bin/bash
(
...
) 2>&1 | tee ...


不显示消息时以正确的顺序,尝试添加另一个子外壳:

#!/bin/bash
((
...
) 2>&1) | tee ...


评论


这很好。实际上,您可以使用以下命令来引导stdout和stderr:(...)|&tee output.log

– Noam Manos
19年1月10日在16:37

#3 楼

在阅读您的问题时,您不想记录输出,但是整个命令序列(在这种情况下,其他答案都将无济于事)。

使用-x调用Shell脚本输出所有内容:

sh -x foo.sh

使用以下命令登录到所需文件:

sh -x foo.sh >> /home/scripts/cron/logs

评论


为-x选项+1

– Coc
13年1月26日在11:35

Dunno为什么使用bash,但是我只能使用以下命令将'set -x'输出输出到日志文件:bash -x foo.sh&> loggingfile.log

–杰西·阿德尔曼(Jesse Adelman)
20年7月9日在22:39



@JesseAdelman是因为xtrace将所有内容输出到sterr,所以您需要重定向stderr和stdout,这是&>的作用。

–迈克尔·多斯特(Michael Dorst)
20 Sep 10 '23:48

#4 楼

在bash中,您可以放置​​set -x,它将在其后打印出它执行的每个命令(以及bash变量)。您可以使用set +x将其关闭。

如果您想变得偏执,可以在脚本中放入set -o errexit。这意味着如果一个命令返回一个非零的退出代码,脚本将失败并停止,这是Unix表示发生问题的标准方法。

如果要获取更好的日志,应该请查看ts debian / ubuntu软件包中的moreutils。它将为每行加上时间戳作为前缀并打印出来。这样您就可以看到事情发生的时间。

评论


“ set -o errexit”必须与“ set -e”相同

–阿吉斯·安东尼(Ajith Antony)
16年7月20日在23:26



#5 楼

遵循别人的说法,手册是一个很好的资源。我输入:

#!/usr/bin/env bash
exec 1> command.log 2>&1
set -x


我希望继续执行脚本的顶部,或者set -ex如果它应在出现错误时退出。

评论


这对ssh日志和成功执行命令有什么帮助?

– asktyagi
19年6月2日在11:30

使用OP的伪脚本进行测试,它会记录每个命令以及任何结果:ssh超时,不正确的域等。或者对于有效的ssh地址,它将命令记录在远程shell中。如果需要有关ssh登录的更多信息,也许是ssh -vv?

–dragon951
19年6月3日在6:53



#6 楼

我发现@nicerobot(如何完整记录所有bash脚本操作?)答案可能不足以将所有输出完全重定向到控制台。某些输出仍然会丢失。

完整的重定向如下所示:

#!/bin/bash

# some basic initialization steps including `NEST_LVL` and `SCRIPTS_LOGS_ROOT variables...
source ".../__myinit__.sh"

# no local logging if nested call
(( ! IMPL_MODE && ! NEST_LVL )) && {
  export IMPL_MODE=1
  exec 3>&1 4>&2
  trap 'exec 2>&4 1>&3' EXIT HUP INT QUIT RETURN

  [[ ! -e "${SCRIPTS_LOGS_ROOT}/.log" ]] && mkdir "${SCRIPTS_LOGS_ROOT}/.log"

  # RANDOM instead of milliseconds
  case $BASH_VERSION in
    # < 4.2
    [123].* | 4.[01] | 4.0* | 4.1[^0-9]*)
      LOG_FILE_NAME_SUFFIX=$(date "+%Y'%m'%d_%H'%M'%S''")$(( RANDOM % 1000 ))
      ;;
    # >= 4.2
    *)
      printf -v LOG_FILE_NAME_SUFFIX "%(%Y'%m'%d_%H'%M'%S'')T$(( RANDOM % 1000 ))" -1
      ;;
  esac

  (
  (
    myfoo1
    #...
    myfooN

    # self script reentrance...
    exec q4312078q "$@"
  ) | tee -a "${SCRIPTS_LOGS_ROOT}/.log/${LOG_FILE_NAME_SUFFIX}.myscript.log" 2>&1
  ) 1>&3 2>&4

  exit $?
}

(( NEST_LVL++ ))

# usual script body goes here...


解释:


仅重定向输出是不够的,因为您仍然必须将内部echo调用都重定向到控制台和文件,因此pipe-plus-tee是拆分输出流的唯一方法。
我们必须使用(...)运算符将每个流重定向到一个独立文件中,或者通过单独的步骤将所有流恢复到原始状态。第二个原因是因为myfoo在自我重入之前必须按原样工作,而无需任何其他重定向。级通话。因此,我们使用NEST_LVL变量来指示嵌套调用级别。
由于重定向可以在外部执行,而与脚本无关,因此我们必须通过IMPL_MODE变量显式启用内部重定向。
我们必须使用日期时间值,以在每次运行脚本时自动创建一个新的唯一日志文件。


#7 楼

TL; DR-昨天我写了一套用于记录程序运行和会话的工具。

当前可在https://github.com/wwalker/quick-log

获得作为管理员,我一直想记录一些命令的输出,通常不是脚本。为了解决这个问题,我写了几件事。最简单的方法是使用程序“ script”作为xX0v0Xx提及。我发现调用脚本(不带任何参数)通常会导致我覆盖先前脚本的输出。所以我创建了这个别名。它所做的只是防止覆盖。您需要〜/ tmp目录。

$ alias scr='script ~/tmp/typescript-$(date +%FT%T)'
$ scr
Script started, file is /home/wwalker/tmp/typescript-2019-12-05T18:56:31
$


当我想捕获一个交互式会话时,这很棒。

当我想登录时命令(脚本或二进制)的输出,我要么想要确切的输出,要么我想要每行前面带有时间戳的输出。因此,我编写了以下两个bash函数:

alias iso8601="date +%Y-%m-%dT%H:%M:%S"

justlog(){
  name=$(basename "")
  log=~/logs/${name}-$(iso8601)
  "$@" > "$log" 2>&1
}

timelog(){
  name=$(basename "")
  log=~/logs/${name}-$(iso8601)
  # https://github.com/wwalker/ilts
  # You could replace ilts with ts
  # /usr/bin/ts %FT%H:%M:%.S
  "$@" |& ilts -S -E > "$log" 2>&1
}


就像平常一样运行命令:

justlog run rcn rcn-work\\\* 'ps -ef -o lstart,cmd | grep [s]upervisor'


or

timelog run rcn rcn-work\\\* 'ps -ef -o lstart,cmd | grep [s]upervisor'


这只是创建了2个文件,名为:

wwalker@polonium:~ ✓ $ ls -l ~/logs/run*
-rw-r--r-- 1 wwalker wwalker 10495 2019-12-05 18:21:14.985 /home/wwalker/logs/run-2019-12-05T18:21:13
-rw-r--r-- 1 wwalker wwalker  1694 2019-12-05 18:24:02.878 /home/wwalker/logs/run-2019-12-05T18:24:01
-rw-r--r-- 1 wwalker wwalker  7623 2019-12-05 18:25:07.873 /home/wwalker/logs/run-2019-12-05T18:25:06
-rw-r--r-- 1 wwalker wwalker 10296 2019-12-05 18:34:59.546 /home/wwalker/logs/run-2019-12-05T18:34:57


但是,还有更多!

我不想做ls来查找justlog或timelog为我创建的日志文件的名称。因此,我增加了3个功能:

newestlog(){
  name=$(basename "")
  ls  ~/logs/"${name}"* | tail -1
}

viewlog(){
  name=$(basename "")
  view $( newestlog "$name" )
}

lesslog(){
  name=$(basename "")
  less $( newestlog "$name" )
}


因此,您使用justlog(或timelog)运行命令,然后只使用lesslog或viewlog(我将可能会为那些人创建一个emacs日志):

justlog run rcn rcn-work\\\* 'ps -ef -o lstart,cmd | grep [s]upervisor'
lesslog run


就这样,没有ls ~/tmp,没有制表符补全游戏来查找文件名。只需运行lesslog(或者如果喜欢使用vim查看日志,则运行viewlog)。

但是,等一下!还有更多!

“我一直在日志文件中使用grep”-答案是,您猜对了,greplog

首先,从所有800个文本中获取文本服务器的/etc/cron.d/atop文件已损坏:

justlog fordcrun 'cat /etc/cron.d/atop; md5dum /etc/cron.d/atop'


然后使用以下命令获取主机名(在文件输出上方的行:-) greplog:

wwalker@polonium:~ ✓ $ greplog fordcrun  -B1 -F "0 0 * * root"
int-salt-01:
    0 0 * * root systemctl restart atop
--
rcn-pg-01:
    0 0 * * root systemctl restart atop
rcn-pg-02:
    0 0 * * root systemctl restart atop
rcn-pg-03:
    0 0 * * root systemctl restart atop


#8 楼

您只需使用“脚本”即可。
man script了解详细信息。
示例:
script -a -e -c "set -x; echo this is logging ..; <your script can be called here>" mylogfile.log


评论


如何在bash脚本中使用脚本?它似乎在调用脚本后立即终止。

–迈克·劳伦斯(Mike Lawrence)
20/11/11在13:11