Chuyển hướng stderr và stdout trong Bash


678

Tôi muốn chuyển hướng cả stdout và stderr của một tiến trình sang một tệp duy nhất. Làm thế nào để tôi làm điều đó trong Bash?


2
Tôi muốn nói rằng đây là một câu hỏi hữu ích đáng ngạc nhiên. Nhiều người không biết làm thế nào để làm điều này, vì họ không phải làm điều đó thường xuyên và đó không phải là hành vi được ghi chép tốt nhất của Bash.
Robert Wm Ruedisueli

2
Đôi khi thật hữu ích khi xem đầu ra (như bình thường) VÀ để chuyển hướng nó đến một tệp. Xem câu trả lời của Marko dưới đây. (Tôi nói điều này ở đây vì thật dễ dàng chỉ cần nhìn vào câu trả lời được chấp nhận đầu tiên nếu đủ để giải quyết vấn đề, nhưng các câu trả lời khác thường cung cấp thông tin hữu ích.)
jvriesem

Câu trả lời:


761

Hãy nhìn vào đây . Nên là:

yourcommand &>filename

(chuyển hướng cả hai stdoutstderrtên tệp).


29
Cú pháp này không được chấp nhận theo Bash Hackers Wiki . Là nó?
Salman von Abbas

20
Theo wiki.bash-hackers.org/scripting/obsolete , nó dường như đã lỗi thời theo nghĩa là nó không phải là một phần của POSIX, nhưng trang bash man không đề cập đến việc nó sẽ bị xóa khỏi bash trong tương lai gần. Trang man không chỉ định tùy chọn cho '&>' hơn '> &', tương đương.
chepner

13
Tôi đoán chúng ta không nên sử dụng &> vì nó không có trong POSIX và các shell thông thường như "dash" không hỗ trợ nó.
Sam Watkins

27
Một gợi ý thêm: Nếu bạn sử dụng điều này trong một tập lệnh, hãy chắc chắn rằng nó bắt đầu bằng #!/bin/bashchứ không phải #!/bin/sh, vì trong yêu cầu bash.
Tor Klingberg

8
Hoặc & >> để nối thay vì ghi đè.
Alexander Gonchiy

448
do_something 2>&1 | tee -a some_file

Điều này sẽ chuyển hướng stderr sang thiết bị xuất chuẩn và thiết bị xuất chuẩn some_file in nó ra thiết bị xuất chuẩn.


16
Trên AIX (ksh) giải pháp của bạn hoạt động. Câu trả lời được chấp nhận do_something &>filenamekhông. +1.
Giữ lại

11
@Daniel, nhưng câu hỏi này đặc biệt về bash
John La Rooy

3
Tôi có Ambiguous output redirect.ý tưởng nào tại sao không?
Alexandre Holden Daly

1
Tôi có một tập lệnh ruby ​​(mà tôi không muốn sửa đổi theo bất kỳ cách nào) in các thông báo lỗi bằng màu đỏ đậm. Tập lệnh ruby ​​này sau đó được gọi từ tập lệnh bash của tôi (mà tôi có thể sửa đổi). Khi tôi sử dụng ở trên, nó sẽ in các thông báo lỗi bằng văn bản thuần túy, trừ đi định dạng. Có cách nào để giữ lại định dạng trên màn hình và nhận đầu ra (cả stdout và stderr) trong một tệp không?
atlantis

8
Lưu ý rằng (theo mặc định) điều này có tác dụng phụ $?không còn đề cập đến trạng thái thoát của do_something, mà là trạng thái thoát của tee.
Flimm

255

Bạn có thể chuyển hướng stderr để stdoutstdout vào một tập tin:

some_command >file.log 2>&1 

Xem http://tldp.org/LDP/abs/html/io-redirection.html

Định dạng này được ưa thích hơn định dạng &> phổ biến nhất chỉ hoạt động trong bash. Trong Bourne shell, nó có thể được hiểu là chạy lệnh trong nền. Ngoài ra định dạng dễ đọc hơn 2 (là STDERR) được chuyển hướng đến 1 (STDOUT).

EDIT: đã thay đổi thứ tự như được chỉ ra trong các ý kiến


47
Điều này chuyển hướng stderr đến thiết bị xuất chuẩn ban đầu, không chuyển đến tập tin mà thiết bị xuất chuẩn đang diễn ra. Đặt '2> & 1' sau '> file.log' và nó hoạt động.

1
Ưu điểm của phương pháp này so với some_command &> file.log là gì?
ubermonkey

6
Nếu bạn muốn chắp thêm vào một tệp thì bạn phải thực hiện theo cách này: echo "foo" 2> & 1 1 >> bar.txt AFAIK không có cách nào để nối thêm bằng cách sử dụng &>
SlappyTheFish

9
Argh, xin lỗi, lặp lại "foo" 1 >> bar.txt 2> & 1
SlappyTheFish

11
Tôi nghĩ rằng cách giải thích rằng 2> & 1 chuyển hướng stderr sang stdout là sai; Tôi tin rằng chính xác hơn để nói rằng nó gửi stderr đến cùng một nơi mà thiết bị xuất chuẩn đang diễn ra tại thời điểm này. Do đó, đặt 2> & 1 sau khi chuyển hướng đầu tiên là điều cần thiết.
jdg

200
# 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'"

Bây giờ, tiếng vang đơn giản sẽ ghi vào $ LOG_FILE. Hữu ích cho daemonizing.

Để tác giả của bài viết gốc,

Nó phụ thuộc vào những gì bạn cần phải đạt được. Nếu bạn chỉ cần chuyển hướng vào / ra một lệnh bạn gọi từ tập lệnh của mình, câu trả lời đã được đưa ra. Của tôi là về việc chuyển hướng trong tập lệnh hiện tại, điều này ảnh hưởng đến tất cả các lệnh / tích hợp (bao gồm các nhánh) sau đoạn mã được đề cập.


Một giải pháp tuyệt vời khác là về việc chuyển hướng đến cả std-err / out AND sang logger hoặc tệp nhật ký cùng một lúc liên quan đến việc chia "một luồng" thành hai. Chức năng này được cung cấp bởi lệnh 'tee' có thể ghi / nối vào một số mô tả tệp (tệp, ổ cắm, đường ống, v.v.) cùng một lúc: 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
}

Vì vậy, ngay từ đầu. Giả sử chúng ta có thiết bị đầu cuối được kết nối với / dev / stdout (FD # 1) và / dev / stderr (FD # 2). Trong thực tế, nó có thể là một đường ống, ổ cắm hoặc bất cứ điều gì.

  • Tạo FD # 3 và # 4 và trỏ đến cùng một "vị trí" tương ứng là # 1 và # 2. Thay đổi FD # 1 không ảnh hưởng đến FD # 3 kể từ bây giờ. Bây giờ, FDs # 3 và # 4 tương ứng với STDOUT và STDERR. Chúng sẽ được sử dụng như thiết bị đầu cuối thực sự STDOUT và STDERR.
  • 1 >> (...) chuyển hướng STDOUT để ra lệnh trong parens
  • parens (shell phụ) thực thi việc đọc 'tee' từ STDOUT (pipe) của exec và chuyển hướng đến lệnh 'logger' thông qua một đường ống khác đến shell phụ trong parens. Đồng thời, nó sao chép cùng một đầu vào vào FD # 3 (thiết bị đầu cuối)
  • phần thứ hai, rất giống nhau, là về cách thực hiện cùng một thủ thuật cho STDERR và FDs # 2 và # 4.

Kết quả của việc chạy tập lệnh có dòng trên và thêm đoạn này:

echo "Will end up in STDOUT(terminal) and /var/log/messages"

...là như sau:

$ ./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

Nếu bạn muốn xem hình ảnh rõ hơn, hãy thêm 2 dòng này vào tập lệnh:

ls -l /proc/self/fd/
ps xf

1
chỉ có một ngoại lệ trong ví dụ đầu tiên bạn đã viết: exec 1 <> $ LOG_FILE. Nó gây ra logfile ban đầu là allower owerwrwr. đối với loggin thực, cách tốt hơn là: exec 1 >> $ LOG_FILE, nó gây ra log được nối thêm.
Znik

4
Điều đó đúng mặc dù nó phụ thuộc vào ý định. Cách tiếp cận của tôi là luôn tạo một tệp nhật ký duy nhất và có dấu thời gian. Cái khác là để chắp thêm. Cả hai cách đều là 'logrotatizable'. Tôi thích các tệp riêng biệt yêu cầu ít phân tích cú pháp hơn nhưng như tôi đã nói, bất cứ điều gì làm cho thuyền của bạn nổi :)
quizac

1
Giải pháp thứ hai của bạn là thông tin, nhưng những gì với tất cả các mã dọn dẹp? Nó dường như không liên quan, và nếu vậy, chỉ làm vẩn đục một ví dụ tốt. Tôi cũng muốn thấy nó được làm lại một chút để FD 1 và 2 không được chuyển hướng đến logger mà thay vào đó là 3 và 4 để mọi thứ gọi tập lệnh này có thể thao túng thêm 1 và 2 theo giả định chung là stdout == 1 và stderr == 2, nhưng thử nghiệm ngắn gọn của tôi cho thấy điều đó phức tạp hơn.
JFlo

1
Tôi thích nó tốt hơn với mã dọn dẹp. Nó có thể là một chút xao lãng từ ví dụ cốt lõi, nhưng tước nó sẽ làm cho ví dụ không đầy đủ. Mạng đã có đầy đủ các ví dụ mà không cần xử lý lỗi, hoặc ít nhất là một lưu ý thân thiện rằng nó vẫn cần khoảng một trăm dòng mã để thực hiện an toàn để sử dụng.
Zoltan K.

1
Tôi muốn xây dựng mã dọn dẹp. Đó là một phần của tập lệnh giúp trình diễn ergo trở nên miễn nhiễm với tín hiệu HANG-UP. 'tee' và 'logger' là các quá trình được sinh ra bởi cùng một PPID và chúng thừa hưởng bẫy HUP từ tập lệnh bash chính. Vì vậy, một khi quá trình chính chết đi, chúng sẽ được kế thừa bởi init [1]. Họ sẽ không trở thành zombie (defunc). Mã dọn dẹp đảm bảo rằng tất cả các tác vụ nền sẽ bị hủy, nếu tập lệnh chính bị chết. Nó cũng áp dụng cho bất kỳ quá trình nào khác có thể đã được tạo và chạy trong nền.
quizac

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

1>file.loghướng dẫn shell gửi STDOUT đến tệp file.log2>&1yêu cầu nó chuyển hướng STDERR (mô tả tệp 2) sang STDOUT (mô tả tệp 1).

Lưu ý: Thứ tự quan trọng như liw.fi đã chỉ ra, 2>&1 1>file.logkhông hoạt động.


21

Thật kỳ lạ, điều này hoạt động:

yourcommand &> filename

Nhưng điều này đưa ra một lỗi cú pháp:

yourcommand &>> filename
syntax error near unexpected token `>'

Bạn phải sử dụng:

yourcommand 1>> filename 2>&1

10
&>>dường như hoạt động trên BASH 4:$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
user272735

15

Câu trả lời ngắn: Command >filename 2>&1hoặcCommand &>filename


Giải trình:

Hãy xem xét đoạn mã sau in từ "stdout" thành stdout và từ "stderror" thành stderror.

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

Lưu ý rằng toán tử '&' cho bash biết rằng 2 là một mô tả tệp (trỏ đến stderr) chứ không phải tên tệp. Nếu chúng ta bỏ qua '&', lệnh này sẽ in stdoutra thiết bị xuất chuẩn và tạo một tệp có tên "2" và ghi vào stderrorđó.

Bằng cách thử nghiệm mã ở trên, bạn có thể tự mình thấy chính xác cách thức các toán tử chuyển hướng hoạt động. Chẳng hạn, bằng cách thay đổi tập tin nào trong hai mô tả 1,2, được chuyển hướng đến /dev/nullhai dòng mã sau sẽ xóa mọi thứ khỏi thiết bị xuất chuẩn và mọi thứ từ stderror (in những gì còn lại).

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

Bây giờ, chúng ta có thể giải thích tại sao giải pháp tại sao đoạn mã sau không tạo ra đầu ra:

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

Để thực sự hiểu điều này, tôi khuyên bạn nên đọc trang web này trên các bảng mô tả tệp . Giả sử bạn đã đọc xong, chúng ta có thể tiến hành. Lưu ý rằng Bash xử lý từ trái sang phải; do đó Bash nhìn thấy >/dev/nullđầu tiên (giống như 1>/dev/null) và đặt bộ mô tả tệp 1 để trỏ đến / dev / null thay vì thiết bị xuất chuẩn. Làm xong việc này, Bash sau đó di chuyển sang phải và thấy 2>&1. Cái này đặt bộ mô tả tệp 2 để trỏ đến cùng một tệp với bộ mô tả tệp 1 (và không phải chính bộ mô tả tệp 1 !!!! (xem tài nguyên này trên con trỏđể biết thêm thông tin)). Vì bộ mô tả tệp 1 điểm đến / dev / null và bộ mô tả tệp 2 điểm vào cùng một tệp với bộ mô tả tệp 1, bộ mô tả tệp 2 bây giờ cũng trỏ đến / dev / null. Do đó, cả hai bộ mô tả tệp đều trỏ đến / dev / null và đây là lý do tại sao không có đầu ra nào được hiển thị.


Để kiểm tra xem bạn có thực sự hiểu khái niệm này hay không, hãy thử đoán đầu ra khi chúng tôi chuyển đổi thứ tự chuyển hướng:

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

stderror

Lý do ở đây là việc đánh giá từ trái sang phải, Bash thấy 2> & 1, và do đó đặt bộ mô tả tệp 2 để trỏ đến cùng một vị trí với bộ mô tả tệp 1, tức là thiết bị xuất chuẩn. Sau đó, nó đặt bộ mô tả tệp 1 (hãy nhớ rằng> / dev / null = 1> / dev / null) để trỏ đến> / dev / null, do đó xóa mọi thứ thường được gửi đến tiêu chuẩn. Do đó, tất cả những gì chúng ta còn lại là cái không được gửi đến thiết bị xuất chuẩn trong lớp con (mã trong ngoặc đơn) - tức là "stderror". Điều thú vị cần lưu ý là mặc dù 1 chỉ là một con trỏ đến thiết bị xuất chuẩn, việc chuyển hướng con trỏ 2 đến 1 thông qua 2>&1KHÔNG tạo thành một chuỗi các con trỏ 2 -> 1 -> thiết bị xuất chuẩn. Nếu có, do chuyển hướng 1 đến / dev / null, mã2>&1 >/dev/null sẽ cung cấp cho chuỗi con trỏ 2 -> 1 -> / dev / null và do đó mã sẽ không tạo ra gì, trái ngược với những gì chúng ta thấy ở trên.


Cuối cùng, tôi lưu ý rằng có một cách đơn giản hơn để làm điều này:

Từ mục 3.6.4 ở đây , chúng ta thấy rằng chúng ta có thể sử dụng toán tử &>để chuyển hướng cả thiết bị xuất chuẩn và thiết bị xuất chuẩn. Do đó, để chuyển hướng cả đầu ra stderr và stdout của bất kỳ lệnh nào sang \dev\null(mà xóa đầu ra), chúng ta chỉ cần gõ $ command &> /dev/null hoặc trong trường hợp ví dụ của tôi:

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

Những điểm chính:

  • Bộ mô tả tệp hoạt động giống như con trỏ (mặc dù bộ mô tả tệp không giống với con trỏ tệp)
  • Chuyển hướng bộ mô tả tệp "a" sang bộ mô tả tệp "b" trỏ đến tệp "f", khiến bộ mô tả tệp "a" trỏ đến cùng một vị trí với bộ mô tả tệp b - tệp "f". Nó KHÔNG tạo thành một chuỗi các con trỏ a -> b -> f
  • Bởi vì ở trên, vấn đề thứ tự, 2>&1 >/dev/nulllà! = >/dev/null 2>&1. Một cái tạo ra đầu ra và cái kia thì không!

Cuối cùng hãy xem những tài nguyên tuyệt vời này:

Tài liệu Bash về chuyển hướng , giải thích các bảng mô tả tệp , giới thiệu về con trỏ


Bộ mô tả tệp (0, 1, 2) chỉ là phần bù vào bảng. Khi 2> & 1 được sử dụng, hiệu ứng là vị trí FD [2] = dup (1), do đó, bất cứ nơi nào FD [1] đang chỉ FD [2] bây giờ đều trỏ đến. Khi bạn thay đổi FD [1] thành trỏ / dev / null, thì FD [1] sẽ thay đổi nhưng nó không thay đổi vị trí FD [2] (trỏ đến thiết bị xuất chuẩn). Tôi sử dụng thuật ngữ dup () vì đó là lệnh gọi hệ thống được sử dụng để sao chép bộ mô tả tệp.
PatS

11
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" )

Nó có liên quan: Viết stdOut & stderr vào syslog.

Nó gần như hoạt động, nhưng không phải từ xinted; (


Tôi đoán nó không hoạt động vì "/ dev / fd / 3 Quyền bị từ chối". Thay đổi thành> & 3 có thể giúp ích.
quizac

6

Tôi muốn một giải pháp để có đầu ra từ thiết bị xuất chuẩn cộng với thiết bị xuất chuẩn được ghi vào tệp nhật ký và thiết bị xuất chuẩn vẫn còn trên bàn điều khiển. Vì vậy, tôi cần phải nhân đôi đầu ra stderr qua tee.

Đây là giải pháp tôi tìm thấy:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • Đầu tiên trao đổi stderr và stdout
  • sau đó nối thiết bị xuất chuẩn vào tệp nhật ký
  • ống stderr vào tee và nối nó vào tệp nhật ký

BTW, điều này không làm việc cho tôi (logfile trống). | tee không có tác dụng. Thay vào đó, tôi đã làm cho nó hoạt động bằng cách sử dụng stackoverflow.com/questions/692000/
Khăn

4

Đối với tình huống, khi "đường ống" là cần thiết, bạn có thể sử dụng:

&

Ví dụ:

echo -ne "15\n100\n"|sort -c |& tee >sort_result.txt

hoặc là

TIMEFORMAT=%R;for i in `seq 1 20` ; do time kubectl get pods |grep node >>js.log  ; done |& sort -h

Các giải pháp dựa trên bash này có thể tách riêng STDOUT và STDERR (từ STDERR của "sort -c" hoặc từ STDERR sang "sort -h").



1

Các chức năng sau đây có thể được sử dụng để tự động hóa quá trình chuyển đổi đầu ra beetwen stdout / stderr và logfile.

#!/bin/bash

    #set -x

    # global vars
    OUTPUTS_REDIRECTED="false"
    LOGFILE=/dev/stdout

    # "private" function used by redirect_outputs_to_logfile()
    function save_standard_outputs {
        if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
            exit 1;
        fi
        exec 3>&1
        exec 4>&2

        trap restore_standard_outputs EXIT
    }

    # Params: $1 => logfile to write to
    function redirect_outputs_to_logfile {
        if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
            exit 1;
        fi
        LOGFILE=$1
        if [ -z "$LOGFILE" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"

        fi
        if [ ! -f $LOGFILE ]; then
            touch $LOGFILE
        fi
        if [ ! -f $LOGFILE ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
            exit 1
        fi

        save_standard_outputs

        exec 1>>${LOGFILE%.log}.log
        exec 2>&1
        OUTPUTS_REDIRECTED="true"
    }

    # "private" function used by save_standard_outputs() 
    function restore_standard_outputs {
        if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: Cannot restore standard outputs because they have NOT been redirected"
            exit 1;
        fi
        exec 1>&-   #closes FD 1 (logfile)
        exec 2>&-   #closes FD 2 (logfile)
        exec 2>&4   #restore stderr
        exec 1>&3   #restore stdout

        OUTPUTS_REDIRECTED="false"
    }

Ví dụ về cách sử dụng bên trong tập lệnh:

echo "this goes to stdout"
redirect_outputs_to_logfile /tmp/one.log
echo "this goes to logfile"
restore_standard_outputs 
echo "this goes to stdout"

Khi tôi sử dụng các chức năng của bạn và nó cố gắng khôi phục các đầu ra tiêu chuẩn, tôi nhận được tiếng vang: lỗi ghi: Số tệp xấu chuyển hướng hoạt động hoàn hảo ... có vẻ như khôi phục không
thom schumacher

để làm cho tập lệnh của bạn hoạt động, tôi đã phải nhận xét các dòng này và tôi đã thay đổi thứ tự: #exec 1> & - #closes FD 1 (logfile) #exec 2> & - #closes FD 2 (logfile); exec 1> & 3 #restore stdout exec 2> & 4 #restore stderr
thom schumacher

Rất tiếc khi biết điều đó. Tôi không nhận được bất kỳ lỗi nào khi chạy trong CentOS 7, bash 4.2.46. Tôi đã chú thích tài liệu tham khảo nơi tôi nhận được các lệnh đó. Đó là: Tham chiếu: logan.tw/posts/2016/02/20/open-and-close-files-in-bash
Fernando Fabreti

Tôi đang chạy các lệnh này trên AIX có lẽ là lý do tại sao. Tôi đã thêm một bài viết cho bản sửa lỗi tôi đã thực hiện.
thom schumacher

1

@ fernando-fabreti

Thêm vào những gì bạn đã làm tôi đã thay đổi các chức năng một chút và loại bỏ & - đóng và nó hoạt động với tôi.

    function saveStandardOutputs {
      if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
        exec 3>&1
        exec 4>&2
        trap restoreStandardOutputs EXIT
      else
          echo "[ERROR]: ${FUNCNAME[0]}: Cannot save standard outputs because they have been redirected before"
          exit 1;
      fi
  }

  # Params: $1 => logfile to write to
  function redirectOutputsToLogfile {
      if [ "$OUTPUTS_REDIRECTED" == "false" ]; then
        LOGFILE=$1
        if [ -z "$LOGFILE" ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: logfile empty [$LOGFILE]"
        fi
        if [ ! -f $LOGFILE ]; then
            touch $LOGFILE
        fi
        if [ ! -f $LOGFILE ]; then
            echo "[ERROR]: ${FUNCNAME[0]}: creating logfile [$LOGFILE]"
            exit 1
        fi
        saveStandardOutputs
        exec 1>>${LOGFILE}
        exec 2>&1
        OUTPUTS_REDIRECTED="true"
      else
        echo "[ERROR]: ${FUNCNAME[0]}: Cannot redirect standard outputs because they have been redirected before"
          exit 1;
      fi
  }
  function restoreStandardOutputs {
      if [ "$OUTPUTS_REDIRECTED" == "true" ]; then
      exec 1>&3   #restore stdout
      exec 2>&4   #restore stderr
      OUTPUTS_REDIRECTED="false"
     fi
  }
  LOGFILE_NAME="tmp/one.log"
  OUTPUTS_REDIRECTED="false"

  echo "this goes to stdout"
  redirectOutputsToLogfile $LOGFILE_NAME
  echo "this goes to logfile"
  echo "${LOGFILE_NAME}"
  restoreStandardOutputs 
  echo "After restore this goes to stdout"

1

Trong các tình huống khi bạn cân nhắc sử dụng những thứ như exec 2>&1tôi thấy dễ đọc hơn nếu có thể viết lại mã bằng các hàm bash như thế này:

function myfunc(){
  [...]
}

myfunc &>mylog.log

0

Đối với tcsh, tôi phải sử dụng lệnh sau:

command >& file

Nếu sử dụng command &> file, nó sẽ báo lỗi "Lệnh null không hợp lệ".

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.