पाइप लेखन को बाधित किए बिना किसी फ़ाइल को खाली करना


12

मेरे पास एक प्रोग्राम है जिसका आउटपुट मैं एक लॉग फ़ाइल में रीडायरेक्ट करता हूं:

./my_app > log

मैं समय-समय पर (मांग पर) लॉग को खाली (यानी खाली) करना चाहता हूं और विभिन्न चीजों की कोशिश की

cat "" > log

हालांकि यह हमेशा लगता है कि मूल पाइप तब बाधित हो जाता है और प्रोग्राम अपने आउटपुट को लॉग फ़ाइल में अब पुनर्निर्देशित नहीं करता है।

क्या ऐसा करने का कोई तरीका है?

अपडेट करें

ध्यान दें कि मैं आउटपुट का उत्पादन करने वाले एप्लिकेशन को संशोधित नहीं कर सकता। यह बस स्टडआउट करने के लिए इसे बाहर थूकता है और मैं इसे एक लॉग में सहेजना चाहता हूं ताकि मैं ज़रूरत पड़ने पर इसका निरीक्षण कर सकूं और जब चाहूँ इसे साफ़ कर सकूँ। हालाँकि मुझे एप्लिकेशन को पुनः आरंभ करने की आवश्यकता नहीं है।


यही कारण है कि आप आमतौर पर चीजों को लॉग करने के लिए लॉगिंग डेमॉन का उपयोग करते हैं ...
कीवी

@ कीवी आप इस बारे में विस्तार से बता सकते हैं कि समस्या का समाधान कैसे होगा?
बैंगनाब

अच्छी तरह से आप आमतौर पर एक लॉग डेमॉन का उपयोग करते हैं या अपने ऐप को लॉग को हैंडल करने देते हैं, क्योंकि चीजों को आउटपुट में लिखना और रीडायरेक्ट करना विश्वसनीय नहीं है। आप पर एक नज़र रखना चाहिए syslogdयाlogrotate
Kiwy

2
क्या आप काम करते हैं ./my_app >> log( यदि आप बल देने के cp /dev/null logलिए ) और इसे कम करने के लिए काम करते हैं?
मार्क प्लॉटनिक

1
आपको क्या त्रुटि संदेश मिलता है? आप क्या व्यवहार देखते हैं? "अब लॉग फ़ाइल में इसके आउटपुट को रीडायरेक्ट नहीं करता है" बहुत विशिष्ट नहीं है। इसके अलावा, cat "" > logकोई मान्य catआदेश नहीं है क्योंकि कोई फ़ाइल नहीं है ""
मिकेल

जवाबों:


13

इस समस्या का एक और रूप लंबे समय तक चलने वाले अनुप्रयोगों के साथ होता है जिनके लॉग समय-समय पर घुमाए जाते हैं। यहां तक ​​कि अगर आप मूल लॉग (जैसे mv log.txt log.1) को स्थानांतरित करते हैं और किसी भी वास्तविक लॉगिंग होने से पहले उसी नाम की फ़ाइल के साथ इसे तुरंत बदल देते हैं, यदि प्रक्रिया फ़ाइल को खुले रखती है, तो यह या तो लेखन को समाप्त कर देगा log.1(क्योंकि अभी भी हो सकता है खुले भीतर) या कुछ भी नहीं है।

इससे निपटने का एक सामान्य तरीका (सिस्टम लॉगर खुद इस तरह से काम करता है) प्रक्रिया में एक सिग्नल हैंडलर को लागू करना है जो इसके लॉग को बंद और फिर से खोल देगा। फिर, जब भी आप लॉग को हटाना या हटाना चाहते हैं (लॉग को हटाकर), उस सिग्नल को तुरंत बाद की प्रक्रिया में भेजें।

यहाँ बैश के लिए एक सरल प्रदर्शन है - मेरे टेढ़े-मेढ़े कौशल को क्षमा करें (लेकिन अगर आप इसे सर्वोत्तम प्रथाओं आदि के लिए संपादित करने जा रहे हैं, तो कृपया सुनिश्चित करें कि आप पहले कार्यक्षमता को समझते हैं और संपादित करने से पहले अपने संशोधन का परीक्षण करें):

#!/bin/bash

trap sighandler INT

function sighandler () {
    touch log.txt
    exec &> log.txt
}

echo $BASHPID
exec &> log.txt

count=0;
while [ $count -lt 60 ]; do
    echo "$BASHPID Count is now $count"
    sleep 2
    ((count++))
done          

पृष्ठभूमि में फोर्क करके इसे प्रारंभ करें:

> ./test.sh &
12356

ध्यान दें कि यह अपने पीआईडी ​​को टर्मिनल को रिपोर्ट करता है और फिर लॉगिंग शुरू करता है log.txt। अब आपके पास खेलने के लिए 2 मिनट हैं। कुछ सेकंड रुकें और कोशिश करें:

> mv log.txt log.1 && kill -s 2 12356

बस सादा kill -2 12356यहाँ भी आपके लिए काम कर सकता है। सिग्नल 2 SIGINT है (यह भी Ctrl-C ही करता है, इसलिए आप इसे अग्रभूमि में आज़मा सकते हैं और लॉगफ़ाइल को किसी अन्य टर्मिनल से हटा सकते हैं या हटा सकते हैं), जिसे trapफंसाना चाहिए। जाँच करने के लिए;

> cat log.1
12356 Count is now 0
12356 Count is now 1
12356 Count is now 2
12356 Count is now 3
12356 Count is now 4
12356 Count is now 5
12356 Count is now 6
12356 Count is now 7
12356 Count is now 8
12356 Count is now 9
12356 Count is now 10
12356 Count is now 11
12356 Count is now 12
12356 Count is now 13
12356 Count is now 14

अब देखते हैं कि क्या यह अभी log.txtभी हम इसे स्थानांतरित करने के लिए लिख रहे हैं :

> cat log.txt
12356 Count is now 15
12356 Count is now 16
12356 Count is now 17
12356 Count is now 18
12356 Count is now 19
12356 Count is now 20
12356 Count is now 21

ध्यान दें कि यह सही जा रहा है जहां इसे छोड़ा गया है। यदि आप रिकॉर्ड नहीं रखना चाहते हैं तो उसे हटाकर लॉग को साफ करें

> rm -f log.txt && kill -s 2 12356

चेक:

> cat log.txt
12356 Count is now 29
12356 Count is now 30
12356 Count is now 31
12356 Count is now 32
12356 Count is now 33
12356 Count is now 34
12356 Count is now 35
12356 Count is now 36

अभी चल रहा है।

आप एक निष्पादित उपप्रकार के लिए एक शेल स्क्रिप्ट में ऐसा नहीं कर सकते, दुर्भाग्य से, क्योंकि अगर यह अग्रभूमि में है, तो बैश के अपने सिग्नल हैंडलर ( trapएस) निलंबित हैं, और यदि आप इसे पृष्ठभूमि में कांटा करते हैं, तो आप इसे फिर से असाइन नहीं कर सकते हैं उत्पादन। यानी, यह कुछ ऐसा है जिसे आपको अपने आवेदन में लागू करना है।

हालाँकि...

यदि आप एप्लिकेशन को संशोधित नहीं कर सकते हैं (उदाहरण के लिए, क्योंकि आपने इसे नहीं लिखा था), मेरे पास एक सीएलआई उपयोगिता है जिसे आप एक मध्यस्थ के रूप में उपयोग कर सकते हैं। आप इसका एक सरल संस्करण भी एक स्क्रिप्ट में लागू कर सकते हैं जो लॉग में एक पाइप के रूप में कार्य करता है:

#!/bin/bash

trap sighandler INT

function sighandler () {
    touch log.txt
    exec 1> log.txt
}

echo "$0 $BASHPID"
exec 1> log.txt

count=0;
while read; do
    echo $REPLY
done  

इसको बुलाते हैं pipetrap.sh। अब हमें परीक्षण के लिए एक अलग कार्यक्रम की आवश्यकता है, उस एप्लिकेशन की नकल करें जिसे आप लॉग इन करना चाहते हैं:

#!/bin/bash

count=0
while [ $count -lt 60 ]; do
    echo "$BASHPID Count is now $count"
    sleep 2
    ((count++))
done           

यह होगा test.sh:

> (./test.sh | ./pipetrap.sh) &
./pipetrap.sh 15859

ये अलग पीआईडी ​​के साथ दो अलग-अलग प्रक्रियाएं हैं। क्लीयर test.shआउटपुट को साफ़ करने के लिए , जिसके माध्यम से फ़नल किया जा रहा है pipetrap.sh:

> rm -f log.txt && kill -s 2 15859

चेक:

>cat log.txt
15858 Count is now 6
15858 Count is now 7
15858 Count is now 8

15858, test.shअभी भी चल रहा है और इसका आउटपुट लॉग किया जा रहा है। इस मामले में, आवेदन में कोई संशोधन की आवश्यकता नहीं है।


अच्छी व्याख्याओं के लिए धन्यवाद। हालाँकि मेरे मामले में मैं आपके समाधान को लागू करने के लिए आवेदन को संशोधित नहीं कर सकता।
बंगनाब

2
यदि आप अपने एप्लिकेशन में सिग्नल हैंडलर लागू नहीं कर सकते हैं (क्योंकि आप इसे संशोधित नहीं कर सकते हैं), तो आप सिग्नल ट्रैप के माध्यम से लॉग को पाइप करने के लिए इस तकनीक का उपयोग कर सकते हैं - "हालांकि ..." के
गोल्डीलॉक्स

ठीक है, मैं इसे एक कोशिश करूँगा और आपको बताऊंगा कि यह कैसे चला गया।
बैंगनाब

मेरे पास अंत में इसके लिए C में एक CLI ऐप लिखा है (क्षमा करें कि यह मूल रूप से अपेक्षित से थोड़ा अधिक लंबा है): cognitivedissonance.ca/cogware/pipelog
goldilocks

6

टी एल; डॉ

अपनी लॉग फ़ाइल को एपेंड मोड में खोलें :

cmd >> log

फिर, आप इसे सुरक्षित रूप से काट सकते हैं:

: > log

विवरण

बॉर्न-जैसे शेल के साथ, 3 मुख्य तरीके हैं जो एक फ़ाइल लिखने के लिए खुले हो सकते हैं। में लिखने केवल ( >), पढ़ा + लिखने ( <>) या संलग्न (और लिखने-ही, >>) मोड।

पहले दो में, कर्नेल आपके द्वारा वर्तमान स्थिति को याद करता है (आपके द्वारा, मेरा मतलब है, खुली हुई फ़ाइल का विवरण , सभी फ़ाइल विवरणकर्ताओं द्वारा साझा किया गया है, जो आपके द्वारा फ़ाइल खोले जाने के बाद उसे फोर्क करके विरासत में मिला है)। फ़ाइल।

जब तुम करोगे:

cmd > log

logके स्टडआउट के लिए शेल द्वारा केवल लेखन मोड में खुला है cmd

cmd(इसकी प्रारंभिक प्रक्रिया खोल और सभी संभावित बच्चों द्वारा पैदा की जाती है) जब उनके स्टडआउट को लिखते हैं, तो उस फ़ाइल पर साझा की गई खुली फ़ाइल विवरण द्वारा आयोजित वर्तमान कर्सर स्थिति पर लिखें ।

उदाहरण के लिए, यदि cmdप्रारंभ में लिखता है zzz, तो फ़ाइल में स्थिति बाइट ऑफ़सेट 4 में होगी, और अगली बार cmdया उसके बच्चे फ़ाइल में लिखते हैं , यहीं पर डेटा की परवाह किए बिना लिखा जाएगा कि फ़ाइल बढ़ी है या अंतराल में सिकुड़ गई है ।

यदि फ़ाइल सिकुड़ गई है, उदाहरण के लिए यदि इसे ए के साथ छोटा किया गया है

: > log

और cmdलिखते हैं xx, उन xxऑफसेट पर लिखा जाएगा 4, और पहले 3 वर्ण NUL वर्णों द्वारा प्रतिस्थापित किया जाएगा।

$ exec 3> log # open file on fd 3.
$ printf zzz >&3
$ od -c log
0000000   z   z   z
0000003
$ printf aaaa >> log # other open file description -> different cursor
$ od -c log
0000000   z   z   z   a   a   a   a
0000007
$ printf bb >&3 # still write at the original position
$ od -c log
0000000   z   z   z   b   b   a   a
0000007
$ : > log
$ wc log
0 0 0 log
$ printf x >&3
$ od -c log
0000000  \0  \0  \0  \0  \0   x
0000006

इसका मतलब है कि आप एक ऐसी फाइल को नहीं खोल सकते हैं जो केवल लिखने के तरीके में खुली हो (और यह पढ़ने + लिखने के लिए भी वैसी ही हो ) जैसे कि आप करते हैं, ऐसी प्रक्रियाएं जिनके पास फाइल के लिए फाइल डिस्क्रिप्टर खुला था, शुरुआत में NUL वर्ण छोड़ देगा। फ़ाइल (वे, ओएस / एक्स को छोड़कर, आमतौर पर डिस्क पर जगह नहीं लेते हैं, हालांकि, वे विरल फाइलें बन जाते हैं)।

इसके बजाय (और आप देखेंगे कि अधिकांश एप्लिकेशन ऐसा करते हैं कि जब वे लॉग फाइल करने के लिए लिखते हैं), तो आपको फ़ाइल को एपेंड मोड में खोलना चाहिए :

cmd >> log

या

: > log && cmd >> log

यदि आप एक खाली फ़ाइल पर शुरू करना चाहते हैं।

एपेंड मोड में, सभी राइट्स फाइल के अंत में बने होते हैं, भले ही अंतिम लेखन कहां था:

$ exec 4>> log
$ printf aa >&4
$ printf x >> log
$ printf bb >&4
$ od -c log
0000000   a   a   x   b   b
0000005
$ : > log
$ printf cc >&4
$ od -c log
0000000   c   c
0000002

यह भी सुरक्षित है जैसे कि दो प्रक्रियाओं में (उस तरह से) फ़ाइल गलती से (उदाहरण के लिए यदि आपने एक ही डेमॉन के दो उदाहरण शुरू किए हैं), तो उनका आउटपुट एक-दूसरे को अधिलेखित नहीं करेगा।

लिनक्स के हाल के संस्करणों पर, आप वर्तमान स्थिति की जांच कर सकते हैं और क्या फ़ाइल डिस्क्रिप्टर अपेंड मोड में खुला है /proc/<pid>/fdinfo/<fd>:

$ cat /proc/self/fdinfo/4
pos:        2
flags:      0102001

या साथ:

$ lsof +f G -p "$$" -ad 4
COMMAND  PID USER   FD   TYPE  FILE-FLAG DEVICE SIZE/OFF     NODE NAME
zsh     4870 root    4w   REG 0x8401;0x0 252,18        2 59431479 /home/chazelas/log
~# lsof +f g -p "$$" -ad 4
COMMAND  PID USER   FD   TYPE FILE-FLAG DEVICE SIZE/OFF     NODE NAME
zsh     4870 root    4w   REG   W,AP,LG 252,18        2 59431479 /home/chazelas/log

वे झंडे O ..._ झंडे openसिस्टम कॉल के लिए दिए गए हैं।

$ gcc -E - <<< $'#include <fcntl.h>\nO_APPEND O_WRONLY' | tail -n1
02000 01

( O_APPEND0x400 या अष्टकोणीय 02000)

खोल के तो >>साथ फ़ाइल को खोलता है O_WRONLY|O_APPEND(और 0,100,000 यहाँ O_LARGEFILE जो इस प्रश्न के लिए प्रासंगिक नहीं है), जबकि >है O_WRONLYकेवल (और <>है O_RDWRकेवल)।

यदि आप एक:

sudo lsof -nP +f g | grep ,AP

फाइलों को खोजने के लिए O_APPEND, आपको अपने सिस्टम पर लिखने के लिए वर्तमान में खुली हुई अधिकांश लॉग फाइलें मिलेंगी।


आप :(कोलन) का उपयोग क्यों करते हैं : > ?
मावेरेसेक

1
@Mvorisek, यह कमांड के आउटपुट को पुनर्निर्देशित करता है जो कोई आउटपुट नहीं पैदा करता है ::। एक आदेश के बिना, व्यवहार गोले के बीच भिन्न होता है।
स्टीफन चेज़लस

1

अगर मैं सही ढंग से समझ रहा हूँ, teeएक उचित दृष्टिकोण की तरह लगता है:

$ ./myapp-that-echoes-the-date-every-second | tee log > /dev/null &
[1] 20519
$ head log
Thu Apr  3 11:29:34 EDT 2014
Thu Apr  3 11:29:35 EDT 2014
Thu Apr  3 11:29:36 EDT 2014
$ > log
$ head log
Thu Apr  3 11:29:40 EDT 2014
Thu Apr  3 11:29:41 EDT 2014
Thu Apr  3 11:29:42 EDT 2014

1

तेज समाधान के रूप में आप रोटेशन के साथ लॉग का उपयोग कर सकते हैं (उदाहरण के लिए दैनिक रोटेशन):

date=`date +%Y%m%d`
LOGFILE=/home/log$date.log

और इसके लिए लॉग इन रीडायरेक्ट करें ./my_app >> log$date.log


मैं माँग पर घूमने में सक्षम होना चाहता हूँ। यह वास्तव में एक लॉग है जो एक स्वचालित परीक्षण के दौरान उत्पन्न होता है और मैं परीक्षण चलाने से पहले इसे साफ़ करना चाहूंगा।
बंगनाब

0

यह एक ऐसी समस्या है जो लंबे समय से syslog (सभी संस्करणों में) के साथ हल हो गई है, लेकिन दो उपकरण हैं जो न्यूनतम प्रयास के साथ आपकी विशेष समस्या को हल करेंगे।

पहला, अधिक पोर्टेबल लेकिन कम बहुमुखी समाधान लकड़हारा है (किसी भी प्रशासक टूलबॉक्स के लिए होना चाहिए)। यह एक सरल उपयोगिता है जो मानक इनपुट की प्रतिलिपि syslog में देता है। (हिरन को पास करना, और फ़ाइल को घुमाने से लॉग्रोटेट और सिसलॉग की समस्या)

दूसरा, अधिक सुरुचिपूर्ण लेकिन कम पोर्टेबल समाधान syslog- एनजी है जो मानक syslog सॉकेट्स से लॉग संदेशों को स्वीकार करने के अलावा उन कार्यक्रमों को निष्पादित कर सकता है जो आउटपुट लकड़हारा के माध्यम से फ़िल्टर किया जाता है। (मैंने अभी तक इस सुविधा का उपयोग नहीं किया है, लेकिन यह जो आप करना चाहते हैं उसके लिए एकदम सही है।)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.