मैं एक प्रक्रिया के stdout और stderr दोनों को एक फ़ाइल में पुनर्निर्देशित करना चाहता हूं। मैं बैश में कैसे करूँ?
मैं एक प्रक्रिया के stdout और stderr दोनों को एक फ़ाइल में पुनर्निर्देशित करना चाहता हूं। मैं बैश में कैसे करूँ?
जवाबों:
यहां देखिए । होना चाहिए:
yourcommand &>filename
(पुनर्निर्देश दोनों stdout
और stderr
फ़ाइल नाम के लिए)।
#!/bin/bash
है #!/bin/sh
, बजाय इसके कि बैश की आवश्यकता होती है।
do_something 2>&1 | tee -a some_file
यह stdout और stdout के लिए stderout को रीडायरेक्ट करने वाला है 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 देखें
यह प्रारूप सबसे लोकप्रिय और> प्रारूप से अधिक पसंद किया जाता है जो केवल बैश में काम करता है। बॉर्न शेल में इसे पृष्ठभूमि में कमांड चलाने के रूप में व्याख्या किया जा सकता है। साथ ही प्रारूप अधिक पठनीय 2 है (STDERR) 1 (STDOUT) पर पुनर्निर्देशित।
EDIT: टिप्पणियों में बताए गए क्रम को बदल दिया
# 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 को लिखेंगे। निस्तारण के लिए उपयोगी है।
मूल पोस्ट के लेखक के लिए,
यह निर्भर करता है कि आपको क्या हासिल करना है। यदि आपको अपनी स्क्रिप्ट से कॉल करने वाली किसी कमांड के अंदर / बाहर अनुप्रेषित करने की आवश्यकता है, तो उत्तर पहले ही दिए जा चुके हैं। मेरा वर्तमान स्क्रिप्ट के भीतर पुनर्निर्देशन के बारे में है जो उल्लिखित कोड स्निपेट के बाद सभी कमांड / बिल्ट-इन (फोर्क शामिल हैं) को प्रभावित करता है।
एक और अच्छा समाधान एसटीडी-इर / आउट दोनों को रीडायरेक्ट करने के बारे में है और एक बार में लकड़हारा या लॉग फाइल करने के लिए जिसमें "स्ट्रीम" को दो में विभाजित करना शामिल है। यह कार्यक्षमता 'टी' कमांड द्वारा प्रदान की जाती है, जो एक साथ कई फाइल डिस्क्रिप्टर (फाइल, सॉकेट, पाइप आदि) को लिख / जोड़ सकती है: 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
यदि आप स्पष्ट चित्र देखना चाहते हैं, तो इन 2 पंक्तियों को स्क्रिप्ट में जोड़ें:
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
&>>
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 एक फाइल डिस्क्रिप्टर है (जो स्टॉडर को इंगित करता है) और फाइल का नाम नहीं है। यदि हमने 'और' छोड़ दिया है, तो यह कमांड stdout
stdout में प्रिंट हो जाएगी , और "2" नामक एक फाइल बनाकर लिख देगाstderror
वहां लिखेगी।
ऊपर दिए गए कोड के साथ प्रयोग करके, आप खुद देख सकते हैं कि रीडायरेक्शन ऑपरेटर कैसे काम करते हैं। उदाहरण के लिए, कौन सी फाइल को बदलकर, दोनों डिस्क्रिप्टर में से कौन सी कोड की निम्नलिखित दो पंक्तियों 1,2
को रीडायरेक्ट किया जाता /dev/null
है, स्टडआउट से सबकुछ हटा दें, और क्रमशः स्टायरर से सब कुछ (प्रिंटिंग जो बनी हुई है)।
$ (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
इसे वास्तव में समझने के लिए, मैं आपको फ़ाइल डिस्क्रिप्टर तालिकाओं पर इस वेबपृष्ठ को पढ़ने की अत्यधिक सलाह देता हूं । यह मानते हुए कि आपने पढ़ा है, हम आगे बढ़ सकते हैं। ध्यान दें कि बैश प्रक्रियाएं दाएं से बाएं; इस प्रकार बैश >/dev/null
पहले देखता है (जो समान है 1>/dev/null
), और स्टडआउट के बजाय फ़ाइल डिस्क्रिप्टर 1 को / dev / null को इंगित करने के लिए सेट करता है। ऐसा करने के बाद, बैश फिर दाईं ओर बढ़ता है और देखता है 2>&1
। यह फ़ाइल डिस्क्रिप्टर 2 को फ़ाइल डिस्क्रिप्टर 1 के समान फ़ाइल को इंगित करने के लिए सेट करता है (और वर्णनकर्ता 1 को स्वयं फ़ाइल नहीं करने के लिए !!!!) ( इस संसाधन को संकेत पर देखें)अधिक जानकारी के लिए)) । चूँकि फ़ाइल डिस्क्रिप्टर 1 अंक / देव / नल के लिए, और फ़ाइल डिस्क्रिप्टर 2 फ़ाइल डिस्क्रिप्टर 1 के रूप में एक ही फ़ाइल के लिए, फ़ाइल डिस्क्रिप्टर 2 अब भी / देव / अशक्त को इंगित करता है। इस प्रकार दोनों फ़ाइल डिस्क्रिप्टर / देव / नल की ओर इशारा करते हैं, और यही कारण है कि कोई आउटपुट प्रदान नहीं किया गया है।
परीक्षण करने के लिए यदि आप वास्तव में अवधारणा को समझते हैं, तो आउटपुट का अनुमान लगाने का प्रयास करें जब हम पुनर्निर्देशन क्रम को बदलते हैं:
(echo "stdout"; echo "stderror" >&2) 2>&1 >/dev/null
stderror
यहाँ तर्क यह है कि बाएं से दाएं का मूल्यांकन, बैश 2> और 1 को देखता है, और इस प्रकार फ़ाइल डिस्क्रिप्टर 1 को उसी स्थान पर इंगित करने के लिए सेट करता है जैसे फ़ाइल डिस्क्रिप्टर 1, यानी स्टडआउट। इसके बाद फ़ाइल डिस्क्रिप्टर 1 (याद रखें कि> / dev / null = 1> / dev / null) को इंगित करने के लिए> / dev / null सेट करता है, इस प्रकार वह सब कुछ हटा दिया जाता है जो आमतौर पर मानक से बाहर भेजा जाता है। इस प्रकार हम सभी के साथ छोड़ दिया गया है जो कि उपधारा (कोष्ठकों में कोड) - यानी "stderror" में stdout को नहीं भेजा गया था। ध्यान देने वाली बात यह है कि भले ही 1 केवल stdout का एक संकेतक है, लेकिन 2 से 1 के माध्यम से सूचक को पुनर्निर्देशित करने से बिंदु 2 की 2>&1
श्रृंखला नहीं बनती है -> 1 -> stdout। यदि ऐसा किया गया, तो कोड को 1 / dev / null पर पुनर्निर्देशित करने के परिणामस्वरूप2>&1 >/dev/null
पॉइंटर चेन 2 -> 1 -> / dev / null देगा, और इस प्रकार कोड कुछ भी उत्पन्न नहीं करेगा, इसके विपरीत जो हमने ऊपर देखा है।
अंत में, मैं यह नोट करूंगा कि ऐसा करने का एक सरल तरीका है:
यहां खंड 3.6.4 से , हम देखते हैं कि हम ऑपरेटर &>
का उपयोग स्टडआउट और स्टडर दोनों को पुनर्निर्देशित करने के लिए कर सकते हैं । इस प्रकार, किसी भी कमांड के 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" )
यह संबंधित है: sdlog को stdOut और stderr लिखना।
यह लगभग काम करता है, लेकिन xinted से नहीं ;;
मैं एक समाधान चाहते थे कि स्टडआउट प्लस स्टडर से आउटपुट लॉग फाइल में लिखा जाए और स्टॉडर कंसोल पर अभी भी है। इसलिए मुझे टी के माध्यम से स्टैडर आउटपुट को डुप्लिकेट करने की आवश्यकता थी।
यह समाधान है जो मैंने पाया:
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
यह बैश-आधारित समाधान STDOUT और STDERR को अलग-अलग ("सॉर्ट -c" के STDERR से या STDERR से "सॉर्ट -h" तक) पाइप कर सकते हैं।
निम्न कार्यों का उपयोग बीट्वेन स्टडआउट / स्टडर और एक लॉगफाइल आउटपुट टॉगल करने की प्रक्रिया को स्वचालित करने के लिए किया जा सकता है।
#!/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"
@ फर्नांडो-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
मुझे पढ़ना आसान लगता है यदि संभव हो तो कोड को इस तरह से उपयोग करके पुन: लिखना संभव है:
function myfunc(){
[...]
}
myfunc &>mylog.log