문제

프로세스의 stdout과 stderr를 단일 파일로 리디렉션하고 싶습니다. Bash에서 어떻게해야합니까?

도움이 되었습니까?

해결책

구경하다 여기. 해야한다:

yourcommand &>filename

(둘 다 리디렉션 stdout 그리고 stderr 파일 이름에).

다른 팁

do_something 2>&1 | tee -a some_file

이것은 stderr를 stdout 및 stdout로 리디렉션 할 것입니다. some_file 그리고 stdout에 인쇄하십시오.

리디렉션 할 수 있습니다 Stderr 에게 stdout 그리고 stdout 파일로 :

some_command >file.log 2>&1 

보다 http://tldp.org/ldp/abs/html/io-redirection.html

이 형식은 Bash에서만 작동하는 가장 인기있는 &> 형식보다 선호됩니다. Bourne Shell에서는 백그라운드에서 명령을 실행하는 것으로 해석 될 수 있습니다. 또한 형식은 더 읽기 쉬운 2 (stderr)를 1 (stdout)로 리디렉션합니다.

편집 : 주석에 지적 된대로 순서가 변경되었습니다.

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

이제 Simple Echo는 $ log_file에 쓸 것입니다. 데모네이징에 유용합니다.

원래 게시물의 저자에게

그것은 당신이 달성해야 할 것에 달려 있습니다. 스크립트에서 호출하는 명령을 내/종료 해야하는 경우 답변이 이미 제공됩니다. 광산은 리디렉션에 관한 것입니다 이내에 언급 된 코드 스 니펫 후 모든 명령/내장 (포크 포함)에 영향을 미치는 현재 스크립트.


또 다른 멋진 솔루션은 STD-ERR/OUT 및 Logger 또는 로그 파일로 다시 리디렉션하여 "스트림"을 2로 분할하는 것입니다. 이 기능은 한 번에 여러 파일 디스크립터 (파일, 소켓, 파이프 등)에 작성/추가 할 수있는 '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)에 연결된 터미널이 있다고 가정 해 봅시다. 실제로, 그것은 파이프, 소켓 또는 무엇이든 일 수 있습니다.

  • FDS #3 및 #4를 만들고 각각 #1 및 #2와 동일한 "위치"를 가리 킵니다. FD #1을 변경해도 FD #3에 영향을 미치지 않습니다. 이제 FDS #3과 #4는 각각 STDOUT 및 Stderr를 가리 킵니다. 이것들은 다음과 같이 사용됩니다 진짜 터미널 stdout 및 stderr.
  • 1 >> (...) parens에서 명령으로 stdout을 리디렉션
  • Parens (Sub-Shell)는 Exec의 stdout (파이프)에서 'Tee'읽기를 실행하고 다른 파이프를 통해 Parens의 하위 쉘로 'Logger'명령으로 리디렉션됩니다. 동시에 FD #3 (터미널)에 동일한 입력을 복사합니다.
  • 두 번째 부분은 매우 유사한 부분은 STDERR 및 FDS #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
bash your_script.sh 1>file.log 2>&1

1>file.log 쉘에 파일에 stdout을 보내도록 지시합니다 file.log, 그리고 2>&1 stderr (파일 디스크립터 2)를 stdout (파일 디스크립터 1)로 리디렉션하도록 지시합니다.

메모: 명령은 liw.fi가 지적한대로 중요합니다. 2>&1 1>file.log 작동하지 않습니다.

흥미롭게도 이것은 작동합니다.

yourcommand &> filename

그러나 이것은 구문 오류를 제공합니다.

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

사용해야합니다.

yourcommand 1>> filename 2>&1

짧은 대답: Command >filename 2>&1 또는 Command &>filename


설명:

"stdout"이라는 단어를 stdout에 인쇄하고 "stderror"라는 단어를 Stderror에 인쇄하는 다음 코드를 고려하십시오.

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

'&'연산자는 Bash에게 파일 이름이 아닌 파일 디스크립터 (stderr을 가리키는)라고 말합니다. 우리가 '&'를 제외하면이 명령이 인쇄됩니다. stdout stdout에 "2"라는 파일을 만들고 쓰기 stderror 거기.

위의 코드를 실험하면 리디렉션 연산자가 어떻게 작동하는지 직접 확인할 수 있습니다. 예를 들어, 두 설명자 중 어느 파일을 변경하여 1,2,, 리디렉션됩니다 /dev/null 다음 두 줄의 코드는 STDOUT에서 각각 모든 것을 삭제합니다.

$ (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을 stdout 대신 /dev /null을 가리 키라고 설정합니다. 이 작업을 수행 한 후 Bash는 오른쪽으로 이동하여 2>&1. 파일 설명 자 2를 설정합니다 같은 파일을 가리 킵니다 파일 설명자 1 (그리고 설명 자체를 제출하지 않음 !!!! (참조 포인터에 대한이 리소스 더 많은 정보를 위해서)) . 파일 디스크립터 1은 /dev /null을 가리키고 파일 디스크립터 2는 파일 디스크립터 1과 동일한 파일을 가리키기 때문에 파일 디스크립터 2는 이제 /dev /null을 가리 킵니다. 따라서 두 파일 디스크립터 모두 /dev /null을 가리키므로 출력이 렌더링되지 않는 이유입니다.


개념을 실제로 이해하는지 테스트하려면 리디렉션 순서를 전환 할 때 출력을 추측하십시오.

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

석유

여기서 추론은 왼쪽에서 오른쪽으로 평가하면 Bash는 2> & 1을보고 파일 디스크립터 2를 파일 디스크립터 1 (즉)와 동일한 장소, 즉 stdout과 동일한 장소로 설정하기 때문입니다. 그런 다음 파일 디스크립터 1 (>/dev/null = 1>/dev/null)을 설정하여>/dev/null을 가리 키므로 일반적으로 표준 아웃으로 보내는 모든 것을 삭제합니다. 따라서 우리가 남긴 것은 서브 쉘 (괄호 안의 코드)- 즉 "Stderror"에서 stdout으로 보내지 않은 것입니다. 흥미로운 점은 1이 STDOUT에 대한 포인터 일지라도 POININT를 2 ~ 1을 통해 리디렉션하는 것입니다. 2>&1 포인터 체인 2-> 1-> stdout을 형성하지 않습니다. 그랬다면, 1로 /dev /null로 리디렉션 한 결과 코드 2>&1 >/dev/null 포인터 체인 2-> 1-> /dev /null을 제공하므로 코드는 위에서 본 것과는 달리 아무것도 생성되지 않습니다.


마지막으로,이를 수행하는 더 간단한 방법이 있습니다.

섹션 3.6.4에서 여기, 우리는 운영자를 사용할 수 있음을 알 수 있습니다 &> stdout과 stderr를 모두 리디렉션합니다. 따라서 명령의 stderr 및 stdout 출력을 모두 리디렉션하기 위해 \dev\null (출력을 삭제하는), 우리는 간단히 입력합니다.$ command &> /dev/null 또는 내 예제의 경우 :

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

주요 테이크 아웃 :

  • 파일 설명자는 포인터처럼 행동합니다 (파일 설명자는 파일 포인터와 같지 않지만)
  • 파일 디스크립터 "A"를 파일 디스크립터 "B"로 리디렉션하여 "F"를 파일로 가리키면 파일 디스크립터 "A"가 파일 디스크립터 B- 파일 "F"와 동일한 위치를 가리 키도록합니다. 그것은 포인터의 사슬을 형성하지 않습니다 -> b-> f
  • 위의 내용으로 인해 주문이 중요합니다. 2>&1 >/dev/null is! = >/dev/null 2>&1. 하나는 출력을 생성하고 다른 하나는 그렇지 않습니다!

마지막 으로이 훌륭한 자료를 살펴보십시오.

리디렉션에 대한 강조 문서, 파일 디스크립터 테이블에 대한 설명, 포인터 소개

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에서는 그렇지 않습니다. (

STDOUT PLUS STDERR의 출력을 로그 파일에 작성하고 여전히 콘솔에 STDERR을 작성하는 솔루션을 원했습니다. 그래서 나는 티를 통해 Stderr 출력을 복제해야했습니다.

이것은 내가 찾은 해결책입니다.

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • 첫 번째 스왑 stderr 및 stdout
  • 그런 다음 stdout을 로그 파일에 추가하십시오
  • stderr에 tee to tee 및 로그 파일에 추가하십시오.

상황의 경우 "배관"이 필요한 경우 다음을 사용할 수 있습니다.

|&

예를 들어:

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

또는

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

이 bash 기반 솔루션은 Stdout과 Stderr ( "Sort -c"의 Stderr에서 또는 Stderr에서 "Sort -H"까지)를 별도로 파이프로 파이프 할 수 있습니다.

"가장 쉬운"방법 (Bash4 만 해당) : ls * 2>&- 1>&-.

다음 기능을 사용하여 출력 Beetwen STDOUT/STDERR 및 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"
    }

스크립트 내부의 사용 예 :

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"

TCSH의 경우 다음 명령을 사용해야합니다.

command >& file

사용하는 경우 command &> file , "유효하지 않은 null 명령"오류가 제공됩니다.

@Fernando-Fabreti

당신이 한 일에 추가하면 기능을 약간 변경하고 &- 마감을 제거했으며 저에게 효과가있었습니다.

    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"

당신이 같은 것을 사용하는 것을 고려할 때 상황에서 exec 2>&1 가능한 경우 BASH 기능을 사용하여 코드를 다시 작성하기가 더 쉽습니다.

function myfunc(){
  [...]
}

myfunc &>mylog.log
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top