पायथन में एक फ़ाइल पर रीडायरेक्ट करें?


313

पायथन में एक मनमानी फ़ाइल के लिए मैं कैसे पुनर्निर्देशित करता हूँ?

जब एक लंबे समय से चल रहे पायथन स्क्रिप्ट (उदाहरण के लिए, वेब एप्लिकेशन) को ssh सत्र के भीतर से शुरू किया जाता है और बैकगाउंड किया जाता है, और ssh सत्र बंद हो जाता है, तो अनुप्रयोग IOError को बढ़ाएगा और उस क्षण को विफल कर देगा, जब वह लिखने के लिए लिखने का प्रयास करता है। IOError के कारण विफलता को रोकने के लिए मुझे किसी फ़ाइल के लिए एप्लिकेशन और मॉड्यूल आउटपुट बनाने के लिए एक रास्ता खोजने की आवश्यकता थी। वर्तमान में, मैं एक फाइल में आउटपुट को रीडायरेक्ट करने के लिए nohup को नियोजित करता हूं, और वह काम पूरा हो जाता है, लेकिन मुझे आश्चर्य हो रहा था कि क्या कोई ऐसा तरीका था, जिसे नोक का उपयोग किए बिना, जिज्ञासा से बाहर किया जाए।

मैंने पहले ही प्रयास किया है sys.stdout = open('somefile', 'w'), लेकिन यह कुछ बाहरी मॉड्यूल को अभी भी टर्मिनल पर आउटपुट देने से रोकता नहीं है (या शायद sys.stdout = ...लाइन में आग नहीं लगी थी)। मुझे पता है कि इसे उन सरल लिपियों से काम करना चाहिए जिन पर मैंने परीक्षण किया है, लेकिन मेरे पास अभी तक वेब एप्लिकेशन पर परीक्षण करने के लिए समय नहीं है।


8
यह वास्तव में एक अजगर बात नहीं है, यह एक शेल फ़ंक्शन है। बस अपनी स्क्रिप्ट जैसेscript.p > file
फल्मरी

मैं वर्तमान में nohup का उपयोग करके समस्या को हल करता हूं, लेकिन मुझे लगा कि कुछ अधिक चतुर हो सकता है ...

1
@foxbunny: nohup? बस क्यों someprocess | python script.py? क्यों शामिल nohup?
एस.लॉट

3
Stdlib से मॉड्यूल printको लागू करने के लिए कथनों को फिर loggingसे लिखें। तो फिर तुम उत्पादन हर जगह अनुप्रेषित कर सकते हैं, आप कितना उत्पादन ज्यादातर मामलों उत्पादन कोड में आदि चाहते हैं नहीं करना चाहिए पर नियंत्रण है printलेकिन log
एरिकबवर्क

2
शायद इस समस्या का एक बेहतर समाधान स्क्रीन कमांड है, जो आपके बैश सत्र को बचाएगा और आपको इसे अलग-अलग रन से एक्सेस करने की अनुमति देगा।
रायन एमोस

जवाबों:


402

यदि आप पायथन स्क्रिप्ट के भीतर पुनर्निर्देशन करना चाहते हैं, तो sys.stdoutफ़ाइल ऑब्जेक्ट पर सेटिंग ट्रिक करती है:

import sys
sys.stdout = open('file', 'w')
print('test')

निष्पादन के समय शेल पुनर्निर्देशन का उपयोग करने के लिए एक अधिक सामान्य विधि है (विंडोज और लिनक्स पर समान):

$ python foo.py > file


7
यह साथ काम नहीं करता है from sys import stdout, हो सकता है क्योंकि यह एक स्थानीय प्रतिलिपि बनाता है। इसके अलावा, आप इसका उपयोग कर सकते हैं with, जैसे with open('file', 'w') as sys.stdout: functionThatPrints()। अब आप functionThatPrints()सामान्य printकथनों का उपयोग करके लागू कर सकते हैं ।
mgold

41
यह एक स्थानीय प्रतिलिपि रखने के लिए, सबसे अच्छा है stdout = sys.stdout, तो आप इसे वापस रख सकते हैं जब आप पूरा कर लें, sys.stdout = stdout। इस तरह अगर आपको किसी ऐसे फ़ंक्शन से बुलाया जा रहा है जो printआपको उपयोग करता है तो उन्हें खराब न करें।
15 से 12

4
@ जान: buffering=0बफरिंग अक्षम करता है (यह प्रदर्शन (10-100 बार) को नकारात्मक रूप से प्रभावित कर सकता है)। buffering=1लाइन बफ़रिंग सक्षम करता है ताकि आप tail -fलाइन-ओरिएंटेड आउटपुट के लिए उपयोग कर सकें ।
JFS

41
@mgold या आप sys.stdout = sys.__stdout__इसे वापस पाने के लिए उपयोग कर सकते हैं ।
क्लेम्मटॉय

175

नहीं है contextlib.redirect_stdout()समारोह अजगर 3.4 में:

from contextlib import redirect_stdout

with open('help.txt', 'w') as f:
    with redirect_stdout(f):
        print('it now prints to `help.text`')

यह उसके जैसा है:

import sys
from contextlib import contextmanager

@contextmanager
def redirect_stdout(new_target):
    old_target, sys.stdout = sys.stdout, new_target # replace sys.stdout
    try:
        yield new_target # run some code with the replaced stdout
    finally:
        sys.stdout = old_target # restore to the previous value

जिसका उपयोग पहले पायथन संस्करणों पर किया जा सकता है। बाद वाला संस्करण पुन: प्रयोज्य नहीं है । इसे यदि चाहें तो बनाया जा सकता है।

यह फ़ाइल डिस्क्रिप्टर के स्तर पर stdout को पुनर्निर्देशित नहीं करता है जैसे:

import os
from contextlib import redirect_stdout

stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, redirect_stdout(f):
    print('redirected to a file')
    os.write(stdout_fd, b'not redirected')
    os.system('echo this also is not redirected')

b'not redirected'और फ़ाइल पर 'echo this also is not redirected'पुनर्निर्देशित नहीं किया जाता है output.txt

फ़ाइल डिस्क्रिप्टर स्तर पर पुनर्निर्देशित करने के लिए, os.dup2()का उपयोग किया जा सकता है:

import os
import sys
from contextlib import contextmanager

def fileno(file_or_fd):
    fd = getattr(file_or_fd, 'fileno', lambda: file_or_fd)()
    if not isinstance(fd, int):
        raise ValueError("Expected a file (`.fileno()`) or a file descriptor")
    return fd

@contextmanager
def stdout_redirected(to=os.devnull, stdout=None):
    if stdout is None:
       stdout = sys.stdout

    stdout_fd = fileno(stdout)
    # copy stdout_fd before it is overwritten
    #NOTE: `copied` is inheritable on Windows when duplicating a standard stream
    with os.fdopen(os.dup(stdout_fd), 'wb') as copied: 
        stdout.flush()  # flush library buffers that dup2 knows nothing about
        try:
            os.dup2(fileno(to), stdout_fd)  # $ exec >&to
        except ValueError:  # filename
            with open(to, 'wb') as to_file:
                os.dup2(to_file.fileno(), stdout_fd)  # $ exec > to
        try:
            yield stdout # allow code to be run with the redirected stdout
        finally:
            # restore stdout to its previous value
            #NOTE: dup2 makes stdout_fd inheritable unconditionally
            stdout.flush()
            os.dup2(copied.fileno(), stdout_fd)  # $ exec >&copied

यदि stdout_redirected()इसके बजाय इसका उपयोग किया जाता है तो वही उदाहरण अब काम करता है redirect_stdout():

import os
import sys

stdout_fd = sys.stdout.fileno()
with open('output.txt', 'w') as f, stdout_redirected(f):
    print('redirected to a file')
    os.write(stdout_fd, b'it is redirected now\n')
    os.system('echo this is also redirected')
print('this is goes back to stdout')

उत्पादन है कि पहले stdout पर मुद्रित किया गया था अब के लिए चला जाता output.txtजब तक कि stdout_redirected()संदर्भ प्रबंधक सक्रिय है।

नोट: stdout.flush()पायथन बफ़र्स को पायथन 3 पर फ्लश नहीं करता है जहाँ I / O को सीधे read()/ write()सिस्टम कॉल पर लागू किया जाता है । सभी खुली सी stdio आउटपुट स्ट्रीम को फ्लश करने के लिए, आप libc.fflush(None)स्पष्ट रूप से कॉल कर सकते हैं यदि कुछ C एक्सटेंशन stdio- आधारित I / S का उपयोग करता है:

try:
    import ctypes
    from ctypes.util import find_library
except ImportError:
    libc = None
else:
    try:
        libc = ctypes.cdll.msvcrt # Windows
    except OSError:
        libc = ctypes.cdll.LoadLibrary(find_library('c'))

def flush(stream):
    try:
        libc.fflush(None)
        stream.flush()
    except (AttributeError, ValueError, IOError):
        pass # unsupported

आप stdoutअन्य धाराओं को पुनर्निर्देशित करने के लिए पैरामीटर का उपयोग कर सकते हैं , न कि केवल sys.stdoutउदाहरण के लिए, विलय sys.stderrऔर करने के लिए sys.stdout:

def merged_stderr_stdout():  # $ exec 2>&1
    return stdout_redirected(to=sys.stdout, stdout=sys.stderr)

उदाहरण:

from __future__ import print_function
import sys

with merged_stderr_stdout():
     print('this is printed on stdout')
     print('this is also printed on stdout', file=sys.stderr)

नोट: stdout_redirected()बफ़र किए गए I / O ( sys.stdoutआमतौर पर) और असंबद्ध I / O को मिलाता है (सीधे फाइल डिस्क्रिप्टर पर संचालन)। खबरदार, मुद्दों बफरिंग हो सकता है

उत्तर देने के लिए, अपना संपादन: आप python-daemonअपनी स्क्रिप्ट को निष्क्रिय करने के लिए उपयोग कर सकते हैं और loggingमॉड्यूल का उपयोग कर सकते हैं (जैसा कि @ erikb85 ने सुझाव दिया है ) printबयानों के बजाय और अपने लंबे समय से चल रहे पायथन स्क्रिप्ट के लिए केवल पुनर्निर्देशन का उपयोग करें जिसे आप nohupअभी उपयोग कर रहे हैं।


3
stdout_redirectedमददगार है। ध्यान रखें कि यह doctests के अंदर काम नहीं करता है, क्योंकि विशेष SpoofOutहैंडलर doctest को बदलने के लिए उपयोग sys.stdoutकरता है एक filenoविशेषता नहीं है ।
क्रिस जॉनसन

@ क्रिसहॉसन: अगर यह नहीं बढ़ा ValueError("Expected a file (`.fileno()`) or a file descriptor")तो यह एक बग है। क्या आप वाकई इसे बढ़ा नहीं रहे हैं?
jfs

यह उस त्रुटि को बढ़ाता है, जो वह है जो एक सिद्धांत के भीतर उपयोग करने योग्य नहीं है। अपने कार्य को एक सिद्धांत के भीतर उपयोग करने के लिए, यह निर्दिष्ट करना आवश्यक है doctest.sys.__stdout__कि हम सामान्य रूप से कहां उपयोग करेंगे sys.stdout। यह आपके फ़ंक्शन के साथ कोई समस्या नहीं है, बस doctest के लिए एक आवास की आवश्यकता है क्योंकि यह एक ऐसी वस्तु के साथ stdout की जगह लेता है जिसमें सभी विशेषताएँ नहीं होती हैं जो एक सच्ची फ़ाइल होती है।
क्रिस जॉनसन

stdout_redirected()है stdoutपैरामीटर, आप के लिए यह सेट कर सकते हैं sys.__stdout__यदि आप मूल अजगर stdout (कि एक वैध होना चाहिए रीडायरेक्ट करना चाहते हैं .fileno()ज्यादातर मामलों में)। sys.stdoutअगर वे अलग हैं तो यह करंट के लिए कुछ नहीं करता है । उपयोग न करें doctest.sys; यह दुर्घटना से उपलब्ध है।
JFS

यह वास्तव में अच्छी तरह से काम करता है, अर्थात एक with stdout_redirected(to=fd): with merged_stderr_stdout(): print('...'); print('...', file=sys.stderr)
डीडीटी पर

90

आप इसे बहुत बेहतर तरीके से आज़मा सकते हैं

import sys

class Logger(object):
    def __init__(self, filename="Default.log"):
        self.terminal = sys.stdout
        self.log = open(filename, "a")

    def write(self, message):
        self.terminal.write(message)
        self.log.write(message)

sys.stdout = Logger("yourlogfilename.txt")
print "Hello world !" # this is should be saved in yourlogfilename.txt

loggerया पाइपिंग के लिए कोई सुझाव syslog?
dsummersl

यदि आप किसी फ़ाइल को संपादित करना चाहते हैं तो यह बहुत उपयोगी नहीं है। वैसे भी अच्छी चाल के लिए +1
aIKid

10
इसमें कोड के परिणाम होंगे जो sys.stdout मानती है एक पूर्ण विकसित फ़ाइल ऑब्जेक्ट है जैसे कि fileno () (जिसमें पायथन मानक पुस्तकालय में कोड शामिल है)। मैं एक __getattr __ (स्वयं, attr) विधि जोड़ूंगा जो स्वफ़ोटोमिनल को देखने की विशेषता को ख़राब करता है। def __getattr__(self, attr): return getattr(self.terminal, attr)
मटरू

4
आपको def flush(self):क्लास के साथ-साथ मेथड भी जोड़ना होगा Logger
लोरेटोपारसी

1
@loretoparisi लेकिन वास्तव में आपके द्वारा बनाई गई विधि में क्या जाता है?
elkshadow5

28

अन्य उत्तरों में उस मामले को शामिल नहीं किया गया है जहाँ आप अपनी नई प्रक्रिया को साझा करने के लिए प्रक्रियाएँ चाहते हैं।

ऐसा करने के लिए:

from os import open, close, dup, O_WRONLY

old = dup(1)
close(1)
open("file", O_WRONLY) # should open on 1

..... do stuff and then restore

close(1)
dup(old) # should dup to 1
close(old) # get rid of left overs

3
किसी को 'w' विशेषता के साथ, os.O_WRONLY | os.O_CREATE को बदलने की आवश्यकता है ... "os" कमांड में तार नहीं भेज सकते हैं!
चामर

3
पुनर्निर्देशित फ़ाइल को आउटपुट मिलता है यह सुनिश्चित करने के लिए कथन से sys.stdout.flush()पहले डालें । इसके अलावा, आप के स्थान पर एक फ़ाइल का उपयोग कर सकते हैं । और सावधान रहें कि आपके पास अन्य थ्रेड्स नहीं चल रहे हैं जो ओएस की पहली फाइल हैंडल को चुरा सकते हैं लेकिन हैंडल का उपयोग करने के लिए खोला जाने से पहले । close(1)'file'tempfile.mkstemp()'file'os.close(1)'file'
एलेक्स रॉबिन्सन 3

2
इसके os.O_Wronly | os.O_CREAT ... वहाँ कोई E नहीं है।
जेफ शेफील्ड


@ Ch'marr यह O_CREAT है, O_CREATE नहीं।
quant_dev

28

पीईपी 343 से उद्धृत - "स्टेटमेंट" (अतिरिक्त आयात विवरण) के साथ:

अस्थायी रूप से पुनर्निर्देशित करें:

import sys
from contextlib import contextmanager
@contextmanager
def stdout_redirected(new_stdout):
    save_stdout = sys.stdout
    sys.stdout = new_stdout
    try:
        yield None
    finally:
        sys.stdout = save_stdout

निम्नानुसार उपयोग किया जाता है:

with open(filename, "w") as f:
    with stdout_redirected(f):
        print "Hello world"

यह निश्चित रूप से थ्रेड-सेफ नहीं है, लेकिन न तो यह एक ही नृत्य मैन्युअल रूप से कर रहा है। एकल-थ्रेडेड प्रोग्राम (उदाहरण के लिए स्क्रिप्ट में) यह चीजों को करने का एक लोकप्रिय तरीका है।


1
+1। नोट: यह उपप्रक्रमों जैसे, के लिए काम नहीं करता है os.system('echo not redirected')मेरा जवाब दिखाता है कि इस तरह के आउटपुट को रीडायरेक्ट करना है
jfs

अजगर 3.4 से शुरू होती है redirect_stdoutमेंcontextlib
वाल्टर Tross


3

यहाँ युदा प्रवीरा उत्तर की भिन्नता है :

  • लागू करें flush()और सभी फ़ाइल विशेषताएँ
  • इसे एक संदर्भकर्ता के रूप में लिखें
  • कब्जा stderrभी

import contextlib, sys

@contextlib.contextmanager
def log_print(file):
    # capture all outputs to a log file while still printing it
    class Logger:
        def __init__(self, file):
            self.terminal = sys.stdout
            self.log = file

        def write(self, message):
            self.terminal.write(message)
            self.log.write(message)

        def __getattr__(self, attr):
            return getattr(self.terminal, attr)

    logger = Logger(file)

    _stdout = sys.stdout
    _stderr = sys.stderr
    sys.stdout = logger
    sys.stderr = logger
    try:
        yield logger.log
    finally:
        sys.stdout = _stdout
        sys.stderr = _stderr


with log_print(open('mylogfile.log', 'w')):
    print('hello world')
    print('hello world on stderr', file=sys.stderr)

# you can capture the output to a string with:
# with log_print(io.StringIO()) as log:
#   ....
#   print('[captured output]', log.getvalue())

2

इस उत्तर के आधार पर: https://stackoverflow.com/a/5916874/1060344 , यहाँ एक और तरीका है जिससे मुझे लगा कि मैं अपने एक प्रोजेक्ट में इस्तेमाल करता हूँ। जो कुछ भी आप प्रतिस्थापित करते हैं sys.stderrया उसके sys.stdoutसाथ करते हैं, आपको यह सुनिश्चित करना होगा कि प्रतिस्थापन fileइंटरफ़ेस का अनुपालन करता है, खासकर यदि यह ऐसा कुछ है जो आप कर रहे हैं क्योंकि कुछ अन्य पुस्तकालय में stderr / stdout का उपयोग किया जाता है जो आपके नियंत्रण में नहीं है। वह लाइब्रेरी फ़ाइल ऑब्जेक्ट के अन्य तरीकों का उपयोग कर सकती है।

इस तरह से देखें जहां मैंने अभी भी सब कुछ करने दिया stderr / stdout (या उस मामले के लिए कोई फ़ाइल) और पायथन लॉगिंग सुविधा का उपयोग करके लॉग फ़ाइल को भी संदेश भेजें (लेकिन आप वास्तव में इसके साथ कुछ भी कर सकते हैं):

class FileToLogInterface(file):
    '''
    Interface to make sure that everytime anything is written to stderr, it is
    also forwarded to a file.
    '''

    def __init__(self, *args, **kwargs):
        if 'cfg' not in kwargs:
            raise TypeError('argument cfg is required.')
        else:
            if not isinstance(kwargs['cfg'], config.Config):
                raise TypeError(
                    'argument cfg should be a valid '
                    'PostSegmentation configuration object i.e. '
                    'postsegmentation.config.Config')
        self._cfg = kwargs['cfg']
        kwargs.pop('cfg')

        self._logger = logging.getlogger('access_log')

        super(FileToLogInterface, self).__init__(*args, **kwargs)

    def write(self, msg):
        super(FileToLogInterface, self).write(msg)
        self._logger.info(msg)

2

आपको या तो tmux या GNU स्क्रीन जैसे टर्मिनल मल्टीप्लेक्सर की आवश्यकता है

मुझे आश्चर्य है कि रयान एमोस की एक छोटी सी टिप्पणी 'मूल प्रश्न के लिए एक प्रस्ताव का जिक्र है, जो प्रस्ताव पर सभी अन्य लोगों के लिए बेहतर है, चाहे वह कितने ही चतुर चालाक हो और कितने भी अपवित्र मिले हों। रयान की टिप्पणी के आगे, tmux GNU स्क्रीन का एक अच्छा विकल्प है।

लेकिन सिद्धांत समान है: यदि आप कभी लॉग-आउट करते समय अपने आप को टर्मिनल की नौकरी छोड़ना चाहते हैं, तो एक सैंडविच के लिए कैफे में जाएं, बाथरूम में जाएं, घर जाएं (आदि) और फिर बाद में, अपने को फिर से कनेक्ट करें टर्मिनल सत्र कहीं से भी या किसी भी कंप्यूटर से जैसे कि आप कभी दूर नहीं होंगे, टर्मिनल मल्टीप्लेक्सर्स का जवाब है। उन्हें टर्मिनल सत्रों के लिए वीएनसी या रिमोट डेस्कटॉप के रूप में सोचें। और कुछ भी एक समाधान है। एक बोनस के रूप में, जब बॉस और / या साथी अंदर आते हैं और आप अनजाने में ctrl-w / cmd-w अपने टर्मिनल विंडो को अपनी डोडी सामग्री के साथ अपने ब्राउज़र विंडो के बजाय, आप पिछले 18 घंटे-प्रसंस्करण खो नहीं होगा !


4
हालांकि यह संपादन के बाद दिखाई देने वाले प्रश्न के भाग के लिए एक अच्छा उत्तर है; यह शीर्षक में प्रश्न (ज्यादातर लोगों खिताब के लिए गूगल से यहां आते हैं) का उत्तर न मिले
JFS

0

अन्य भाषाओं (जैसे C) में लिखे गए प्रोग्रामों को विशेष रूप से टर्मिनल से अलग करने के लिए (और ज़ोंबी प्रक्रियाओं को रोकने के लिए) डबल जादू करने के लिए विशेष जादू करना पड़ता है। इसलिए, मुझे लगता है कि सबसे अच्छा समाधान उनका अनुकरण करना है।

अपने कार्यक्रम को फिर से निष्पादित करने का एक प्लस यह है, आप कमांड-लाइन पर पुनर्निर्देशन चुन सकते हैं, जैसे /usr/bin/python mycoolscript.py 2>&1 1>/dev/null

अधिक जानकारी के लिए इस पोस्ट को देखें: डेमन बनाते समय दोहरा कांटा प्रदर्शन करने का क्या कारण है?


एह ... यह नहीं कह सकता कि मैं अपनी डबल-फोर्किंग के प्रबंधन की प्रक्रियाओं का प्रशंसक हूं। यदि आप सावधान नहीं हैं तो यह गलत है, और इतना आसान कोड गलत है। अग्रभूमि में चलाने के लिए अपनी प्रक्रिया को लिखने के लिए बेहतर है, और फोर्किंग बॉयलरप्लेट को संभालने के लिए एक सिस्टम बैकग्राउंड टास्क मैनेजर ( systemd, upstart) या अन्य उपयोगिता ( daemon(1)) का उपयोग करें।
लुकट्रेल 5
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.