Python programlarının uygun unix araçları gibi davranmasını nasıl sağlayabilirim?


24

Etrafımda birkaç Python senaryosu var ve onları yeniden yazmaya çalışıyorum. Hepsinde de aynı problem var.

Programların nasıl yazıldığını ve böylece unix araçları gibi davranmalarını açıklamıyor.

Çünkü bu

$ cat characters | progname

ve bu

$ progname characters

aynı çıktıyı üretmelidir.

Python'da bulabileceğim en yakın şey fileinput kütüphanesiydi. Ne yazık ki, hepsi bu gibi görünen Python scriptlerimi nasıl yeniden yazacağımı gerçekten göremiyorum:

#!/usr/bin/env python 
# coding=UTF-8

import sys, re

for file in sys.argv[1:]:
    f = open(file)
    fs = f.read()
    regexnl = re.compile('[^\s\w.,?!:;-]')
    rstuff = regexnl.sub('', fs)
    f.close()
    print rstuff

Fileinput kütüphanesi bir stdin varsa stdin'i işler ve eğer bir dosya varsa bir dosyayı işler. Ancak, tek satırlar üzerinden yinelenir.

import fileinput
for line in fileinput.input():
    process(line)

Gerçekten anlamadım. Küçük dosyalarla uğraşıyorsanız veya dosyalara çok fazla şey yapmıyorsanız, bu açık görünebilir. Ancak, amaçlarıma göre bu, dosyayı tümüyle açmak ve yukarıdaki gibi bir dizgede okumaktan çok daha yavaş hale getiriyor.

Şu anda yukarıdaki betiği çalıştırıyorum

$ pythonscript textfilename1 > textfilename2

Ama onu (ve onun kardeşlerini) borularda çalıştırabilmek istiyorum.

$ grep pattern textfile1 | pythonscript | pythonscript | pythonscript > textfile2

Yanıtlar:


9

Neden sadece

files = sys.argv[1:]
if not files:
    files = ["/dev/stdin"]

for file in files:
    f = open(file)
    ...

12
sys.stdinbunun yerine, dosyanın kodlanmış yolundan daha taşınabilir olması nedeniyle kullanılmalıdır.
Piotr Dobrogost,

sys.stdinPiotr söylediği gibi, bunun yerine kullanılmalıdır
SMCI

Ancak sys.stdinbir dosyadır ve zaten açıktır ve kapatılmaması gerekir. Çemberlere atlamaksızın tıpkı bir dosya argümanı gibi işlemek imkansız .
alexis

@alexis Tabii, kapatmak fveya bir içerik yöneticisi kullanmak istiyorsanız daha karmaşık bir şeye ihtiyacınız var. Yeni cevabımı alternatif olarak gör.
Mikel

12

Bir dosya adının bir argüman olarak verilip verilmediğini veya başka birinden okunup okunmadığını kontrol edin sys.stdin.

Bunun gibi bir şey:

if sys.argv[1]:
   f = open(sys.argv[1])
else:
   f = sys.stdin 

sysModülü kullanması dışında Mikel'in cevabına benzer . Sanırım oradalarsa bir sebebi olmalı ...


Komut satırında iki dosya adı belirtilirse ne olur?
Mikel

3
Ah kesinlikle! Bunu gösterme zahmetine girmedim, çünkü cevabınız çoktan gösterildi. Bir noktada, neye ihtiyacı olduğuna karar vermek için kullanıcıya güvenmek zorundasın. Ancak bunun en iyisi olduğuna inanıyorsanız düzenlemekten çekinmeyin. Demek istediğim sadece değiştirmektir "open(/dev/stdin")ile sys.stdin.
rahmu

2
Aksi takdirde aralık hatası dışında bir indeks almak if len(sys.argv)>1:yerine kontrol etmek isteyebilirsinizif sys.argv[1]:
Yibo Yang

3

Bunu yapmam için tercih ettiğim yol şu ... (ve bu Harbinger's Hollow adlı küçük bir Linux blogundan alınmıştır )

#!/usr/bin/env python

import argparse, sys

parser = argparse.ArgumentParser()
parser.add_argument('filename', nargs='?')
args = parser.parse_args()
if args.filename:
    string = open(args.filename).read()
elif not sys.stdin.isatty():
    string = sys.stdin.read()
else:
    parser.print_help()

Bunu en çok sevmemin nedeni, blog yazarının dediği gibi, yanlışlıkla giriş yapılmadan çağrıldığında aptalca bir mesaj çıkarmasıdır. Ayrıca mevcut Python komut dosyalarımın tümüne çok hoş bir şekilde giriyor ve hepsini dahil edecek şekilde değiştirdim.


3
Bazen bir tty'den girişi etkileşimli olarak girmek istersiniz; kontrol isattyve iptali, Unix filtrelerinin felsefesine uygun değildir.
musiphil

isattySiğil dışında , bu diğer cevaplarda bulunmayan yararlı ve önemli bir zemini kapsar, bu yüzden benim kazancımı alır.
üçlü

3
files=sys.argv[1:]

for f in files or [sys.stdin]:
   if isinstance(f, file):
      txt = f.read()
   else:
      txt = open(f).read()

   process(txt)

/dev/stdinTüm sistemlerimde kullanılamaz olsaydı , böyle yazardım.
Mikel

0

Bu çözümü kullanıyorum ve bir cazibe gibi çalışıyor. Aslında belli bir dizgeden aksanları düşüren ve silen unaccent komut dosyasında kullanıyorum

argument = sys.argv[1:] if len(sys.argv) > 1 else sys.stdin.read()

Sanırım bu çözümün en kötü zamanı buradaydı .


0

Sisteminizde yoksa /dev/stdinveya daha genel bir çözüm istiyorsanız, daha karmaşık bir şey deneyebilirsiniz:

class Stdin(object):
    def __getattr__(self, attr):
        return getattr(sys.stdin, attr)

    def __enter__(self):
        return self

def myopen(path):
    if path == "-":
        return Stdin()
    return open(path)

for n in sys.argv[1:] or ["-"]:
    with myopen(n) as f:
            ...

Neden dosya işaretçisini çıkışta hareket ettiriyorsunuz? Kötü bir fikir. Girdi bir dosyadan yönlendirildi ise, bir sonraki program tekrar okuyacak. (Ve eğer stdin bir terminal ise, aramak genellikle hiçbir şey yapmaz, değil mi?) Sadece rahat bırakın.
alexis,

Evet bitti. Sadece -birkaç kez kullanmanın hoş olduğunu düşündüm . :)
Mikel
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.