سؤال

أريد إعادة توجيه كل من stdout وstderr للعملية إلى ملف واحد.كيف أفعل ذلك في باش؟

هل كانت مفيدة؟

المحلول

هنا . يجب أن تكون:

yourcommand &>filename

و(الموجهات كلا stdout وstderr إلى اسم الملف).

نصائح أخرى

do_something 2>&1 | tee -a some_file

وهذا هو الذهاب الى توجيه ستدير إلى المعياري والمعياري لsome_file و طباعة لالمعياري.

ويمكنك إعادة توجيه <م> ستدير إلى <م> المعياري و <م> المعياري في ملف:

some_command >file.log 2>&1 

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

ويفضل هذا الشكل من الشكل الأكثر شعبية و> التي تعمل فقط في باش. في بورن قذيفة يمكن أن تفسر على أنها تشغيل الأمر في الخلفية. كما والشكل هو أكثر قابلية للقراءة 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" الذي يمكنه كتابة/إلحاق العديد من واصفات الملفات (الملفات، والمقابس، والأنابيب، وما إلى ذلك) في وقت واحد:تي 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> >(...) يعيد توجيه STDOUT إلى الأمر بين قوسين
  • ينفذ parens(sub-Shell) قراءة "tee" من STDOUT(pipe) الخاص بـ exec ويعيد التوجيه إلى أمر "logger" عبر أنبوب آخر إلى sub-shell في الأقواس.وفي نفس الوقت يقوم بنسخ نفس الإدخال إلى 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 أن 2 هو واصف ملف (يشير إلى stderr) وليس اسم ملف.إذا تركنا علامة "&"، فسيتم طباعة هذا الأمر 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 من اليسار إلى اليمين؛هكذا يرى باش >/dev/null الأول (وهو نفس 1>/dev/null)، ويقوم بتعيين واصف الملف 1 للإشارة إلى /dev/null بدلاً من stdout.بعد القيام بذلك، يتحرك باش نحو اليمين ويرى 2>&1.يؤدي هذا إلى تعيين واصف الملف 2 للإشارة إلى نفس الملف كواصف الملف 1 (وليس واصف الملف 1 نفسه!!!)(يرى هذا المورد على المؤشرات لمزيد من المعلومات)) .نظرًا لأن واصف الملف 1 يشير إلى /dev/null، ويشير واصف الملف 2 إلى نفس الملف مثل واصف الملف 1، فإن واصف الملف 2 يشير الآن أيضًا إلى /dev/null.وبالتالي يشير كلا واصفي الملف إلى /dev/null، ولهذا السبب لا يتم عرض أي مخرجات.


لاختبار ما إذا كنت تفهم المفهوم حقًا، حاول تخمين النتيجة عندما نقوم بتبديل ترتيب إعادة التوجيه:

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

com.stderror

السبب هنا هو أن التقييم من اليسار إلى اليمين، يرى Bash 2>&1، وبالتالي يقوم بتعيين واصف الملف 2 للإشارة إلى نفس مكان واصف الملف 1، أي stdout.ثم يقوم بتعيين واصف الملف 1 (تذكر أن >/dev/null = 1>/dev/null) للإشارة إلى >/dev/null، وبالتالي حذف كل شيء يتم إرساله عادةً إلى المخرج القياسي.وبالتالي كل ما تبقى لنا هو ما لم يتم إرساله إلى stdout في الغلاف الفرعي (الرمز الموجود بين قوسين) - أي."الخطأ الثابت".الشيء المثير للاهتمام الذي يجب ملاحظته هو أنه على الرغم من أن 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" إلى واصف الملف "b" الذي يشير إلى الملف "f"، يؤدي إلى أن يشير واصف الملف "a" إلى نفس مكان واصف الملف b - الملف "f".لا تشكل سلسلة من المؤشرات a -> b -> f
  • وبسبب ما سبق، فإن النظام مهم، 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" )

ومن ذات الصلة: الكتابة المعياري وستدير إلى سيسلوغ

وكاد العمل، ولكن ليس من xinted؛ (

كنت أرغب في حل لكتابة الإخراج من stdout بالإضافة إلى stderr في ملف سجل ولا يزال stderr على وحدة التحكم.لذلك كنت بحاجة إلى تكرار إخراج stderr عبر نقطة الإنطلاق.

هذا هو الحل الذي وجدته:

command 3>&1 1>&2 2>&3 1>>logfile | tee -a logfile
  • أول مبادلة stderr وstdout
  • ثم قم بإلحاق stdout بملف السجل
  • قم بتوجيه الأنبوب stderr إلى نقطة الإنطلاق وإلحاقه أيضًا بملف السجل

بالنسبة للحالة، عندما تكون "الأنابيب" ضرورية، يمكنك استخدام:

|&

على سبيل المثال:

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 بشكل منفصل (من STDERR من "sort -c" أو من STDERR إلى "sort -h").

و"أسهل" طريقة (bash4 فقط): ls * 2>&- 1>&-

والوظائف التالية يمكن استخدامها لأتمتة عملية تبديل مخرجات beetwen المعياري / ستدير وملف السجل.

#!/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، وسوف تعطي خطأ "القيادة لاغية غير صالح".

و @فرناندو-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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top