বাশ স্ক্রিপ্টের মধ্যে থেকেই ফাইল লগ করতে স্টডআউট এর কপি পুনর্নির্দেশ করুন


235

আমি জানি যে কীভাবে কোনও ফাইলে স্টডআউটকে পুনর্নির্দেশ করতে হবে:

exec > foo.log
echo test

এটি foo.log ফাইলটিতে 'পরীক্ষা' রাখবে।

এখন আমি আউটপুটটিকে লগ ফাইলে পুনর্নির্দেশ করতে এবং স্টডআউটে রাখতে চাই

অর্থাৎ এটি স্ক্রিপ্টের বাইরে থেকেও তুচ্ছভাবে করা যেতে পারে:

script | tee foo.log

তবে আমি এটি স্ক্রিপ্টের মধ্যেই ঘোষণা করতে চাই

আমি চেষ্টা করেছিলাম

exec | tee foo.log

কিন্তু এটি কার্যকর হয়নি।


3
আপনার প্রশ্নটি খারাপভাবে বর্ণিত। আপনি যখন ডাকা 'Exec> foo.log', স্ক্রিপ্ট stdout- এ হল ফাইল foo.log। আমার মনে হয় আপনি বোঝাতে চেয়েছেন যে আপনি আউটপুটটি foo.log এবং tty তে যেতে চান, যেহেতু foo.log স্টাডআউট যাচ্ছে।
উইলিয়াম পার্সেল

আমি যা করতে চাই তা হ'ল | 'এক্সিকিউটিভ' তে এটি আমার জন্য নিখুঁত হবে, যেমন "এক্সিকিউট | টি ফু.লগ", দুর্ভাগ্যক্রমে আপনি এক্সিকিউটি কলটিতে পাইপ পুনর্নির্দেশ ব্যবহার করতে পারবেন না
ভাইটালি কুশনার

উত্তর:


297
#!/usr/bin/env bash

# Redirect stdout ( > ) into a named pipe ( >() ) running "tee"
exec > >(tee -i logfile.txt)

# Without this, only stdout would be captured - i.e. your
# log file would not contain any error messages.
# SEE (and upvote) the answer by Adam Spiers, which keeps STDERR
# as a separate stream - I did not want to steal from him by simply
# adding his answer to mine.
exec 2>&1

echo "foo"
echo "bar" >&2

নোট করুন যে এটি bash, না sh। আপনি যদি স্ক্রিপ্টটির সাথে অনুরোধ করেন তবে আপনি sh myscript.shএর লাইনে একটি ত্রুটি পাবেন syntax error near unexpected token '>'

আপনি যদি সিগন্যাল ট্র্যাপগুলির সাথে কাজ করছেন, tee -iযদি কোনও সংকেত দেখা দেয় তবে আপনি আউটপুটটির ব্যত্যয় এড়াতে বিকল্পটি ব্যবহার করতে চাইতে পারেন । (মন্তব্যের জন্য জেমস থমাসমুন ১৯৯৯ কে ধন্যবাদ।)


যে সরঞ্জামগুলি পাইপ বা টার্মিনালে লিখেছে কিনা তার উপর নির্ভর করে তাদের আউটপুট পরিবর্তন করে ( lsউদাহরণস্বরূপ রঙ এবং কলামযুক্ত আউটপুট ব্যবহার করে) উপরের কনস্ট্রাক্টটিকে আবিষ্কার করবে যার অর্থ তারা পাইপে আউটপুট দেয়।

রঙিনকরণ / কলামাইজিং (যেমন ls -C --color=always) প্রয়োগ করার বিকল্প রয়েছে । নোট করুন যে এর ফলে রঙিন কোডগুলি লগফাইলে পাশাপাশি লিখিত হবে, এটি কম পাঠযোগ্য হবে।


5
বেশিরভাগ সিস্টেমে টি বাফার করা হয়, সুতরাং স্ক্রিপ্ট শেষ না হওয়া পর্যন্ত আউটপুট না আসতে পারে। এছাড়াও, যেহেতু এই টি একটি শিশু প্রক্রিয়া নয়, সাব-শেইলে চলছে, তাই কলিং প্রক্রিয়াতে আউটপুট সিঙ্ক্রোনাইজ করার জন্য অপেক্ষা করা যায় না। আপনি যা চান তা বোগোমাইস.আর.এস.রাইনবোউস.ইউটি

14
@Barry: POSIX নির্দিষ্ট করে teeতার আউটপুট বাফার করা উচিত নয়। যদি এটি বেশিরভাগ সিস্টেমে বাফার করে তবে এটি বেশিরভাগ সিস্টেমে বিভক্ত। এটি teeআমার সমাধানের নয়, বাস্তবায়নের সমস্যা ।
দেবসোলার

3
@ সেবাস্তিয়ান: execখুব শক্তিশালী, তবে খুব জড়িত। আপনি বর্তমান স্টডআউটকে একটি ভিন্ন ফাইলডেস্কিপ্টরে "ব্যাকআপ" রাখতে পারেন, পরে এটি পুনরুদ্ধার করতে পারেন। গুগল "ব্যাশ এক্সিকিউট টিউটোরিয়াল", সেখানে প্রচুর অ্যাডভান্সড স্টাফ রয়েছে।
দেবসোলার

2
@ অ্যাডামস্পিয়ার্স: আমি নিশ্চিত নই যে ব্যারি সম্পর্কে কী ছিল। নতুন প্রক্রিয়া শুরু না করার execজন্য বাশদের নথিভুক্ত করা হয়েছে , >(tee ...)এটি একটি স্ট্যান্ডার্ড নামক পাইপ / প্রক্রিয়া প্রতিস্থাপন, এবং &পুনঃনির্দেশে অবশ্যই ব্যাকগ্রাউন্ডিংয়ের সাথে কোনও সম্পর্ক নেই ...? :-)
দেবসোলার

11
আমি পাস -iকরার পরামর্শ দিচ্ছি tee। অন্যথায়, সংকেত বাধা (ট্র্যাপস) মূল স্ক্রিপ্টে স্টডআউটকে ব্যাহত করবে। উদাহরণস্বরূপ, আপনার যদি একটি থাকে trap 'echo foo' EXITএবং তারপরে টিপুন তবে ctrl+cআপনি " ফু " দেখতে পাবেন না । সুতরাং আমি উত্তর পরিবর্তন করতে হবে exec &> >(tee -ia file)
জেমস থমাসমুন 1979

173

গৃহীত উত্তরটি পৃথক ফাইল বর্ণনাকারী হিসাবে STDERR সংরক্ষণ করে না। এর মানে

./script.sh >/dev/null

barটার্মিনালে আউটপুট হবে না , শুধুমাত্র লগফাইলে এবং

./script.sh 2>/dev/null

উভয় fooএবং barটার্মিনাল আউটপুট হবে । স্পষ্টতই এটি এমন আচরণ নয় যা সাধারণ ব্যবহারকারী প্রত্যাশা করে। এটি দুটি পৃথক টি প্রক্রিয়া একই লগ ফাইলে সংযোজন দুটি ব্যবহার করে স্থির করা যেতে পারে:

#!/bin/bash

# See (and upvote) the comment by JamesThomasMoon1979 
# explaining the use of the -i option to tee.
exec >  >(tee -ia foo.log)
exec 2> >(tee -ia foo.log >&2)

echo "foo"
echo "bar" >&2

(নোট করুন যে উপরেরগুলি প্রথমে লগ ফাইলটি ছাঁটাই করে না - আপনি যদি এমন আচরণ চান তবে আপনার যুক্ত করা উচিত

>foo.log

স্ক্রিপ্ট শীর্ষে।)

এর POSIX.1-2-2008 স্পেসিফিকেশনটিরtee(1) প্রয়োজন হয় যে আউটপুটটি unbuffered করা হবে, এমনকি লাইন-বাফারও নয়, সুতরাং এক্ষেত্রে এটি সম্ভব যে STDOUT এবং STDERR একই লাইনে শেষ হতে পারে foo.log; তবে যে টার্মিন্যালে ঘটতে পারে, তাই লগ ফাইল কি একটি বিশ্বস্ত প্রতিফলন হতে হবে পারে , টার্মিনাল দেখা যেতে যদি না এটি একটি সঠিক আয়না। আপনি যদি STDOUT লাইনগুলি STDERR রেখাগুলি থেকে পরিষ্কারভাবে আলাদা করতে চান তবে দুটি লগ ফাইল ব্যবহার করার কথা বিবেচনা করুন, সম্ভবত পরবর্তী সময়ে কালানুক্রমিক পুনঃস্থাপনের অনুমতি দেওয়ার জন্য প্রতিটি লাইনের তারিখ স্ট্যাম্প উপসর্গ সহ।


কিছু কারণে, আমার ক্ষেত্রে, যখন স্ক্রিপ্টটি সি-প্রোগ্রাম সিস্টেম () কল থেকে কার্যকর করা হয়, মূল স্ক্রিপ্টটি প্রস্থান করার পরেও দুটি টি উপ-প্রক্রিয়া বিদ্যমান থাকে। : তাই আমি ভালো যাত্রীর সঙ্গের নিজলটবহর যোগ করার জন্য ছিলexec > >(tee -a $LOG) trap "kill -9 $! 2>/dev/null" EXIT exec 2> >(tee -a $LOG >&2) trap "kill -9 $! 2>/dev/null" EXIT
alveko

15
আমি পাস -iকরার পরামর্শ দিচ্ছি tee। অন্যথায়, সংকেত বাধা (ট্র্যাপস) স্ক্রিপ্টের স্টডআউটকে ব্যাহত করবে। উদাহরণস্বরূপ, আপনি trap 'echo foo' EXITএবং তারপরে চাপলে ctrl+cআপনি " foo " দেখতে পাবেন না । সুতরাং আমি উত্তর পরিবর্তন করতে হবে exec > >(tee -ia foo.log)
জেমস থমাসমুন 1979

এর উপর ভিত্তি করে আমি কিছুটা "উত্সযোগ্য" স্ক্রিপ্ট তৈরি করেছি। এগুলি স্ক্রিপ্টে . logবা তাদের মতো ব্যবহার করতে পারেন . log foo.log: sam.nipl.net/sh/log sam.nipl.net/sh/log-a
স্যাম ওয়াটকিন্স

1
এই পদ্ধতির সমস্যাটি হ'ল বার্তাগুলি STDOUTপ্রথমে একটি ব্যাচ হিসাবে প্রদর্শিত হবে এবং তারপরে বার্তাগুলি STDERRপ্রদর্শিত হবে। এগুলি সাধারণত প্রত্যাশিতভাবে ইন্টারলিভড হয় না।
সিএমসিডিগ্রাগনকাই

28

ব্যস্তবক্স, ম্যাকোস ব্যাশ এবং নন-ব্যাশ শেলগুলির সমাধান

গৃহীত উত্তর অবশ্যই বাশের সেরা পছন্দ। আমি ব্যাশ অ্যাক্সেস ছাড়াই একটি ব্যস্তবক্স পরিবেশে কাজ করছি এবং এটি exec > >(tee log.txt)সিনট্যাক্সটি বুঝতে পারে না । এটি exec >$PIPEঠিকমতো করে না , নামের পাইপ হিসাবে একই নামের একটি সাধারণ ফাইল তৈরি করার চেষ্টা করে যা ব্যর্থ হয় এবং স্তব্ধ হয়ে যায়।

আশা করি এটি কারও কাছে উপকারী হবে যার কাছে ব্যাশ নেই।

এছাড়াও, নামযুক্ত পাইপ ব্যবহার করা যে কোনও ব্যক্তির পক্ষে, এটি নিরাপদ rm $PIPE, কারণ এটি ভিএফএস থেকে পাইপটিকে লিঙ্কমুক্ত করে, তবে যে প্রক্রিয়াগুলি এটি ব্যবহার করে তারা শেষ না হওয়া পর্যন্ত তার উপর রেফারেন্স গণনা বজায় রাখে।

নোট করুন $ * এর ব্যবহার অগত্যা নিরাপদ নয়।

#!/bin/sh

if [ "$SELF_LOGGING" != "1" ]
then
    # The parent process will enter this branch and set up logging

    # Create a named piped for logging the child's output
    PIPE=tmp.fifo
    mkfifo $PIPE

    # Launch the child process with stdout redirected to the named pipe
    SELF_LOGGING=1 sh $0 $* >$PIPE &

    # Save PID of child process
    PID=$!

    # Launch tee in a separate process
    tee logfile <$PIPE &

    # Unlink $PIPE because the parent process no longer needs it
    rm $PIPE    

    # Wait for child process, which is running the rest of this script
    wait $PID

    # Return the error code from the child process
    exit $?
fi

# The rest of the script goes here

আমি এখন পর্যন্ত এটিই একমাত্র সমাধান দেখেছি যা
ম্যাকটিতে

19

আপনার স্ক্রিপ্ট ফাইলের ভিতরে, সমস্ত কমান্ডগুলি প্রথম বন্ধনীগুলির মধ্যে রাখুন:

(
echo start
ls -l
echo end
) | tee foo.log


হ্যাঁ, আমি এটি বিবেচনা করেছি, তবে এটি বর্তমান শেল স্টডআউটের পুনঃনির্দেশ নয়, এটি ধরণের ধরণের, আপনি আসলে একটি সাবস্কেল চালাচ্ছেন এবং এটির উপর নিয়মিত পাইপার পুনঃনির্দেশ করছেন doing কাজ চিন্তা। আমি এটি এবং "পুচ্ছ - foo.log &" সমাধানের সাথে বিভক্ত। এটির চেয়ে আরও ভাল একটি পৃষ্ঠ হতে পারে কিনা তা দেখার জন্য একটু অপেক্ষা করবেন। যদি সম্ভবত স্থির হতে না পারে;)
ভিটালি কুশনার

8
shell the বর্তমান শেল পরিবেশে একটি তালিকা কার্যকর করে। () একটি সাব-শেল পরিবেশে একটি তালিকা কার্যকর করে a

অভিশাপ। ধন্যবাদ. সেখানে গ্রহণযোগ্য উত্তরটি আমার পক্ষে কার্যকর হয়নি, উইন্ডোজ সিস্টেমে মিংডাব্লুয়ের অধীনে চালনার জন্য একটি স্ক্রিপ্ট নির্ধারণের চেষ্টা করা হয়েছিল। এটি অভিযোগ করেছে, আমি বিশ্বাস করি, অযৌক্তিক প্রক্রিয়া প্রতিস্থাপন সম্পর্কে। নিম্নলিখিতটি পরিবর্তনের পরে, stderr এবং stdout উভয়কে ক্যাপচার করতে এই উত্তরটি ঠিক কাজ করেছে: `` `-) | টি foo.log +) 2> & 1 | tee foo.log
জন কার্টার

14

স্যাশলগে ব্যাশ স্ক্রিপ্ট লগ করার সহজ উপায়। স্ক্রিপ্ট আউটপুট /var/log/syslogস্ট্ডার এর মাধ্যমে এবং উভয়ই উপলব্ধ । syslog টাইমস্ট্যাম্প সহ দরকারী মেটাডেটা যুক্ত করবে।

শীর্ষে এই লাইন যুক্ত করুন:

exec &> >(logger -t myscript -s)

বিকল্পভাবে, লগটি একটি পৃথক ফাইলে প্রেরণ করুন:

exec &> >(ts |tee -a /tmp/myscript.output >&2 )

এর জন্য প্রয়োজন moreutils( tsকমান্ডের জন্য, যা টাইমস্ট্যাম্প যুক্ত করে)।


10

গৃহীত উত্তরটি ব্যবহার করে আমার স্ক্রিপ্টটি ব্যতিক্রমী পর্দার মধ্যে আমার বাকী স্ক্রিপ্টটি চালিয়ে রেখে (প্রথম 'এক্সিকিউটিউট> (টি ...)' এর পরে খুব ব্যতিক্রমীভাবে ফিরে আসতে থাকবে। আমার কাজটি করার মতো সমাধানটি আমি না পাওয়ায় আমি সমস্যার সমাধানের জন্য আরও একটি সমাধান / কাজ পেয়েছি:

# Logging setup
logfile=mylogfile
mkfifo ${logfile}.pipe
tee < ${logfile}.pipe $logfile &
exec &> ${logfile}.pipe
rm ${logfile}.pipe

# Rest of my script

এটি স্ক্রিপ্ট থেকে আউটপুট প্রক্রিয়া থেকে, পাই-এর মাধ্যমে 'টি'র উপ পটভূমি প্রক্রিয়াতে চলে যায় যা ডিস্কে এবং স্ক্রিপ্টের মূল স্টাডাউটে সমস্ত কিছু লগ করে।

নোট করুন যে 'এক্সিকিউটিভ &>' স্ট্যান্ডআউট এবং স্টডার উভয়ই পুনঃনির্দেশ করে, আমরা চাইলে সেগুলি আলাদাভাবে পুনর্নির্দেশ করতে পারি, বা আমরা কেবল স্টডআউট চাইলে 'এক্সিকিউটিউট' 'এ পরিবর্তন করতে পারি।

এমনকি আপনি পাইপটি ফাইল সিস্টেম থেকে স্ক্রিপ্টের শুরুতে সরিয়ে ফেলা হয়েছে এটি প্রক্রিয়া শেষ না হওয়া পর্যন্ত এটি চলতে থাকবে। আমরা কেবল আরএম-লাইনের পরে ফাইলের নামটি ব্যবহার করে এটি উল্লেখ করতে পারি না।


ডেভিড জেড থেকে দ্বিতীয় ধারণা হিসাবে একই উত্তর । এর মন্তব্যগুলি দেখুন। +1 ;-)
অলিব্রে

ভাল কাজ করে. আমি এর $logfileঅংশ বুঝতে পারছি না tee < ${logfile}.pipe $logfile &। বিশেষত, আমি সম্পূর্ণ বর্ধিত কমান্ড লগ লাইনগুলি (থেকে set -x) ফাইলগুলিতে ক্যাপচার করার জন্য এটি পরিবর্তন করার চেষ্টা করেছি যখন স্টাডআউটে কেবল '+' না করেই লাইনগুলি প্রদর্শন করে পরিবর্তিত হয়েছি (tee | grep -v '^+.*$') < ${logfile}.pipe $logfile &তবে সম্পর্কিত একটি ত্রুটি বার্তা পেয়েছি $logfile। আপনি কি teeআরও একটু বিস্তারিতভাবে লাইনটি ব্যাখ্যা করতে পারেন ?
ক্রিস জনসন

আমি এটি পরীক্ষা করে দেখলাম এবং মনে হচ্ছে এই উত্তরটি STDERR সংরক্ষণ করে না (এটি STDOUT এর সাথে একত্রীকরণ করা হয়), সুতরাং যদি আপনি ত্রুটি সনাক্তকরণ বা অন্য পুনঃনির্দেশের জন্য পৃথক স্ট্রিমগুলির উপর নির্ভর করেন তবে আপনার অ্যাডামের উত্তরের দিকে নজর দেওয়া উচিত।
হিরোসিসি


1

এক্সিকিউটিভের ভিত্তিতে যে কোনও সমাধান নিয়ে আমি স্বাচ্ছন্দ্য বোধ করতে পারি না। আমি সরাসরি টি ব্যবহার করতে পছন্দ করি, তাই অনুরোধ করার সময় আমি স্ক্রিপ্টটি টি দিয়েই কল করি:

# my script: 

check_tee_output()
{
    # copy (append) stdout and stderr to log file if TEE is unset or true
    if [[ -z $TEE || "$TEE" == true ]]; then 
        echo '-------------------------------------------' >> log.txt
        echo '***' $(date) $0 $@ >> log.txt
        TEE=false $0 $@ 2>&1 | tee --append log.txt
        exit $?
    fi 
}

check_tee_output $@

rest of my script

এটি আপনাকে এটি করতে দেয়:

your_script.sh args           # tee 
TEE=true your_script.sh args  # tee 
TEE=false your_script.sh args # don't tee
export TEE=false
your_script.sh args           # tee

আপনি এটি কাস্টমাইজ করতে পারেন, উদাহরণস্বরূপ টি-টু ডিফল্ট এর পরিবর্তে টিউ তৈরি করুন, পরিবর্তে টিআইইকে লগ ফাইলটি ধরে রাখুন ইত্যাদি I আমার ধারণা এই সমাধানটি জবারলো এর অনুরূপ, তবে সরল, সম্ভবত আমার সীমাবদ্ধতা রয়েছে যা আমি এখনও পারিনি।


-1

এগুলির কোনওটিই একটি নিখুঁত সমাধান নয় তবে এখানে কয়েকটি চেষ্টা করা যেতে পারে:

exec >foo.log
tail -f foo.log &
# rest of your script

অথবা

PIPE=tmp.fifo
mkfifo $PIPE
exec >$PIPE
tee foo.log <$PIPE &
# rest of your script
rm $PIPE

দ্বিতীয়টি যদি আপনার স্ক্রিপ্টে কিছু ভুল হয়ে থাকে তবে এটি কোনও সমস্যা হতে পারে বা নাও করতে পারে (যা সম্ভবত rmপরে আপনি এটি প্যারেন্ট শেলটিতে করতে পারেন) এর আশেপাশে বসে একটি পাইপ ফাইল রেখে দেবে ।


1
লেজটি দ্বিতীয় স্ক্রিপ্ট টিতে ব্লক হয়ে যাওয়ার পরে একটি চলমান প্রক্রিয়া ছেড়ে যাবে, বা আপনার এটি চালাতে হবে এবং এক্ষেত্রে এটি প্রথম প্রক্রিয়া হিসাবে প্রসেসটি ছেড়ে যাবে।
ভাইটালি কুশনার

@ ভিটিলি: ওফফ, ব্যাকগ্রাউন্ডে ভুলে গেছি tee- আমি সম্পাদনা করেছি। যেমনটি আমি বলেছি, উভয়ই একটি নিখুঁত সমাধান নয়, তবে পিতামাতার শেল বন্ধ হয়ে গেলে পটভূমি প্রক্রিয়াগুলি মারা যাবে, সুতরাং তাদের চিরতরে হোগিং সংস্থান সম্পর্কে আপনার চিন্তা করার দরকার নেই।
ডেভিড জেড

1
হ্যাঁ: এগুলি দেখতে আকর্ষণীয়, তবে লেজ-ফ এর আউটপুটও foo.log এ চলেছে। এক্সিকিউটের আগে লেজ -f চালিয়ে আপনি এটি ঠিক করতে পারেন তবে পিতামাতার সমাপ্তির পরে লেজটি এখনও চালানো বাকি রয়েছে। আপনাকে সম্ভবত স্পষ্টভাবে 0 ফাঁদে এটি হত্যা করতে হবে
উইলিয়াম পার্সেল

Yeap। স্ক্রিপ্টটি ব্যাকগ্রাউন্ড করা থাকলে এটি সমস্ত প্রক্রিয়া ছেড়ে যায়।
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.