[英]Output stdout and stderr to file and screen and stderr to file in a limited environment
該問題可能可以使用mkfifo
解決,但在我的 QNAP 上不存在。 所以,這里是問題的描述以及我到目前為止所嘗試的內容。
我有一個名為activateLogs
的 function 如果將日志寫入磁盤或兩者(屏幕和磁盤),它會重新啟動腳本。 這兩個選項都是我想要實現的新功能。
exec 3<> "$logPath/$logFileName.log"
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
這段代碼是寫入磁盤的版本。 mainArgs
包含傳遞給腳本的所有 arguments,並在此 function 中定義。 該解決方案來自https://stackoverflow.com/a/45426547/214898 。 它將 stderr 和 stdout 組合在一個文件中,而 output stderr 在另一個文件中。
所以,現在,我希望能夠保留它並將打印標准錯誤和標准輸出添加到屏幕。
無法應用上面鏈接的問題中接受的解決方案,因為腳本正在使用sh
運行並且mkfifo
不存在。
嘗試#1
exec 3>&1
"$0" "${mainArgs[@]}" 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" 1>&3 &
--> 替換上面的代碼並在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
我有屏幕,錯誤文件,但日志文件是空的。
嘗試 #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" &
結果相同。 我可能在這個中理解第一個tee
最初沒有寫入文件描述符#3,因此3>&1
什么都不做。
嘗試 #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"
我收到 mkfifo:從busybox
mkfifo: applet not found
嘗試 #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"
我沒有日志(“真實”日志“也沒有錯誤)。臨時文件被刪除了。此外,腳本永遠不會結束,這是由trap
引起的。
嘗試#5
exec 3>&1
{ "$0" "${mainArgs[@]}" | tee -a "$logPath/$logFileName.log"; } 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" &
exit
該解決方案似乎很有希望,但現在重新啟動腳本不起作用,因為我的代碼檢測當前是否正在執行並停止它。 這是正常的,因為即使我在整行末尾使用&
,它也會在子進程中執行,但是......(在編寫時進行測試)。 更換終結器;
通過&
修復它。
現在我將 stdout 和 stderr 放到一個文件中,並將 stderr 放到一個單獨的文件中。
最終版本工作
exec 3>&1
{ "$0" "${mainArgs[@]}" | tee -a "$logPath/$logFileName.log" & } 2>&1 1>&3 | tee -a "$logPath/$logFileName.err" &
exit
令人難以置信的是,我的第一次嘗試是如此接近解決方案。
也許我沒有正確閱讀問題,但似乎您可以執行以下操作:
#!/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"
我的activateLogs
function 的完整代碼供感興趣的人使用。 我還包括了依賴項,即使它們可以插入到activateLogs
function 中。 有關具體問題的解決方案,請參閱@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.