我的脚本如下:
#!/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 楼
通常,我会在每个脚本的开头加上以下内容(特别是如果它将作为守护程序运行):#!/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
评论
您也可以使用“脚本”执行此操作。看到这篇文章:[这里] [1] [1]:stackoverflow.com/questions/5985060 / ...您必须谨慎使用errexit-我注意到它被忽略或错误不会退出脚本。如果脚本中包含以下内容:command ||命令失败时的dosmthg脚本不会在设置errexit的情况下立即退出,而只是运行dosmthg并继续执行脚本