프로세스의 stdout 및 stderr을 단일 파일로 리디렉션하고 싶습니다. Bash에서 어떻게합니까?
프로세스의 stdout 및 stderr을 단일 파일로 리디렉션하고 싶습니다. Bash에서 어떻게합니까?
답변:
#!/bin/bash
아닌 시작하십시오 #!/bin/sh
.
do_something 2>&1 | tee -a some_file
이에 표준 출력과 표준 출력에 열려진 리디렉션 할 것입니다 some_file
및 stdout에 그것을 인쇄 할 수 있습니다.
do_something &>filename
은 그렇지 않습니다. +1.
Ambiguous output redirect.
이유는 어떤 생각을?
$?
더 이상 종료 상태가 do_something
아니라 종료 상태를 나타내는 부작용이 있습니다 tee
.
stderr 을 stdout으로 리디렉션 하고 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'"
이제 간단한 에코는 $ LOG_FILE에 씁니다. 데몬 화에 유용합니다.
원래 게시물의 작성자에게
그것은 당신이 달성해야 할 것에 달려 있습니다. 스크립트에서 호출하는 명령의 인 / 아웃을 리디렉션해야하는 경우 이미 답변이 제공됩니다. 내 코드는 현재 스크립트 내에서 리디렉션 하여 언급 된 코드 스 니펫 후 모든 명령 / 내장 (포크 포함)에 영향을 미칩니다.
또 다른 멋진 해결책은 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)에 연결되었다고 가정 해 봅시다. 실제로 파이프, 소켓 또는 무엇이든 될 수 있습니다.
위의 줄과 추가 로이 줄을 가진 스크립트를 실행 한 결과 :
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
흥미롭게도 다음과 같이 작동합니다.
yourcommand &> filename
그러나 이것은 구문 오류를 제공합니다.
yourcommand &>> filename
syntax error near unexpected token `>'
다음을 사용해야합니다.
yourcommand 1>> filename 2>&1
&>>
BASH 4에서 작동하는 것 같습니다$ echo $BASH_VERSION 4.1.5(1)-release $ (echo to stdout; echo to stderr > /dev/stderr) &>> /dev/null
짧은 답변 : Command >filename 2>&1
또는Command &>filename
설명:
"stdout"이라는 단어를 stdout으로, "stderror"라는 단어를 stderror로 인쇄하는 다음 코드를 고려하십시오.
$ (echo "stdout"; echo "stderror" >&2)
stdout
stderror
'&'연산자는 2에게 파일 이름이 아닌 파일 디스크립터 (stderr를 가리키는)임을 bash에 알려줍니다. '&'를 생략하면이 명령은 stdout
stdout으로 인쇄 하고 "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
)보고 stdout 대신 / dev / null을 가리 키도록 파일 디스크립터 1을 설정합니다. 이 작업을 수행 한 후 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
표준 오류
여기서 왼쪽에서 오른쪽으로 평가하면 Bash는 2> & 1을보고 파일 디스크립터 2가 파일 디스크립터 1과 동일한 위치 (예 : stdout)를 가리 키도록 설정하기 때문입니다. 그런 다음 파일 설명자 1 (> / dev / null = 1> / dev / null을 기억해야 함)이> / dev / null을 가리 키도록 설정하여 일반적으로 표준 출력으로 전송되는 모든 것을 삭제합니다. 따라서 우리가 남은 것은 서브 쉘에서 stdout (괄호 안의 코드)으로 보내지지 않은 것, 즉 "stderror"뿐입니다. 흥미로운 점은 1이 stdout에 대한 포인터 일지라도 포인터 2를 1로 리디렉션하면 포인터 2- 2>&1
> 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
주요 내용 :
2>&1 >/dev/null
!는 중요 >/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 및 stderr의 출력을 로그 파일에 기록하고 stderr을 콘솔에 계속 유지하는 솔루션을 원했습니다. 그래서 티를 통해 stderr 출력을 복제해야했습니다.
이것이 내가 찾은 해결책입니다.
command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
"파이핑"이 필요한 상황에서는 다음을 사용할 수 있습니다.
| &
예를 들면 다음과 같습니다.
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"로).
다음 함수는 stdout / stderr와 로그 파일 사이에서 출력을 전환하는 프로세스를 자동화하는 데 사용할 수 있습니다.
#!/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"
@ 페르난도
당신이 한 일에 덧붙여서 기능을 약간 변경하고 &-닫기를 제거했으며 그것은 나를 위해 일했습니다.
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