ฉันจะทำให้โปรแกรมหลามทำงานอย่างไรเช่นเครื่องมือยูนิกซ์ที่เหมาะสม?


24

ฉันมีสคริปต์ Python อยู่สองสามตัวและฉันก็กำลังเขียนมันใหม่ ฉันมีปัญหาเดียวกันกับพวกเขาทั้งหมด

ฉันไม่เห็นได้ชัดว่าจะเขียนโปรแกรมอย่างไรเพื่อให้พวกเขาทำตัวเหมือนเครื่องมือยูนิกซ์ที่เหมาะสม

เพราะสิ่งนี้

$ cat characters | progname

และนี่

$ progname characters

ควรสร้างผลลัพธ์เดียวกัน

สิ่งที่ใกล้เคียงที่สุดที่ฉันสามารถพบได้ใน Python คือไลบรารี fileinput น่าเสียดายที่ฉันไม่เห็นวิธีเขียนสคริปต์ Python ของฉันอีกครั้งซึ่งทั้งหมดมีลักษณะดังนี้:

#!/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 ประมวลผล stdin หากมี stdin และประมวลผลไฟล์หากมีไฟล์ แต่มันวนซ้ำกว่าบรรทัดเดียว

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

ฉันไม่เข้าใจจริงๆ ฉันเดาว่าคุณกำลังจัดการกับไฟล์ขนาดเล็กหรือหากคุณไม่ได้ทำอะไรกับไฟล์มากขนาดนี้อาจดูเหมือนชัดเจน แต่สำหรับจุดประสงค์ของฉันสิ่งนี้ทำให้ช้ากว่าการเปิดไฟล์ทั้งหมดและอ่านมันเป็นสตริงดังที่ได้กล่าวไว้ข้างต้น

ขณะนี้ฉันเรียกใช้สคริปต์ด้านบนเช่น

$ pythonscript textfilename1 > textfilename2

แต่ฉันต้องการที่จะสามารถเรียกใช้มัน (และพี่น้องของมัน) ในท่อเหมือน

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

คำตอบ:


9

ทำไมไม่เพียงแค่

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

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

12
sys.stdinควรใช้แทนเนื่องจากพกพาได้ดีกว่าฮาร์ดโค้ดที่มีเส้นทางไปยังไฟล์
Piotr Dobrogost

sys.stdinควรใช้แทนอย่างที่ Piotr พูด
smci

แต่sys.stdinเป็นไฟล์และมันเปิดอยู่แล้วและจะต้องไม่ถูกปิด เป็นไปไม่ได้ที่จะจัดการเหมือนกับอาร์กิวเมนต์ไฟล์โดยไม่ต้องผ่านห่วง
alexis

@alexis แน่นอนถ้าคุณต้องการปิดfหรือต้องการใช้ตัวจัดการบริบทคุณต้องมีสิ่งที่ซับซ้อนกว่านี้ ดูคำตอบใหม่ของฉันเป็นทางเลือก
มิเคล

12

ตรวจสอบว่าชื่อไฟล์ถูกกำหนดเป็นอาร์กิวเมนต์หรืออ่านจากsys.stdinอื่น

บางสิ่งเช่นนี้

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

มันคล้ายกับคำตอบของ Mikel ยกเว้นว่าจะใช้sysโมดูล ฉันคิดว่าพวกเขามีในนั้นมันจะต้องมีเหตุผล ...


จะเกิดอะไรขึ้นหากมีการระบุชื่อไฟล์สองชื่อไว้ในบรรทัดคำสั่ง
Mikel

3
โอ้! ฉันไม่ได้รำคาญที่จะแสดงมันเพราะมันแสดงให้เห็นแล้วในคำตอบของคุณ ในบางจุดคุณต้องเชื่อใจผู้ใช้ในการตัดสินใจเลือกสิ่งที่เธอต้องการ แต่อย่าลังเลที่จะแก้ไขหากคุณเชื่อว่าสิ่งนี้ดีที่สุด จุดของฉันเป็นเพียงการแทนที่ด้วย"open(/dev/stdin") sys.stdin
rahmu

2
คุณอาจต้องการตรวจสอบif len(sys.argv)>1:แทนif sys.argv[1]:มิฉะนั้นคุณจะได้รับดัชนีจากข้อผิดพลาดในช่วง
Yibo ยาง

3

วิธีที่ฉันชอบในการทำมันกลายเป็น ... (และนี่นำมาจากบล็อก Linux ตัวเล็ก ๆ ที่เรียกว่าHarbinger's Hollow )

#!/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()

เหตุผลที่ฉันชอบสิ่งนี้ที่สุดก็คืออย่างที่บล็อคบอกว่ามันแค่ส่งข้อความโง่ ๆ ถ้าบังเอิญถูกเรียกโดยไม่ป้อนข้อมูล นอกจากนี้ยังมีการรวมเข้ากับสคริปต์ Python ที่มีอยู่ทั้งหมดของฉันที่ฉันได้ปรับเปลี่ยนทั้งหมดเพื่อรวมไว้ในสคริปต์


3
บางครั้งคุณต้องการป้อนอินพุตแบบโต้ตอบจาก tty การตรวจสอบisattyและประกันตัวออกไปไม่สอดคล้องกับปรัชญาของตัวกรอง Unix
musiphil

นอกเหนือจากisattyหูดนี้ครอบคลุมพื้นดินที่มีประโยชน์และมีความสำคัญไม่พบในคำตอบอื่น ๆ ดังนั้นมันได้รับ upvote ของฉัน
tripleee

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/stdinมันไม่สามารถใช้ได้กับทุกระบบของฉัน
Mikel

0

ฉันใช้โซลูชันนี้และทำงานได้อย่างมีเสน่ห์ อันที่จริงผมใช้ในสคริปต์ Calle unaccentที่ lowercases และลบสำเนียงจากสตริงที่กำหนด

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

ผมคิดว่าเวลา firest ที่ผมเห็นการแก้ปัญหานี้เป็นที่นี่


0

หากระบบของคุณไม่มี/dev/stdinหรือคุณต้องการวิธีแก้ปัญหาทั่วไปเพิ่มเติมคุณสามารถลองทำสิ่งที่ซับซ้อนกว่าเช่น:

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:
            ...

เหตุใดคุณจึงย้ายตัวชี้ไฟล์เมื่อออก ความคิดที่ไม่ดี หากอินพุตถูกเปลี่ยนเส้นทางจากไฟล์โปรแกรมถัดไปจะอ่านอีกครั้ง (และถ้า stdin เป็นเทอร์มินัลก็มักจะไม่ทำอะไรเลยใช่ไหม) แค่ปล่อยให้มันอยู่คนเดียว
alexis

ใช่เสร็จแล้ว ฉันแค่คิดว่ามันน่ารักที่จะใช้-หลายครั้ง :)
มิเคล
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.