shell shell - 在Bash中重定向stderr和stdout




to exec (10)

我想將進程的stdout和stderr重定向到單個文件。 我如何在Bash中做到這一點?


Answers

# 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。 用於守護進程。

對於原帖的作者,

這取決於你需要達到什麼。 如果您只是需要重定向進出您腳本中調用的命令,則已經給出了答案。 Mine是關於當前腳本中重定向的它影響所提到的代碼片段之後的所有命令/內置插件(包括分叉)。

另一個很酷的解決方案是關於重定向到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)。 在實踐中,它可能是一個管道,套接字或其他。

  • 創建FD#3和#4並分別指向與#1和#2相同的“位置”。 從現在開始,更改FD#1不會影響FD#3。 現在,FD#3和#4分別指向STDOUT和STDERR。 這些將用作真正的終端STDOUT和STDERR。
  • 1>>(...)將STDOUT重定向到parens中的命令
  • parens(sub-shell)執行'tee'從exec的STDOUT(管道)讀取,並通過另一個管道重定向到'logger'命令到parens中的子shell。 同時它將相同的輸入複製到FD#3(終端)
  • 第二部分,非常相似,是為STDERR和FD#2和#4做同樣的技巧。

運行具有上述行的腳本的結果,另外還有這一行:

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

看看here 。 應該:

yourcommand &>filename

(將stdoutstderr重定向到文件名)。


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。

它幾乎可以工作,但不是來自xinted;(


bash your_script.sh 1>file.log 2>&1

1>file.log指示shell將STDOUT發送到文件file.log2>&1指示它將STDERR(文件描述符2)重定向到STDOUT(文件描述符1)。

注意:訂單很重要,liw.fi指出, 2>&1 1>file.log file.log不起作用。


對於tcsh,我必須使用以下命令:

command >& file

如果使用command &> file ,它會給出“無效的空命令”錯誤。


“最簡單”的方式(僅限bash4): ls * 2>&- 1>&-


do_something 2>&1 | tee -a some_file

這將把stderr重定向到標準輸出和標準輸出到some_file 並將其輸出到標準輸出。


我想要一個解決方案,將stdout和stderr的輸出寫入日誌文件,而stderr仍然在控制台上。 所以我需要通過tee複製stderr輸出。

這是我找到的解決方案:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • 首先交換stderr和stdout
  • 然後將stdout附加到日誌文件
  • pipe stderr來開球並將其附加到日誌文件中

簡短的回答: Command >filename 2>&1Command &>filename

說明:

考慮下面的代碼,它將stdout這個單詞輸出到stdout,將stderror這個單詞輸出到stderror。

$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror

請注意,'&'運算符告訴bash 2是一個文件描述符(指向stderr)而不是文件名。 如果我們忽略'&',這個命令會將stdout打印到stdout,並創建一個名為“2”的文件並stderror那裡寫入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

stderror

這裡的推理是從左向右評估Bash看到2>&1,並且因此將文件描述符2設置為指向與文件描述符1相同的地方,即標準輸出。 然後它將文件描述符1(記住> / dev / null = 1> / dev / null)指向> / dev / null,從而刪除通常會發送到標準輸出的所有內容。 因此,我們只剩下那些沒有發送到子shell中的標準輸出(括號中的代碼) - 即“stderror”。 有趣的是,即使1只是一個指向stdout的指針,通過2>&1將指針2重定向到2>&1也不會形成指針鏈2 - > 1 - > stdout。 如果是這樣,作為將1重定向到/ dev / null的結果,代碼2>&1 >/dev/null會給指針鏈2 - > 1 - > / dev / null,因此代碼不會生成任何內容,與我們上面看到的相反。

最後,我會注意到有一個更簡單的方法來做到這一點:

從3.6.4節我們可以看到,我們可以使用operator &>重定向stdout和stderr。 因此,要將任何命令的stderr和stdout輸出都重定向到\dev\null (這會刪除輸出),我們只需鍵入$ command &> /dev/null或以我的示例為例:

$ (echo "stdout"; echo "stderror" >&2) &>/dev/null

關鍵要點:

  • 文件描述符的行為與指針類似(儘管文件描述符與文件指針不同)
  • 將文件描述符“a”重定向到指向文件“f”的文件描述符“b”會導致文件描述符“a”指向與文件描述符b相同的地方 - 文件“f”。 它不會形成指針鏈a - > b - > f
  • 由於以上所述,順序很重要, 2>&1 >/dev/null是!= >/dev/null 2>&1 。 一個產生輸出,另一個不產生!

最後看看這些偉大的資源:

here ,文件描述符表的解釋, 指針介紹


這適用於jQuery:

$(window).attr("location", "http://google.fr");




bash shell redirect pipe