Bash에서 Stderr 및 Stdout을 리디렉션합니다
문제
프로세스의 stdout과 stderr를 단일 파일로 리디렉션하고 싶습니다. Bash에서 어떻게해야합니까?
해결책
다른 팁
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