簡體   English   中英

如何實時重定向后台bash管道的output

[英]How to redirect output of background bash pipeline in real time

我有一個長時間運行的 bash 腳本(就像一個守護進程),它可能會向 stdout 和 stderr 發出 output。

我有這些要求:

  • 在每 output 行前加上一個字符串(例如日期、日志條目的時間和其他信息)
  • 將所有 output 重定向到日志文件中
  • output 重定向必須是實時的(即日志文件必須在進程運行時被填充)
  • 該進程需要在后台運行

這是我到目前為止所取得的成就:

myscript="script_name"
log_file="/tmp/blabla"

prepend_info() {
    xargs -d '\n' printf "$(date +'%b %d %T') $HOSTNAME $myscript: %s\n"
}

script -q -c "$myscript" /dev/null 2>&1 | prepend_info > "$log_file" 2>&1 &

問題是日志文件僅在我的腳本終止后才被填充,但我想在它運行時看到 output。

如果我刪除|& prepend_info它會按預期工作,但我也需要將這些附加信息添加到日志文件中。

似乎 pipe 僅在第一個命令終止后才執行。

有沒有辦法修改后台腳本的 output 並在運行時將其重定向到文件中?

我需要盡可能兼容,只能使用簡單的bash命令。 例如,我不能使用ts ,因為它並不總是可用,而且我不僅需要時間戳,還需要其他信息。

更新:到目前為止我找到的唯一解決方案(它解決了所有問題,也解決了日期問題)如下。

myscript="script_name"
log_file="/tmp/blabla"

exec_script() {
    rm -f "$log_file"
    local out_log=<($myscript 2>&1)
    while read -r line; do
        echo "$(date +'%b %d %T') $HOSTNAME $myscript: $line" >> $log_file
    done < "$out_log"
}

exec_script &

如果有人有更好的解決方案,我會洗耳恭聽。

我想了解我的代碼片段哪里錯了

請記住使用 shellcheck 檢查您的腳本。 $myscript$log_file沒有被引用。 另請參閱https://wiki.bash-hackers.org/scripting/obsolete - 您正在使用function NAME() ,只需使用NAME() ,不應同時使用|&>&

管道中的命令是完全緩沖的,在您的情況下,它會在命令終止時刷新 output,因為緩沖區足夠大。 script_nametr都是完全緩沖的,這在您的命令到達xargs之前提供了很大的緩沖。

tr '\n' '\0' | xargs -0 tr '\n' '\0' | xargs -0聽起來很奇怪。 為什么將零的換行符更改為使用零? 從一開始就使用換行符xargs -d '\n'

此外,命令替換會在運行xargs之前擴展,因此$(date +'%b %d %T')將擴展一次xargs開始之前的日期,而不是當前行已寫入的時間。

在 Bash 中, $HOSTNAME$(hostname)快。

此外, tr output 始終是全緩沖的,因為tr程序本身使用全緩沖算法。 Sooo .. 不要使用tr

有沒有辦法修改后台腳本的 output 並在運行時將其重定向到文件中?

關於如何為命令 output 添加前綴,在 .net 上有很多很多解決方案,例如https://unix.stackexchange.com/questions/26728/prepending-a-timestamp-to-each-line-of-output -來自命令https://unix.stackexchange.com/questions/592215/best-way-to-prefix-output-of-a-command-and-check-its-return-value

主觀上,最好不要使用 shell,而編寫一個 python 腳本,它將subprocess.popen命令並通過讀取文件描述符來實現你想要的。 這樣性能就可以接受了。

使用 bash 4.2+,您可以避免使用printf '%(... )T'和全局變量SECONDS在每行輸入中分叉date命令

#!/bin/bash

myscript="script_name"
log_file="/tmp/blabla"

SECONDS=$(command -p awk 'BEGIN{srand();print srand()}')

{
    "$my_script" 2>&1 |
    while IFS='' read -r line
    do
        printf '%(%b %d %T)T %s %s: %s\n' "$SECONDS" "$HOSTNAME" "$myscript" "$line"
    done > "$log_file" 2>&1
} &

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM