developer tip

Bash에서 stderr 및 stdout 리디렉션

copycodes 2020. 10. 2. 22:48
반응형

Bash에서 stderr 및 stdout 리디렉션


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


여기 . 해야한다:

yourcommand &>filename

( stdout모두 stderr파일 이름으로 리디렉션 ).


do_something 2>&1 | tee -a some_file

이에 표준 출력과 표준 출력에 열려진 리디렉션 할 것입니다 some_file stdout에 그것을 인쇄 할 수 있습니다.


stderrstdout으로 리디렉션 하고 stdout 을 파일로 리디렉션 할 수 있습니다 .

some_command >file.log 2>&1 

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

이 형식은 bash에서만 작동하는 가장 인기있는 &> 형식보다 선호됩니다. Bourne 쉘에서는 백그라운드에서 명령을 실행하는 것으로 해석 될 수 있습니다. 또한 형식은 더 읽기 쉽게 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'"

이제 간단한 echo가 $ 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 (sub-shell)은 exec의 STDOUT (pipe)에서 읽는 'tee'를 실행하고 다른 파이프를 통해 'logger'명령으로 리디렉션하여 parens의 하위 쉘로 리디렉션합니다. 동시에 동일한 입력을 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

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

1>file.log파일에 STDOUT을 보낼 수있는 쉘을 지시 file.log하고, 2>&1STDOUT (파일 기술자 1)에 STDERR (파일 기술자 2) 리디렉션을 알려줍니다.

참고 : 순서는 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

'&'연산자는 2가 파일 이름이 아니라 파일 설명자 (stderr를 가리킴)임을 bash에 알려줍니다. '&'를 생략하면이 명령은 stdoutstdout에 인쇄 하고 "2"라는 파일을 만들고 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이 stdout 대신 / dev / null을 가리 키도록 설정합니다. 이렇게하면 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과 동일한 위치, 즉 stdout을 가리 키도록 설정하기 때문입니다. 그런 다음> / dev / null을 가리 키도록 파일 설명자 1 (> / dev / null = 1> / dev / null을 기억하십시오)을 설정하여 일반적으로 표준 출력으로 보내는 모든 것을 삭제합니다. 따라서 우리가 남긴 것은 서브 쉘 (괄호 안의 코드)의 stdout으로 보내지 않은 것뿐입니다. 즉 "stderror"입니다. 주목해야 할 흥미로운 점은 1이 stdout에 대한 포인터 일지라도 포인터 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"를 파일 "f"를 가리키는 파일 설명자 "b"로 리디렉션하면 파일 설명자 "a"가 파일 설명자 b-파일 "f"와 같은 위치를 가리 킵니다. 포인터 체인 a-> b-> f를 형성하지 않습니다.
  • 위의 이유로 순서가 중요 합니다. 2>&1 >/dev/null! = >/dev/null 2>&1입니다. 하나는 출력을 생성하고 다른 하나는 생성하지 않습니다!

마지막으로 다음과 같은 훌륭한 리소스를 살펴보십시오.

리디렉션대한 Bash 문서 , 파일 설명자 테이블 설명 , 포인터 소개


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과 stderr의 출력을 로그 파일에 기록하고 stderr을 콘솔에 계속 표시하는 솔루션을 원했습니다. 그래서 나는 tee를 통해 stderr 출력을 복제해야했습니다.

This is the solution I found:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • First swap stderr and stdout
  • then append the stdout to the log file
  • pipe stderr to tee and append it also to the log file

For situation, when "piping" is necessary you can use :

|&

For example:

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

or

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

This bash-based solutions can pipe STDOUT and STDERR separately (from STDERR of "sort -c" or from STDERR to "sort -h").


"Easiest" way (bash4 only): ls * 2>&- 1>&-.


The following functions can be used to automate the process of toggling outputs beetwen stdout/stderr and a 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"
    }

Example of usage inside script:

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"

In situations when you consider using things like exec 2>&1 I find easier to read if possible rewriting code using bash functions like this:

function myfunc(){
  [...]
}

myfunc &>mylog.log

For tcsh, I have to use the following command :

command >& file

If use command &> file , it will give "Invalid null command" error.


@fernando-fabreti

Adding to what you did I changed the functions slightly and removed the &- closing and it worked for me.

    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"

참고URL : https://stackoverflow.com/questions/637827/redirect-stderr-and-stdout-in-bash

반응형