helper class to quickly write a loop over all standard input files typical use is import fileinput for line in fileinput inputencodingutf8 processline this iterates over the lines of all files listed in sys argv1 defaulting to sys stdin if the list is empty if a filename is it is also replaced by sys stdin and the optional arguments mode and openhook are ignored to specify an alternative list of filenames pass it as the argument to input a single file name is also allowed functions filename lineno return the filename and cumulative line number of the line that has just been read filelineno returns its line number in the current file isfirstline returns true iff the line just read is the first line of its file isstdin returns true iff the line was read from sys stdin function nextfile closes the current file so that the next iteration will read the first line from the next file if any lines not read from the file will not count towards the cumulative line count the filename is not changed until after the first line of the next file has been read function close closes the sequence before any lines have been read filename returns none and both line numbers are zero nextfile has no effect after all lines have been read filename and the line number functions return the values pertaining to the last line read nextfile has no effect all files are opened in text mode by default you can override this by setting the mode parameter to input or fileinput init if an io error occurs during opening or reading a file the oserror exception is raised if sys stdin is used more than once the second and further use will return no lines except perhaps for interactive use or if it has been explicitly reset e g using sys stdin seek0 empty files are opened and immediately closed the only time their presence in the list of filenames is noticeable at all is when the last file opened is empty it is possible that the last line of a file doesn t end in a newline character otherwise lines are returned including the trailing newline class fileinput is the implementation its methods filename lineno fileline isfirstline isstdin nextfile and close correspond to the functions in the module in addition it has a readline method which returns the next input line and a getitem method which implements the sequence behavior the sequence must be accessed in strictly sequential order sequence access and readline cannot be mixed optional inplace filtering if the keyword argument inplace1 is passed to input or to the fileinput constructor the file is moved to a backup file and standard output is directed to the input file this makes it possible to write a filter that rewrites its input file in place if the keyword argument backup some extension is also given it specifies the extension for the backup file and the backup file remains around by default the extension is bak and it is deleted when the output file is closed inplace filtering is disabled when standard input is read xxx the current implementation does not work for msdos 83 filesystems return an instance of the fileinput class which can be iterated the parameters are passed to the constructor of the fileinput class the returned instance in addition to being an iterator keeps global state for the functions of this module close the sequence global state state state state none if state state close def nextfile if not state raise runtimeerrorno active input return state nextfile def filename if not state raise runtimeerrorno active input return state filename def lineno if not state raise runtimeerrorno active input return state lineno def filelineno if not state raise runtimeerrorno active input return state filelineno def fileno if not state raise runtimeerrorno active input return state fileno def isfirstline if not state raise runtimeerrorno active input return state isfirstline def isstdin if not state raise runtimeerrorno active input return state isstdin class fileinput def initself filesnone inplacefalse backup moder openhooknone encodingnone errorsnone if isinstancefiles str files files elif isinstancefiles os pathlike files os fspathfiles else if files is none files sys argv1 if not files files else files tuplefiles self files files self inplace inplace self backup backup self savestdout none self output none self filename none self startlineno 0 self filelineno 0 self file none self isstdin false self backupfilename none self encoding encoding self errors errors we can not use io textencoding here because old openhook doesn t take encoding parameter if sys flags warndefaultencoding and b not in mode and encoding is none and openhook is none import warnings warnings warn encoding argument not specified encodingwarning 2 restrict mode argument to reading modes if mode not in r rb raise valueerrorfileinput opening mode must be r or rb self mode mode self writemode mode replace r w if openhook if inplace raise valueerrorfileinput cannot use an opening hook in inplace mode if not callableopenhook raise valueerrorfileinput openhook must be callable self openhook openhook def delself self close def closeself try self nextfile finally self files def enterself return self def exitself type value traceback self close def iterself return self def nextself while true line self readline if line self filelineno 1 return line if not self file raise stopiteration self nextfile repeat with next file def nextfileself savestdout self savestdout self savestdout none if savestdout sys stdout savestdout output self output self output none try if output output close finally file self file self file none try del self readline restore fileinput readline except attributeerror pass try if file and not self isstdin file close finally backupfilename self backupfilename self backupfilename none if backupfilename and not self backup try os unlinkbackupfilename except oserror pass self isstdin false def readlineself while true line self readline if line self filelineno 1 return line if not self file return line self nextfile repeat with next file def readlineself if not self files if b in self mode return b else return self filename self files0 self files self files1 self startlineno self lineno self filelineno 0 self file none self isstdin false self backupfilename 0 encodingwarning is emitted in init already if b not in self mode encoding self encoding or locale else encoding none if self filename self filename stdin if b in self mode self file getattrsys stdin buffer sys stdin else self file sys stdin self isstdin true else if self inplace self backupfilename os fspathself filename self backup or bak try os unlinkself backupfilename except oserror pass the next few lines may raise oserror os renameself filename self backupfilename self file openself backupfilename self mode encodingencoding errorsself errors try perm os fstatself file fileno stmode except oserror self output openself filename self writemode encodingencoding errorsself errors else mode os ocreat os owronly os otrunc if hasattros obinary mode os obinary fd os openself filename mode perm self output os fdopenfd self writemode encodingencoding errorsself errors try os chmodself filename perm except oserror pass self savestdout sys stdout sys stdout self output else this may raise oserror if self openhook custom hooks made previous to python 3 10 didn t have encoding argument if self encoding is none self file self openhookself filename self mode else self file self openhook self filename self mode encodingself encoding errorsself errors else self file openself filename self mode encodingencoding errorsself errors self readline self file readline hide fileinput readline return self readline def filenameself return self filename def linenoself return self startlineno self filelineno def filelinenoself return self filelineno def filenoself if self file try return self file fileno except valueerror return 1 else return 1 def isfirstlineself return self filelineno 1 def isstdinself return self isstdin classgetitem classmethodgenericalias def hookcompressedfilename mode encodingnone errorsnone if encoding is none and b not in mode encodingwarning is emitted in fileinput already encoding locale ext os path splitextfilename1 if ext gz import gzip stream gzip openfilename mode elif ext bz2 import bz2 stream bz2 bz2filefilename mode else return openfilename mode encodingencoding errorserrors gzip and bz2 are binary mode by default if b not in mode stream io textiowrapperstream encodingencoding errorserrors return stream def hookencodedencoding errorsnone def openhookfilename mode return openfilename mode encodingencoding errorserrors return openhook def test import getopt inplace false backup false opts args getopt getoptsys argv1 ib for o a in opts if o i inplace true if o b backup a for line in inputargs inplaceinplace backupbackup if line1 n line line 1 if line1 r line line 1 printd sds s lineno filename filelineno isfirstline and or line printd sd lineno filename filelineno if name main test return an instance of the fileinput class which can be iterated the parameters are passed to the constructor of the fileinput class the returned instance in addition to being an iterator keeps global state for the functions of this module close the sequence close the current file so that the next iteration will read the first line from the next file if any lines not read from the file will not count towards the cumulative line count the filename is not changed until after the first line of the next file has been read before the first line has been read this function has no effect it cannot be used to skip the first file after the last line of the last file has been read this function has no effect return the name of the file currently being read before the first line has been read returns none return the cumulative line number of the line that has just been read before the first line has been read returns 0 after the last line of the last file has been read returns the line number of that line return the line number in the current file before the first line has been read returns 0 after the last line of the last file has been read returns the line number of that line within the file return the file number of the current file when no file is currently opened returns 1 returns true the line just read is the first line of its file otherwise returns false returns true if the last line was read from sys stdin otherwise returns false fileinput files inplace backup mode none openhook none class fileinput is the implementation of the module its methods filename lineno fileline isfirstline isstdin fileno nextfile and close correspond to the functions of the same name in the module in addition it has a readline method which returns the next input line and a __getitem__ method which implements the sequence behavior the sequence must be accessed in strictly sequential order random access and readline cannot be mixed we can not use io text_encoding here because old openhook doesn t take encoding parameter restrict mode argument to reading modes repeat with next file restore fileinput _readline repeat with next file encodingwarning is emitted in __init__ already the next few lines may raise oserror this may raise oserror custom hooks made previous to python 3 10 didn t have encoding argument hide fileinput _readline encodingwarning is emitted in fileinput already gzip and bz2 are binary mode by default
import io
import sys, os
from types import GenericAlias

__all__ = ["input", "close", "nextfile", "filename", "lineno", "filelineno",
           "fileno", "isfirstline", "isstdin", "FileInput", "hook_compressed",
           "hook_encoded"]

_state = None

def input(files=None, inplace=False, backup="", *, mode="r", openhook=None,
          encoding=None, errors=None):
    
    global _state
    if _state and _state._file:
        raise RuntimeError("input() already active")
    _state = FileInput(files, inplace, backup, mode=mode, openhook=openhook,
                       encoding=encoding, errors=errors)
    return _state

def close():
    
    global _state
    state = _state
    _state = None
    if state:
        state.close()

def nextfile():
    
    if not _state:
        raise RuntimeError("no active input()")
    return _state.nextfile()

def filename():
    
    if not _state:
        raise RuntimeError("no active input()")
    return _state.filename()

def lineno():
    
    if not _state:
        raise RuntimeError("no active input()")
    return _state.lineno()

def filelineno():
    
    if not _state:
        raise RuntimeError("no active input()")
    return _state.filelineno()

def fileno():
    
    if not _state:
        raise RuntimeError("no active input()")
    return _state.fileno()

def isfirstline():
    
    if not _state:
        raise RuntimeError("no active input()")
    return _state.isfirstline()

def isstdin():
    
    if not _state:
        raise RuntimeError("no active input()")
    return _state.isstdin()

class FileInput:
    

    def __init__(self, files=None, inplace=False, backup="", *,
                 mode="r", openhook=None, encoding=None, errors=None):
        if isinstance(files, str):
            files = (files,)
        elif isinstance(files, os.PathLike):
            files = (os.fspath(files), )
        else:
            if files is None:
                files = sys.argv[1:]
            if not files:
                files = ('-',)
            else:
                files = tuple(files)
        self._files = files
        self._inplace = inplace
        self._backup = backup
        self._savestdout = None
        self._output = None
        self._filename = None
        self._startlineno = 0
        self._filelineno = 0
        self._file = None
        self._isstdin = False
        self._backupfilename = None
        self._encoding = encoding
        self._errors = errors

        
        
        if (sys.flags.warn_default_encoding and
                "b" not in mode and encoding is None and openhook is None):
            import warnings
            warnings.warn("'encoding' argument not specified.",
                          EncodingWarning, 2)

        
        if mode not in ('r', 'rb'):
            raise ValueError("FileInput opening mode must be 'r' or 'rb'")
        self._mode = mode
        self._write_mode = mode.replace('r', 'w')
        if openhook:
            if inplace:
                raise ValueError("FileInput cannot use an opening hook in inplace mode")
            if not callable(openhook):
                raise ValueError("FileInput openhook must be callable")
        self._openhook = openhook

    def __del__(self):
        self.close()

    def close(self):
        try:
            self.nextfile()
        finally:
            self._files = ()

    def __enter__(self):
        return self

    def __exit__(self, type, value, traceback):
        self.close()

    def __iter__(self):
        return self

    def __next__(self):
        while True:
            line = self._readline()
            if line:
                self._filelineno += 1
                return line
            if not self._file:
                raise StopIteration
            self.nextfile()
            

    def nextfile(self):
        savestdout = self._savestdout
        self._savestdout = None
        if savestdout:
            sys.stdout = savestdout

        output = self._output
        self._output = None
        try:
            if output:
                output.close()
        finally:
            file = self._file
            self._file = None
            try:
                del self._readline  
            except AttributeError:
                pass
            try:
                if file and not self._isstdin:
                    file.close()
            finally:
                backupfilename = self._backupfilename
                self._backupfilename = None
                if backupfilename and not self._backup:
                    try: os.unlink(backupfilename)
                    except OSError: pass

                self._isstdin = False

    def readline(self):
        while True:
            line = self._readline()
            if line:
                self._filelineno += 1
                return line
            if not self._file:
                return line
            self.nextfile()
            

    def _readline(self):
        if not self._files:
            if 'b' in self._mode:
                return b''
            else:
                return ''
        self._filename = self._files[0]
        self._files = self._files[1:]
        self._startlineno = self.lineno()
        self._filelineno = 0
        self._file = None
        self._isstdin = False
        self._backupfilename = 0

        
        if "b" not in self._mode:
            encoding = self._encoding or "locale"
        else:
            encoding = None

        if self._filename == '-':
            self._filename = '<stdin>'
            if 'b' in self._mode:
                self._file = getattr(sys.stdin, 'buffer', sys.stdin)
            else:
                self._file = sys.stdin
            self._isstdin = True
        else:
            if self._inplace:
                self._backupfilename = (
                    os.fspath(self._filename) + (self._backup or ".bak"))
                try:
                    os.unlink(self._backupfilename)
                except OSError:
                    pass
                
                os.rename(self._filename, self._backupfilename)
                self._file = open(self._backupfilename, self._mode,
                                  encoding=encoding, errors=self._errors)
                try:
                    perm = os.fstat(self._file.fileno()).st_mode
                except OSError:
                    self._output = open(self._filename, self._write_mode,
                                        encoding=encoding, errors=self._errors)
                else:
                    mode = os.O_CREAT | os.O_WRONLY | os.O_TRUNC
                    if hasattr(os, 'O_BINARY'):
                        mode |= os.O_BINARY

                    fd = os.open(self._filename, mode, perm)
                    self._output = os.fdopen(fd, self._write_mode,
                                             encoding=encoding, errors=self._errors)
                    try:
                        os.chmod(self._filename, perm)
                    except OSError:
                        pass
                self._savestdout = sys.stdout
                sys.stdout = self._output
            else:
                
                if self._openhook:
                    
                    
                    if self._encoding is None:
                        self._file = self._openhook(self._filename, self._mode)
                    else:
                        self._file = self._openhook(
                            self._filename, self._mode, encoding=self._encoding, errors=self._errors)
                else:
                    self._file = open(self._filename, self._mode, encoding=encoding, errors=self._errors)
        self._readline = self._file.readline  
        return self._readline()

    def filename(self):
        return self._filename

    def lineno(self):
        return self._startlineno + self._filelineno

    def filelineno(self):
        return self._filelineno

    def fileno(self):
        if self._file:
            try:
                return self._file.fileno()
            except ValueError:
                return -1
        else:
            return -1

    def isfirstline(self):
        return self._filelineno == 1

    def isstdin(self):
        return self._isstdin

    __class_getitem__ = classmethod(GenericAlias)


def hook_compressed(filename, mode, *, encoding=None, errors=None):
    if encoding is None and "b" not in mode:  
        encoding = "locale"
    ext = os.path.splitext(filename)[1]
    if ext == '.gz':
        import gzip
        stream = gzip.open(filename, mode)
    elif ext == '.bz2':
        import bz2
        stream = bz2.BZ2File(filename, mode)
    else:
        return open(filename, mode, encoding=encoding, errors=errors)

    
    if "b" not in mode:
        stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors)
    return stream


def hook_encoded(encoding, errors=None):
    def openhook(filename, mode):
        return open(filename, mode, encoding=encoding, errors=errors)
    return openhook


def _test():
    import getopt
    inplace = False
    backup = False
    opts, args = getopt.getopt(sys.argv[1:], "ib:")
    for o, a in opts:
        if o == '-i': inplace = True
        if o == '-b': backup = a
    for line in input(args, inplace=inplace, backup=backup):
        if line[-1:] == '\n': line = line[:-1]
        if line[-1:] == '\r': line = line[:-1]
        print("%d: %s[%d]%s %s" % (lineno(), filename(), filelineno(),
                                   isfirstline() and "*" or "", line))
    print("%d: %s[%d]" % (lineno(), filename(), filelineno()))

if __name__ == '__main__':
    _test()