Gato pitón, aka unix maull -- python campo con python-3.x campo con console campo con unix camp codereview Relacionados El problema

Python cat, aka Unix Meow


8
vote

problema

Español

He usado Linux y Windows intercambiablemente en la última vez, y me acostumbré a la sintaxis más cómoda y rica en opciones de los comandos de Linux, así que decidí replicar algunos de ellos en Python (con alguna modificación), así que Puede usarlos en Windows y comenzaron con el ls y cat55544335 [Después de agregar su director de padre en PATH y asociar py < / Código> Archivos con Python, se pueden usar como los originales].

I construé cat con casi las mismas características que el original (consulte Página de hombre de Linux ), con la opción de la adición de -r REPEAT que repite cada archivo import {Component} from "@angular/core"; import {ClockService} from "./clock.service"; @Component({ selector: 'clock', templateUrl: './clock.component.html', styleUrls: ['./clock.component.css'] }) export class Clock { time: Date; constructor(private clockService: ClockService) { } ngOnInit() { this.clockService.getClock().subscribe(time => this.time = time); } } 0 momento (predeterminado 1) y el import {Component} from "@angular/core"; import {ClockService} from "./clock.service"; @Component({ selector: 'clock', templateUrl: './clock.component.html', styleUrls: ['./clock.component.css'] }) export class Clock { time: Date; constructor(private clockService: ClockService) { } ngOnInit() { this.clockService.getClock().subscribe(time => this.time = time); } } 1 Opción de encabezado que imprime el nombre del archivo antes de cada archivo (como Windows ' import {Component} from "@angular/core"; import {ClockService} from "./clock.service"; @Component({ selector: 'clock', templateUrl: './clock.component.html', styleUrls: ['./clock.component.css'] }) export class Clock { time: Date; constructor(private clockService: ClockService) { } ngOnInit() { this.clockService.getClock().subscribe(time => this.time = time); } } 2 ), y la interpretación de los nombres de archivos que no sean import {Component} from "@angular/core"; import {ClockService} from "./clock.service"; @Component({ selector: 'clock', templateUrl: './clock.component.html', styleUrls: ['./clock.component.css'] }) export class Clock { time: Date; constructor(private clockService: ClockService) { } ngOnInit() { this.clockService.getClock().subscribe(time => this.time = time); } } 3 como patrones de archivo - que se expandirá para cubrir todos los archivos correspondientes, en casos como import {Component} from "@angular/core"; import {ClockService} from "./clock.service"; @Component({ selector: 'clock', templateUrl: './clock.component.html', styleUrls: ['./clock.component.css'] }) export class Clock { time: Date; constructor(private clockService: ClockService) { } ngOnInit() { this.clockService.getClock().subscribe(time => this.time = time); } } 4 . Todo el import {Component} from "@angular/core"; import {ClockService} from "./clock.service"; @Component({ selector: 'clock', templateUrl: './clock.component.html', styleUrls: ['./clock.component.css'] }) export class Clock { time: Date; constructor(private clockService: ClockService) { } ngOnInit() { this.clockService.getClock().subscribe(time => this.time = time); } } 5 funciona lo mismo.

He estado usando Python por un tiempo ahora, y traté de hacer que el código sea eficiente y "Pythonic" como pude. Sin embargo, todavía creo que hay un lot de la habitación para mejoras, y mucho menos algunos problemas como la necesidad de escanear los archivos antes del bucle de impresión en el modo de numeración para asegurar el ajuste de formato de número.


Aquí está mi código:

  import {Component} from "@angular/core"; import {ClockService} from "./clock.service";  @Component({   selector: 'clock',   templateUrl: './clock.component.html',   styleUrls: ['./clock.component.css'] }) export class Clock {    time: Date;    constructor(private clockService: ClockService) {   }    ngOnInit() {     this.clockService.getClock().subscribe(time => this.time = time);   }  } 6  

Demo:

  import {Component} from "@angular/core"; import {ClockService} from "./clock.service";  @Component({   selector: 'clock',   templateUrl: './clock.component.html',   styleUrls: ['./clock.component.css'] }) export class Clock {    time: Date;    constructor(private clockService: ClockService) {   }    ngOnInit() {     this.clockService.getClock().subscribe(time => this.time = time);   }  } 7  

  import {Component} from "@angular/core"; import {ClockService} from "./clock.service";  @Component({   selector: 'clock',   templateUrl: './clock.component.html',   styleUrls: ['./clock.component.css'] }) export class Clock {    time: Date;    constructor(private clockService: ClockService) {   }    ngOnInit() {     this.clockService.getClock().subscribe(time => this.time = time);   }  } 8  
Original en ingles

I have used linux and Windows interchangeably over the last time, and I got used to the more comfortable and options-rich syntax of linux' commands, so I decided to replicate some of them in python (with some modification) so I can use them in Windows, and started with the ls and cat commands [after adding their parent dir under PATH and associating py files with python, they can be used like the originals].

I built cat with almost the same features as the original (see linux man page), with the addition of the -r REPEAT option that repeats every file REPEAT times (default 1), and the -f header option that prints the file name before every file (like windows' TYPE), and the interpretation of filenames other than - as file patterns - that will expand to cover all matching files, on cases like cat *s*.*. All of the nbAeEtTv options function the same.

I have been using python for a while now, and tried to make the code as efficient and "pythonic" as I could. However, I still believe there is a lot of room for improvements, let alone some issues as the need to scan the files before the printing loop on numbering mode to assure number format adjustment.


Here is my code:

import argparse, shutil, sys import itertools import os, os.path import string, glob, re   if __name__ == '__main__':      parser = argparse.ArgumentParser(description='concatenate files and print on the standard output')      parser.add_argument('filenames', nargs='+', help='directory to scan')     parser.add_argument('-r', '--repeat', dest='repeat', default=1, type=int, help='repeat output multiple times')     parser.add_argument('-f', '--header', dest='header', default=False, help='precede every file with its name', action='store_true')      parser.add_argument('-n', '--number', dest='number', default=False, help='number all output lines', action='store_true')     parser.add_argument('-b', '--number-nonblank', dest='number_nonblank', default=False, help='number nonempty output lines', action='store_true')      parser.add_argument('-A', '--show-all', dest='show_all', default=False, help='equivalent to -vET', action='store_true')     parser.add_argument('-e', dest='show_ends_and_nonprinting', default=False, help='equivalent to -vE', action='store_true')     parser.add_argument('-E', '--show-ends', dest='show_ends', default=False, help='display $ at end of each line', action='store_true')     parser.add_argument('-t', dest='show_tabs_and_nonprinting', default=False, help='equivalent to -vT', action='store_true')     parser.add_argument('-T', '--show-tabs', dest='show_tabs', default=False, help='display TAB characters as ^I', action='store_true')     parser.add_argument('-v', '--show-nonprinting', dest='show_nonprinting', default=False, help='use ^ and M- notation, except for LFD and TAB', action='store_true')      args = parser.parse_args()       # Special chars option overlapping     args.show_tabs = args.show_tabs or args.show_tabs_and_nonprinting or args.show_all     args.show_ends = args.show_ends or args.show_ends_and_nonprinting or args.show_all     args.show_nonprinting = args.show_nonprinting or args.show_tabs_and_nonprinting or args.show_ends_and_nonprinting or args.show_all      # Special chars definitions     tabs = '^I' if args.show_tabs else '\t'     ends = '$\n' if args.show_ends else '\n'     nonprinting = {k: '^' + v for k, v in zip(range(32), '@'+ string.ascii_uppercase + '[\]^!') if v not in 'IJ'} if args.show_nonprinting else {}       # Set filenames by provided patterns     GET_INPUT = 0     args.filenames = list(itertools.chain(*([GET_INPUT] if filepattern == '-' else glob.glob(filepattern) for filepattern in args.filenames)))       # Numbering settings     if args.number or args.number_nonblank:         line_number = 1         longest_line_number_length = len(str((sum(open(filename, 'rb').read().count(b'\n') if filename != GET_INPUT else 1 for filename in args.filenames) + 1) * args.repeat))      # The printing loop(s)     for filename in args.filenames:          if filename == GET_INPUT:             print(input())             continue          if args.header:             print('{:=^{}}'.format(filename, shutil.get_terminal_size().columns // 2))          for _ in range(args.repeat):             with open(filename, 'rb') as file:                 for line in file:                     nonblank_line = re.match('.*\S.*', line.decode(errors='replace'))                     print(                         '    {:>{}}  '.format(line_number, longest_line_number_length) if args.number or (args.number_nonblank and nonblank_line) \                             else ' ' * (longest_line_number_length + 6) if args.number_nonblank else '', # For -E under -b                         line.decode(errors='backslashreplace').rstrip('\n\r').replace('\t', tabs).translate(nonprinting),                         sep='', end=ends                     )                     if args.number or (args.number_nonblank and nonblank_line):                         line_number += 1 

Demo:

C:\Dev>dump a -t 000000000000 xe2x94x82 31 32 33 34   35                                      xe2x94x82 12345  C:\Dev>dump b -t 000000000000 xe2x94x82 04 09 41 42   43 44 45 46                             xe2x94x82 ..ABCDEF  C:\Dev>echo 789 | cat a - b >> c  C:\Dev>cat c -Anr2     1  12345$     2  789 $     3  ^D^IABCDEF$     4  12345$     5  789 $     6  ^D^IABCDEF$ 

    _                ___       _.--.     \`.|\..----...-'`   `-._.-'_.-'`     /  ' `         ,       __.--'     )/' _/     \   `-_,   /     `-'" `"\_  ,_.-;_.-\_ ',         _.-'_./   {_.'   ; /        {_.-``-'         {_/ 
           

Lista de respuestas

3
 
vote
vote
La mejor respuesta
 

Al menos el código no está en el nivel superior del archivo, pero realmente debe usar funciones para documentar lo que está sucediendo. Los nombres bien elegidos pueden pasar un largo camino para transmitir lo que está haciendo y por qué.

También tengo una tendencia a extraer la maquinaria 9988776665544330 fuera del camino del cálculo real. De esta manera, puedo burlarme de esta parte en mis pruebas:

  >>> class Args: ...   def __getattr__(self, key): ...     return None ...  >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'}   

y llame main(args) (o en su caso cat(args) ) Sin tener que confiar en la línea de comandos. Así que vamos a construir eso:

  def parse_command_line():     parser = argparse.ArgumentParser(             description='concatenate files and print on the standard output')      parser.add_argument('filenames', nargs='+', help='directory to scan')     parser.add_argument('-r', '--repeat', dest='repeat', default=1, type=int,                         help='repeat output multiple times')     parser.add_argument('-f', '--header', dest='header', default=False,                         help='precede every file with its name',                         action='store_true')      parser.add_argument('-n', '--number', dest='number', default=False,                         help='number all output lines', action='store_true')     parser.add_argument('-b', '--number-nonblank', dest='number_nonblank',                         default=False, help='number nonempty output lines',                         action='store_true')      parser.add_argument('-A', '--show-all', dest='show_all', default=False,                         help='equivalent to -vET', action='store_true')     parser.add_argument('-e', dest='show_ends_and_nonprinting', default=False,                         help='equivalent to -vE', action='store_true')     parser.add_argument('-E', '--show-ends', dest='show_ends', default=False,                         help='display $ at end of each line', action='store_true')     parser.add_argument('-t', dest='show_tabs_and_nonprinting', default=False,                         help='equivalent to -vT', action='store_true')     parser.add_argument('-T', '--show-tabs', dest='show_tabs', default=False,                         help='display TAB characters as ^I', action='store_true')     parser.add_argument('-v', '--show-nonprinting', dest='show_nonprinting',                         default=False, action='store_true',                         help='use ^ and M- notation, except for LFD and TAB')      args = parser.parse_args()       # Special chars option overlapping     if args.show_all:         args.show_tabs = True         args.show_ends = True         args.show_nonprinting = True     if args.show_tabs_and_nonprinting:         args.show_tabs = True         args.show_nonprinting = True     if args.show_ends_and_nonprinting:         args.show_ends = True         args.show_nonprinting = True     if args.number_nonblank:         args.number = True  # See later why      return args   

También invirtí un poco la gestión de las opciones superpuestas, ya que siento que se lee mejor.

De esta manera, incluso si coloca el resto del código en una función única 9988776665544335

  if __name__ == '__main__':     args = parse_command_line()     cat(args)   

que debería ser mucho más fácil de comenzar la prueba.


Ahora, según el núcleo de su programa, puedo ver al menos 2 comportamientos indocumentados que difieren de la utilidad 9988777665544337 en Linux. En primer lugar, solo está leyendo una línea de entrada en lugar de todo el stdin . Considere los casos de uso como grep PATTERN unfiltered.txt | cat header.txt - footer.txt que el original >>> class Args: ... def __getattr__(self, key): ... return None ... >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 0 se encarga perfectamente (, es decir, toda la salida de >>> class Args: ... def __getattr__(self, key): ... return None ... >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 1 se imprime entre El contenido de >>> class Args: ... def __getattr__(self, key): ... return None ... >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 2 y >>> class Args: ... def __getattr__(self, key): ... return None ... >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 3 ). Usted no Dado que tampoco entiendo por qué repetiría todos los demás archivos, pero 99887766555443314 , sugiero usar el módulo >>> class Args: ... def __getattr__(self, key): ... return None ... >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 5 tampoco el contenido de >>> class Args: ... def __getattr__(self, key): ... return None ... >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 6 .

Necesitaremos cambiar el contenido de la matriz de entrada, así que vamos a encapsular que en una función que también realice el globo, para limpiar este comando 99887766655443317

. Mientras estoy en ello, >>> class Args: ... def __getattr__(self, key): ... return None ... >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 8 También permite especificar ningún nombre de archivo en la línea de comandos, que se trata de la misma manera que un solo 99887776655443319 , así que también vamos a arreglarlo: < / p>
  main(args)0 

SER USO: main(args)1 . Sin embargo, esta implementación deja un archivo en el directorio temporal para cada main(args)2 presente en la línea de comandos; Llegaré a eso este último.

El segundo comportamiento que difiere es la forma en que maneja los conteos de línea en main(args)3 main(args)76655443324 main(args)5 utiliza el equivalente de main(args)6 para prepararlos cuando sea necesario; Haciendo la alineación entre la línea 999999 y 1000000 Go:

  main(args)7  

y el entre la línea 9999999 y 10000000 Go:

  main(args)8  

Si realizó lo mismo, podría simplificar un poco el código al no tener que leer cada archivo por adelantado.

También maneja main(args)9 A "Mejor" de manera cat(args)0 que simplemente elimina el formato total:

  cat(args)1  

Intentaré mantener eso. Sin embargo, cambiaría la forma en que maneja los caracteres que no prolongados porque los casos especiales 99887776555443332 99887766555443333 son de hecho manejo de pestañas y recién llegadas. Así que usaría un solo cat(args)4 en lugar de cat(args)5 + 998877665554433336 .

  cat(args)7  

Ahora para volver a los archivos permanecer en disco después de eso, corrió en la línea de comandos, puede definir una clase como:

  cat(args)9  

en lugar de la función def parse_command_line(): parser = argparse.ArgumentParser( description='concatenate files and print on the standard output') parser.add_argument('filenames', nargs='+', help='directory to scan') parser.add_argument('-r', '--repeat', dest='repeat', default=1, type=int, help='repeat output multiple times') parser.add_argument('-f', '--header', dest='header', default=False, help='precede every file with its name', action='store_true') parser.add_argument('-n', '--number', dest='number', default=False, help='number all output lines', action='store_true') parser.add_argument('-b', '--number-nonblank', dest='number_nonblank', default=False, help='number nonempty output lines', action='store_true') parser.add_argument('-A', '--show-all', dest='show_all', default=False, help='equivalent to -vET', action='store_true') parser.add_argument('-e', dest='show_ends_and_nonprinting', default=False, help='equivalent to -vE', action='store_true') parser.add_argument('-E', '--show-ends', dest='show_ends', default=False, help='display $ at end of each line', action='store_true') parser.add_argument('-t', dest='show_tabs_and_nonprinting', default=False, help='equivalent to -vT', action='store_true') parser.add_argument('-T', '--show-tabs', dest='show_tabs', default=False, help='display TAB characters as ^I', action='store_true') parser.add_argument('-v', '--show-nonprinting', dest='show_nonprinting', default=False, action='store_true', help='use ^ and M- notation, except for LFD and TAB') args = parser.parse_args() # Special chars option overlapping if args.show_all: args.show_tabs = True args.show_ends = True args.show_nonprinting = True if args.show_tabs_and_nonprinting: args.show_tabs = True args.show_nonprinting = True if args.show_ends_and_nonprinting: args.show_ends = True args.show_nonprinting = True if args.number_nonblank: args.number = True # See later why return args 0 . Debe cambiar la llamada a def parse_command_line(): parser = argparse.ArgumentParser( description='concatenate files and print on the standard output') parser.add_argument('filenames', nargs='+', help='directory to scan') parser.add_argument('-r', '--repeat', dest='repeat', default=1, type=int, help='repeat output multiple times') parser.add_argument('-f', '--header', dest='header', default=False, help='precede every file with its name', action='store_true') parser.add_argument('-n', '--number', dest='number', default=False, help='number all output lines', action='store_true') parser.add_argument('-b', '--number-nonblank', dest='number_nonblank', default=False, help='number nonempty output lines', action='store_true') parser.add_argument('-A', '--show-all', dest='show_all', default=False, help='equivalent to -vET', action='store_true') parser.add_argument('-e', dest='show_ends_and_nonprinting', default=False, help='equivalent to -vE', action='store_true') parser.add_argument('-E', '--show-ends', dest='show_ends', default=False, help='display $ at end of each line', action='store_true') parser.add_argument('-t', dest='show_tabs_and_nonprinting', default=False, help='equivalent to -vT', action='store_true') parser.add_argument('-T', '--show-tabs', dest='show_tabs', default=False, help='display TAB characters as ^I', action='store_true') parser.add_argument('-v', '--show-nonprinting', dest='show_nonprinting', default=False, action='store_true', help='use ^ and M- notation, except for LFD and TAB') args = parser.parse_args() # Special chars option overlapping if args.show_all: args.show_tabs = True args.show_ends = True args.show_nonprinting = True if args.show_tabs_and_nonprinting: args.show_tabs = True args.show_nonprinting = True if args.show_ends_and_nonprinting: args.show_ends = True args.show_nonprinting = True if args.number_nonblank: args.number = True # See later why return args 1 en def parse_command_line(): parser = argparse.ArgumentParser( description='concatenate files and print on the standard output') parser.add_argument('filenames', nargs='+', help='directory to scan') parser.add_argument('-r', '--repeat', dest='repeat', default=1, type=int, help='repeat output multiple times') parser.add_argument('-f', '--header', dest='header', default=False, help='precede every file with its name', action='store_true') parser.add_argument('-n', '--number', dest='number', default=False, help='number all output lines', action='store_true') parser.add_argument('-b', '--number-nonblank', dest='number_nonblank', default=False, help='number nonempty output lines', action='store_true') parser.add_argument('-A', '--show-all', dest='show_all', default=False, help='equivalent to -vET', action='store_true') parser.add_argument('-e', dest='show_ends_and_nonprinting', default=False, help='equivalent to -vE', action='store_true') parser.add_argument('-E', '--show-ends', dest='show_ends', default=False, help='display $ at end of each line', action='store_true') parser.add_argument('-t', dest='show_tabs_and_nonprinting', default=False, help='equivalent to -vT', action='store_true') parser.add_argument('-T', '--show-tabs', dest='show_tabs', default=False, help='display TAB characters as ^I', action='store_true') parser.add_argument('-v', '--show-nonprinting', dest='show_nonprinting', default=False, action='store_true', help='use ^ and M- notation, except for LFD and TAB') args = parser.parse_args() # Special chars option overlapping if args.show_all: args.show_tabs = True args.show_ends = True args.show_nonprinting = True if args.show_tabs_and_nonprinting: args.show_tabs = True args.show_nonprinting = True if args.show_ends_and_nonprinting: args.show_ends = True args.show_nonprinting = True if args.number_nonblank: args.number = True # See later why return args 2 para que aún trabaje y figura una forma de llamar a eliminar ese objeto al final de las repeticiones.

 

At least the code is not at the top-level of the file, but you should really use functions to document what is going on. Well chosen names can go a long way into conveying what you are doing and why.

I also have a tendency to extract out the argparse machinery out of the way of the actual computation. This way, I can mock this part in my tests:

>>> class Args: ...   def __getattr__(self, key): ...     return None ...  >>> args = Args() >>> args.foo >>> args.spam = 'egg' >>> args.bacon = 'foo' >>> vars(args) {'spam': 'egg', 'bacon': 'foo'} 

and call main(args) (or in your case cat(args)) without having to rely on the command-line. So lets build that:

def parse_command_line():     parser = argparse.ArgumentParser(             description='concatenate files and print on the standard output')      parser.add_argument('filenames', nargs='+', help='directory to scan')     parser.add_argument('-r', '--repeat', dest='repeat', default=1, type=int,                         help='repeat output multiple times')     parser.add_argument('-f', '--header', dest='header', default=False,                         help='precede every file with its name',                         action='store_true')      parser.add_argument('-n', '--number', dest='number', default=False,                         help='number all output lines', action='store_true')     parser.add_argument('-b', '--number-nonblank', dest='number_nonblank',                         default=False, help='number nonempty output lines',                         action='store_true')      parser.add_argument('-A', '--show-all', dest='show_all', default=False,                         help='equivalent to -vET', action='store_true')     parser.add_argument('-e', dest='show_ends_and_nonprinting', default=False,                         help='equivalent to -vE', action='store_true')     parser.add_argument('-E', '--show-ends', dest='show_ends', default=False,                         help='display $ at end of each line', action='store_true')     parser.add_argument('-t', dest='show_tabs_and_nonprinting', default=False,                         help='equivalent to -vT', action='store_true')     parser.add_argument('-T', '--show-tabs', dest='show_tabs', default=False,                         help='display TAB characters as ^I', action='store_true')     parser.add_argument('-v', '--show-nonprinting', dest='show_nonprinting',                         default=False, action='store_true',                         help='use ^ and M- notation, except for LFD and TAB')      args = parser.parse_args()       # Special chars option overlapping     if args.show_all:         args.show_tabs = True         args.show_ends = True         args.show_nonprinting = True     if args.show_tabs_and_nonprinting:         args.show_tabs = True         args.show_nonprinting = True     if args.show_ends_and_nonprinting:         args.show_ends = True         args.show_nonprinting = True     if args.number_nonblank:         args.number = True  # See later why      return args 

I also inverted a bit the management of overlapping options as I feel it reads better.

This way, even if you put the rest of the code into a single def cat(args): function, the main part is down to:

if __name__ == '__main__':     args = parse_command_line()     cat(args) 

which should be much easier to start testing.


Now, as regard to the core of your program, I can see at least 2 undocumented behaviour that differ from the cat utility on linux. First of, youxe2x80x99re only ever reading one line of input instead of the whole stdin. Consider use cases like grep PATTERN unfiltered.txt | cat header.txt - footer.txt that the original cat handles perfectly (i.e. all of the output of grep is printed between the content of header.txt and footer.txt). You don't. Since I also don't understand why you would repeat every other files but stdin, I suggest using the tempfile module to buffer the content of stdin.

We will thus need to change the content of the input array, so let's encapsulate that in a function that perform the globing too, to clean up this chain command. While Ixe2x80x99m at it, cat also allow to specify no filename on the command line, which is treated the same as a single '-' so let's fix that as well:

def sanitize_filenames(file_patterns):     for pattern in file_patterns:         if pattern == '-':             yield buffer_stdin()         else:             yield from glob.glob(pattern)      if not file_patterns:         yield buffer_stdin()   def buffer_stdin():     with tempfile.NamedTemporaryFile(delete=False) as stdin:         for line in sys.stdin:             stdin.write(line)     return stdin.name 

Usage being: filenames = list(sanitize_filenames(args.filenames)). However, this implementation leave a file in the temporary directory for each '-' present on the command line; Ixe2x80x99ll get to it latter.

The second behaviour that differs is the way you handle line counts on -n or -b options. cat uses the equivalent of '{:>6}\t'.format(line_number) to prepend them when needed; making the alignment between line 999999 and 1000000 go:

999999  one line 1000000 another line 

and the one between line 9999999 and 10000000 go:

9999999 one line 10000000    another line 

If you made the same, you could simplify the code a bit by not having to read each file upfront.

You also handle -Eb a "better" way than cat which just remove the formatting altogether:

9999999 one line$ $ 10000000    another line$ 

Ixe2x80x99ll try to keep that. I would however change the way you handle nonprinting characters because the special cases I and J are in fact handling of tabs and newlines. So I'd use a single translate instead of replace + translate.

def prepend_line_numbers(numbers):     print('{:>6}\t'.format(numbers), end='')   def build_special_chars_table(args):     table = {         9: '^I' if args.show_tabs else '\t',         10: '$' if args.show_ends else '',     }     if args.show_nonprinting:         nonprinting_representation = '@{}[\]^!'.format(string.ascii_uppercase)         table.update({             k: '^' + v             for k, v in enumerate(nonprinting_representation)             if v not in 'IJ'         })     return table   def cat(args):     nonprinting = build_special_chars_table(args)     header_width = shutil.get_terminal_size().columns // 2     line_count = 0     for filename in sanitize_filenames(args.filenames):         if args.header:             print('{:=^{}}'.format(filename, header_width))          for _ in range(args.repeat):             with open(filename, 'rb') as file:                 for line in file:                     blank_line = not line.rstrip()                     if not (args.number_nonblank and blank_line):                         line_count += 1                         if args.number:                             prepend_line_numbers(line_count)                     elif args.number:  # Useful there to have that True even when -b only                         # For -E under -b                         prepend_line_numbers(' ' * len(str(line_count)))                     print(line                             .decode(errors='backslashreplace')                             .translate(nonprinting)) 

Now to get back on the files staying on disc after that utily ran over some '-' on the command line, you can define a class like:

class StdInWrapper:     def __init__(self):         with tempfile.NamedTemporaryFile(delete=False) as stdin:             self.name = stdin.name             for line in sys.stdin:                 stdin.write(line)      def __str__(self):         return self.name      def remove(self):         os.remove(self.name) 

instead of the buffer_stdin function. You would need to change the call to open(filename, 'rb') into open(str(filename), 'rb') so that it still work and figure a way to call remove on that object at the end of the repetitions.

 
 
 
 

Relacionados problema

4  Huffman que codifica como Utilidad de la línea de comandos de estilo UNIX  ( Huffman encoding as unix style command line utility ) 
Después de ver a Tom Scott explicar Huffman codificando en este video youtube , quería Implementalo yo mismo. Quiero usar este proyecto para promover mi comp...

5  Portabilidad y "Rincón oscuro" Gotchas en este programa "WatchDog"  ( Portability and dark corner gotchas in this watchdog program ) 
El siguiente programa está destinado a "vigilando" un proceso secundario, y todos los subprocesos, el niño engendra . El comportamiento previsto es: Un ...

3  Línea de comando FFMPEG para mostrar dos videos lado a lado  ( Ffmpeg command line for showing two videos side by side ) 
Aquí está la línea de comandos para jugar dos videos de lado a lado en sincronización en FFMPEG (FFPlay). Puede ser útil para los videos de comado, por ejempl...

1  Fortuna de UNIX V7 implementado en Java  ( Fortune of unix v7 implemented in java ) 
Estoy aprendiendo a Java recientemente, este es uno de mi primer programa escrito en este idioma. Reimplementación de una versión básica de Fortune . Esta ...

4  Find-grep-sed para la búsqueda de proyectos y reemplazar  ( Find grep sed for project wide search replace ) 
Siempre olvido cómo hacer esto de manera eficiente con el Arglista de VIM. Inspiración de dibujo en una publicación en Desbordamiento de la pila , escribí un...

9  Plataforma independiente donde la función  ( Platform independent whereis function ) 
Estoy tratando de escribir un paquete independiente de Python (& gt; = 3.6.5) que tiene un puñado de archivos de clase Java. Clase que necesitan compilarse us...

3  Aplicación singularidad e IPC unilateral en UNIX  ( Application uniqueness and unilateral ipc on unix ) 
este programa Detecta la singularidad de la aplicación, si la aplicación es una instancia única / primaria, lanza un servidor, de lo contrario, un cliente...

1  ¿Cómo puedo optimizar aún más este script Perl para encontrar todos los archivos y directorios sin reconocimiento en UNIX?  ( How can i further optimize this perl script for finding all unowned files and di ) 
(originalmente publicado en Desbordamiento de pila ) Siguiendo mis hallazgos y sugerencias en mi otra publicación Cómo excluir una lista de rutas de dir...

5  Programa de coincidencia y listado de PathName  ( Pathname matching and listing program ) 
Este programa Python 3 emite una lista de todos los nombres de ruta en el sistema de archivos que coincide con una lista de reglas tomadas de un archivo. Pued...

7  Patrón Pseudoportable C Script - Seguimiento  ( Pseudoportable c script pattern follow up ) 
Consulte la iteración inicial / anterior . He reescrito el script siguiendo la respuesta de @ 200_success . Ahora se ve así: #! /bin/bash # Create a...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos