[英]Output stdout and stderr to file and screen and stderr to file in a limited environment
The problem could be probably fixed using mkfifo
, but it doesn't exist on my QNAP.该问题可能可以使用mkfifo
解决,但在我的 QNAP 上不存在。 So, here is the description of problem and what I tried so far.所以,这里是问题的描述以及我到目前为止所尝试的内容。
I have a function called activateLogs
that restarts the script if writing logs to disk or both (screen and disk).我有一个名为activateLogs
的 function 如果将日志写入磁盘或两者(屏幕和磁盘),它会重新启动脚本。 Both option is the new functionality I would like to achieve.这两个选项都是我想要实现的新功能。
exec 3<> "$logPath/$logFileName.log"
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
This piece of code is the version that writes to disk.这段代码是写入磁盘的版本。 mainArgs
contains all the arguments passed to the script and is defined out of this function. mainArgs
包含传递给脚本的所有 arguments,并在此 function 中定义。 This solution came from https://stackoverflow.com/a/45426547/214898 .该解决方案来自https://stackoverflow.com/a/45426547/214898 。 It combines stderr & stdout in a file and still output stderr in another.它将 stderr 和 stdout 组合在一个文件中,而 output stderr 在另一个文件中。
So, now, I would like to be able to keep this and add printing stderr & stdout to screen.所以,现在,我希望能够保留它并将打印标准错误和标准输出添加到屏幕。
The accepted solution from the question linked above cannot be applied because the script is running using sh
and mkfifo
is not present.无法应用上面链接的问题中接受的解决方案,因为脚本正在使用sh
运行并且mkfifo
不存在。
Attempt #1尝试#1
exec 3>&1
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
--> to replace the code above and in a if
branching (that already existed) I added the tee
command. --> 替换上面的代码并在if
分支(已经存在)中添加了tee
命令。
local isFileDescriptor3Exist=$(command 2>/dev/null >&3 && echo "Y")
if [ "$isFileDescriptor3Exist" = "Y" ]; then
tee -a "logs/123.log" &
echo "Logs are configured"
else
### CODE ABOVE
fi
I have the screen, the error file, but the log file is empty.我有屏幕,错误文件,但日志文件是空的。
Attempt #2 Now, no tee in the if
branching above, but included in the relaunching command.尝试 #2现在,上面的if
分支中没有 tee,但包含在重新启动命令中。
exec 3>&1
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 3>&1 | tee -a "logs/123.log" &
Same result.结果相同。 I may understand in this one that the first tee
not initially writing to the file descriptor #3, thus, the 3>&1
does nothing.我可能在这个中理解第一个tee
最初没有写入文件描述符#3,因此3>&1
什么都不做。
Attempt #3 (No more relaunching the script)尝试 #3 (不再重新启动脚本)
out="${TMPDIR:-/tmp}/out.$$"
err="${TMPDIR:-/tmp}/err.$$"
busybox mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a "$logPath/$logFileName.log" &
tee -a "$logPath/$logFileName.err" < "$err" >&2 &
command >"$out" 2>"$err"
I am getting mkfifo: applet not found
from busybox
我收到 mkfifo:从busybox
mkfifo: applet not found
Attempt #4 (No more relaunching the script)尝试 #4 (不再重新启动脚本)
out="${TMPDIR:-/tmp}/out.$$"
err="${TMPDIR:-/tmp}/err.$$"
python -c "import os; os.mkfifo(\"$out\")"
python -c "import os; os.mkfifo(\"$err\")"
trap 'rm "$out" "$err"' EXIT
tee -a "$logPath/$logFileName.log" &
tee -a "$logPath/$logFileName.err" < "$err" >&2 &
command >"$out" 2>"$err"
I have no logs (neither "real" logs" nor errors). The temporary files are deleted though. Moreover, the script never ends which is caused by trap
.我没有日志(“真实”日志“也没有错误)。临时文件被删除了。此外,脚本永远不会结束,这是由trap
引起的。
Attempt #5尝试#5
exec 3>&1
{ "$0" "${mainArgs[@]}" | tee -a "$logPath/$logFileName.log"; } 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" &
exit
That solution seemed promising, but now relaunching the script doesn't work because my code detects if currently executing and stop it.该解决方案似乎很有希望,但现在重新启动脚本不起作用,因为我的代码检测当前是否正在执行并停止它。 This is normal because it is executed in subprocess even though I use &
at the end of the full line, but... (testing while writing).这是正常的,因为即使我在整行末尾使用&
,它也会在子进程中执行,但是......(在编写时进行测试)。 Replacing the terminator ;
更换终结器;
by &
fixed it.通过&
修复它。
Now I have stdout & stderr to one file & to screen plus stderr to a separate file.现在我将 stdout 和 stderr 放到一个文件中,并将 stderr 放到一个单独的文件中。
Final version working最终版本工作
exec 3>&1
{ "$0" "${mainArgs[@]}" | tee -a "$logPath/$logFileName.log" & } 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" &
exit
Incredible how my first attempt was so close to the solution.令人难以置信的是,我的第一次尝试是如此接近解决方案。
Maybe I'm not reading the question correctly, but it seems that you could do something like:也许我没有正确阅读问题,但似乎您可以执行以下操作:
#!/bin/sh
exec 3>&1
cmd (){
echo stdout;
echo stderr >&2;
}
stderr_outfile=outfile
if test -n "$log_stdout"; then
stdout_outfile=$log_stdout
else
stdout_outfile=/dev/null
fi
{ cmd | tee "$stdout_outfile"; } 2>&1 1>&3 | tee "$stderr_outfile"
Full code of my activateLogs
function for those interested.我的activateLogs
function 的完整代码供感兴趣的人使用。 I also included the dependencies even though they could be inserted into the activateLogs
function.我还包括了依赖项,即使它们可以插入到activateLogs
function 中。 For the specific issue resolution, see @WilliamPursell's answer.有关具体问题的解决方案,请参阅@WilliamPursell 的回答。
m=0
declare -a mainArgs
if [ ! "$#" = "0" ]; then
for arg in "$@"; do
mainArgs[$m]=$arg
m=$(($m + 1))
done
fi
function hasMainArg()
# $1 string to find
# return 0 if there is a match, otherwise 1
{
local match="$1"
containsElement "$1" "${mainArgs[@]}"
return $?
}
function containsElement()
# $1 string to find
# $2 array to search in
# return 0 if there is a match, otherwise 1
{
local e match="$1"
shift
for e; do [[ "$e" == "$match" ]] && return 0; done
return 1
}
function activateLogs()
# $1 = logOutput: What is the output for logs: SCREEN, DISK, BOTH. Default is DISK. Optional parameter.
{
local logOutput=$1
if [ "$logOutput" != "SCREEN" ] && [ "$logOutput" != "BOTH" ]; then
logOutput="DISK"
fi
if [ "$logOutput" = "SCREEN" ]; then
echo "Logs will only be output to screen"
return
fi
hasMainArg "--force-log"
local forceLog=$?
local isFileDescriptor3Exist=$(command 2>/dev/null >&3 && echo "Y")
if [ "$isFileDescriptor3Exist" = "Y" ]; then
echo "Logs are configured"
elif [ "$forceLog" = "1" ] && ([ ! -t 1 ] || [ ! -t 2 ]); then
# Use external file descriptor if they are set except if having "--force-log"
echo "Logs are configured externally"
else
echo "Relaunching with logs files"
local logPath="logs"
if [ ! -d $logPath ]; then mkdir $logPath; fi
local logFileName=$(basename "$0")"."$(date +%Y-%m-%d.%k-%M-%S)
if [ "$logOutput" = "DISK" ]; then
# FROM: https://stackoverflow.com/a/45426547/214898
exec 3<> "$logPath/$logFileName.log"
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
else
# FROM: https://stackoverflow.com/a/70790574/214898
exec 3>&1
{ "$0" "${mainArgs[@]}" | tee -a "$logPath/$logFileName.log" & } 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" &
fi
exit
fi
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.