简体   繁体   English

Output stdout and stderr to file and screen and stderr to file 在有限的环境中

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM