Answers:
#!/bin/bash
而不是开头#!/bin/sh
,因为in需要bash。
do_something 2>&1 | tee -a some_file
这将STDERR重定向到标准输出和标准输出到some_file
并打印到stdout。
do_something &>filename
不是。+1。
Ambiguous output redirect.
知道为什么吗?
$?
不再引用的退出状态do_something
,而是引用的退出状态tee
。
您可以将stderr重定向到stdout,然后将stdout重定向到文件中:
some_command >file.log 2>&1
参见http://tldp.org/LDP/abs/html/io-redirection.html
该格式比仅在bash中起作用的最受欢迎的&>格式更受青睐。在Bourne Shell中,它可以解释为在后台运行命令。同样,格式更易读2(是STDERR)重定向到1(STDOUT)。
编辑:更改顺序,如注释中指出
# Close STDOUT file descriptor
exec 1<&-
# Close STDERR FD
exec 2<&-
# Open STDOUT as $LOG_FILE file for read and write.
exec 1<>$LOG_FILE
# Redirect STDERR to STDOUT
exec 2>&1
echo "This line will appear in $LOG_FILE, not 'on screen'"
现在,简单的回显将写入$ LOG_FILE。对守护有用。
对于原始帖子的作者,
这取决于您需要实现的目标。如果您只需要重定向从脚本中调用的命令,就可以给出答案。我的是关于在当前脚本中进行重定向,这会影响上述代码段之后的所有命令/内置命令(包括forks)。
另一个很酷的解决方案是立即重定向到std-err / out并同时重定向到记录器或日志文件,这涉及将“流”分成两部分。此功能由'tee'命令提供,该命令可以一次写入/附加到多个文件描述符(文件,套接字,管道等):tee FILE1 FILE2 ...>(cmd1)>(cmd2)...
exec 3>&1 4>&2 1> >(tee >(logger -i -t 'my_script_tag') >&3) 2> >(tee >(logger -i -t 'my_script_tag') >&4)
trap 'cleanup' INT QUIT TERM EXIT
get_pids_of_ppid() {
local ppid="$1"
RETVAL=''
local pids=`ps x -o pid,ppid | awk "\\$2 == \\"$ppid\\" { print \\$1 }"`
RETVAL="$pids"
}
# Needed to kill processes running in background
cleanup() {
local current_pid element
local pids=( "$$" )
running_pids=("${pids[@]}")
while :; do
current_pid="${running_pids[0]}"
[ -z "$current_pid" ] && break
running_pids=("${running_pids[@]:1}")
get_pids_of_ppid $current_pid
local new_pids="$RETVAL"
[ -z "$new_pids" ] && continue
for element in $new_pids; do
running_pids+=("$element")
pids=("$element" "${pids[@]}")
done
done
kill ${pids[@]} 2>/dev/null
}
因此,从一开始。假设我们已将终端连接到/ dev / stdout(FD#1)和/ dev / stderr(FD#2)。实际上,它可以是管道,插座或其他任何东西。
运行具有以上一行以及另外一行的脚本的结果:
echo "Will end up in STDOUT(terminal) and /var/log/messages"
...如下:
$ ./my_script
Will end up in STDOUT(terminal) and /var/log/messages
$ tail -n1 /var/log/messages
Sep 23 15:54:03 wks056 my_script_tag[11644]: Will end up in STDOUT(terminal) and /var/log/messages
如果要查看更清晰的图片,请将以下两行添加到脚本中:
ls -l /proc/self/fd/
ps xf
bash your_script.sh 1>file.log 2>&1
1>file.log
指示Shell将STDOUT发送到文件file.log
,并2>&1
告诉它将STDERR(文件描述符2)重定向到STDOUT(文件描述符1)。
注意:如liw.fi所指出的那样,顺序很重要,2>&1 1>file.log
不起作用。
奇怪的是,这有效:
yourcommand &> filename
但这给出了语法错误:
yourcommand &>> filename
syntax error near unexpected token `>'
您必须使用:
yourcommand 1>> filename 2>&1
&>>
似乎可以在BASH 4上使用:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
简短答案:Command >filename 2>&1
或Command &>filename
说明:
考虑以下代码,该代码将单词“ stdout”打印到stdout,将单词“ stderror”打印到stderror。
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
请注意,“&”运算符告诉bash 2是文件描述符(指向stderr),而不是文件名。如果我们省略了“&”,此命令将打印stdout
到stdout,并创建一个名为“ 2”的文件并写入该文件stderror
。
通过试验以上代码,您可以亲自了解重定向运算符的工作方式。例如,通过更改哪个文件,将两个描述符中的哪一个1,2
重定向到/dev/null
以下两行代码,分别删除stdout中的所有内容和stderror中的所有内容(打印剩下的内容)。
$ (echo "stdout"; echo "stderror" >&2) 1>/dev/null
stderror
$ (echo "stdout"; echo "stderror" >&2) 2>/dev/null
stdout
现在,我们可以解释为什么以下代码不产生任何输出的解决方案:
(echo "stdout"; echo "stderror" >&2) >/dev/null 2>&1
为了真正理解这一点,我强烈建议您阅读此页面上的文件描述符表。假设您已完成阅读,我们可以继续进行。请注意,Bash从左到右处理。因此Bash >/dev/null
首先看到(与相同1>/dev/null
),并将文件描述符1设置为指向/ dev / null而不是stdout。完成此操作后,Bash向右移动并看到2>&1
。这会将文件描述符2设置为指向与文件描述符1 相同的文件(而不是文件描述符1本身!!!)(请参阅指针上的此资源有关更多信息))。由于文件描述符1指向/ dev / null,文件描述符2指向与文件描述符1相同的文件,因此文件描述符2现在也指向/ dev / null。因此,两个文件描述符都指向/ dev / null,这就是为什么不呈现任何输出的原因。
要测试您是否真正理解该概念,请尝试在切换重定向顺序时猜测输出:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
标准错误
这里的理由是,从Bash从左到右进行评估,看到2>&1,因此将文件描述符2设置为指向与文件描述符1相同的位置,即stdout。然后,它将文件描述符1(记住> / dev / null = 1> / dev / null)设置为指向> / dev / null,从而删除通常发送到标准输出的所有内容。因此,剩下的就是子shell中没有发送到stdout的内容(括号中的代码),即“ stderror”。需要注意的有趣一点是,即使1只是指向stdout的指针,通过重定向指针2到1 2>&1
并不会形成指针2-> 1-> stdout的链。如果这样做,则由于将1重定向到/ dev / null,所以代码2>&1 >/dev/null
会给指针链2-> 1-> / dev / null,因此与上面看到的相反,代码不会产生任何东西。
最后,我注意到有一种更简单的方法可以做到这一点:
在这里的 3.6.4节中,我们看到可以使用运算符&>
来重定向stdout和stderr。因此,要将任何命令的stderr和stdout输出都重定向到\dev\null
(删除输出),我们只需键入
$ command &> /dev/null
或在我的示例中:
$ (echo "stdout"; echo "stderror" >&2) &>/dev/null
关键要点:
2>&1 >/dev/null
就是!= >/dev/null 2>&1
。一个产生输出,而另一个不产生!最后看看这些出色的资源:
LOG_FACILITY="local7.notice"
LOG_TOPIC="my-prog-name"
LOG_TOPIC_OUT="$LOG_TOPIC-out[$$]"
LOG_TOPIC_ERR="$LOG_TOPIC-err[$$]"
exec 3>&1 > >(tee -a /dev/fd/3 | logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_OUT" )
exec 2> >(logger -p "$LOG_FACILITY" -t "$LOG_TOPIC_ERR" )
与之相关:将stdOut&stderr写入syslog。
它几乎可以工作,但不是从信德()
我想要一个解决方案,将stdout和stderr的输出写入日志文件,并且stderr仍在控制台上。所以我需要通过tee复制stderr输出。
这是我发现的解决方案:
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
对于这种情况,当需要“管道”时,可以使用:
|&
例如:
echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt
要么
TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log ; done |& sort -h
这种基于bash的解决方案可以分别传递STDOUT和STDERR(从“ sort -c”的STDERR或从STDERR到“ sort -h”的管道)。
以下功能可用于自动切换beetwen stdout / stderr和日志文件的输出过程。
#!/bin/bash
#set -x
# global vars
OUTPUTS_REDIRECTED="false"
LOGFILE=/dev/stdout
# "private" function used by redirect_outputs_to_logfile()
function save_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
exec 3>&1
exec 4>&2
trap restore_standard_outputs EXIT
}
# Params: $1 => logfile to write to
function redirect_outputs_to_logfile {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
save_standard_outputs
exec 1>>${LOGFILE%.log}.log
exec 2>&1
OUTPUTS_REDIRECTED="true"
}
# "private" function used by save_standard_outputs()
function restore_standard_outputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
exit 1;
fi
exec 1>&- #closes FD 1 (logfile)
exec 2>&- #closes FD 2 (logfile)
exec 2>&4 #restore stderr
exec 1>&3 #restore stdout
OUTPUTS_REDIRECTED="false"
}
脚本内部用法示例:
echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs
echo "this goes to stdout"
@ fernando-fabreti
除了您所做的之外,我还稍微更改了功能,并删除了&-结束符,它对我有用。
function saveStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
exec 3>&1
exec 4>&2
trap restoreStandardOutputs EXIT
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
exit 1;
fi
}
# Params: $1 => logfile to write to
function redirectOutputsToLogfile {
if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
LOGFILE=$1
if [ -z "$LOGFILE" ]; then
echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
fi
if [ ! -f $LOGFILE ]; then
touch $LOGFILE
fi
if [ ! -f $LOGFILE ]; then
echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
exit 1
fi
saveStandardOutputs
exec 1>>${LOGFILE}
exec 2>&1
OUTPUTS_REDIRECTED="true"
else
echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
exit 1;
fi
}
function restoreStandardOutputs {
if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
exec 1>&3 #restore stdout
exec 2>&4 #restore stderr
OUTPUTS_REDIRECTED="false"
fi
}
LOGFILE_NAME="tmp/one.log"
OUTPUTS_REDIRECTED="false"
echo "this goes to stdout"
redirectOutputsToLogfile $LOGFILE_NAME
echo "this goes to logfile"
echo "${LOGFILE_NAME}"
restoreStandardOutputs
echo "After restore this goes to stdout"