Làm cách nào để tạo chương trình python hoạt động như các công cụ unix thích hợp?


24

Tôi có một vài tập lệnh Python nằm xung quanh và tôi đang viết lại chúng. Tôi có cùng một vấn đề với tất cả chúng.

Tôi không rõ ràng về cách viết chương trình để chúng hoạt động như các công cụ unix thích hợp.

Bởi vì điều này

$ cat characters | progname

và cái này

$ progname characters

nên sản xuất cùng một đầu ra.

Thứ gần nhất tôi có thể tìm thấy trong Python là thư viện fileinput. Thật không may, tôi thực sự không thấy cách viết lại các tập lệnh Python của mình, tất cả đều trông như thế này:

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

Thư viện fileinput xử lý stdin nếu có stdin và xử lý tệp nếu có tệp. Nhưng nó lặp đi lặp lại trên các dòng duy nhất.

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

Tôi thực sự không có được điều đó. Tôi đoán nếu bạn đang xử lý các tệp nhỏ hoặc nếu bạn không làm gì nhiều với các tệp, điều này có vẻ rõ ràng. Nhưng, với mục đích của tôi, điều này làm cho nó chậm hơn nhiều so với việc chỉ mở toàn bộ tệp và đọc nó thành một chuỗi, như trên.

Hiện tại tôi chạy đoạn script trên như

$ pythonscript textfilename1 > textfilename2

Nhưng tôi muốn có thể chạy nó (và anh em của nó) trong các đường ống, như

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

Câu trả lời:


9

Tại sao không chỉ

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

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

12
sys.stdinnên được sử dụng thay vì nó dễ mang theo hơn đường dẫn được mã hóa cứng.
Piotr Dobrogost

sys.stdinnên được sử dụng thay thế, như Piotr nói
smci

Nhưng sys.stdinlà một tập tin và nó đã được mở và không được đóng lại. Không thể xử lý giống như một đối số tập tin mà không nhảy qua vòng.
alexis

@alexis Chắc chắn, nếu bạn muốn đóng f, hoặc muốn sử dụng trình quản lý bối cảnh, bạn cần một cái gì đó phức tạp hơn. Xem câu trả lời mới của tôi như là một thay thế.
Mikel

12

Kiểm tra nếu một tên tệp được đưa ra làm đối số, hoặc người khác đọc từ sys.stdin.

Một cái gì đó như thế này:

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

Nó tương tự như câu trả lời của Mikel ngoại trừ nó sử dụng sysmô-đun. Tôi nghĩ rằng nếu họ có nó trong đó thì phải vì một lý do ...


Nếu hai tên tệp được chỉ định trên dòng lệnh thì sao?
Mikel

3
Ôi tuyệt đối! Tôi không bận tâm đến việc hiển thị nó vì nó đã được hiển thị trong câu trả lời của bạn. Tại một số điểm bạn phải tin tưởng người dùng để quyết định những gì cô ấy cần. Nhưng hãy thoải mái chỉnh sửa nếu bạn tin rằng điều này là tốt nhất. Quan điểm của tôi là chỉ để thay thế "open(/dev/stdin")bằng sys.stdin.
rahmu

2
bạn có thể muốn kiểm tra if len(sys.argv)>1:thay vì if sys.argv[1]:nếu không, bạn sẽ nhận được một chỉ số nằm ngoài phạm vi lỗi
Yibo Yang

3

Cách làm ưa thích của tôi hóa ra là ... (và điều này được lấy từ một blog Linux nhỏ xinh có tên là 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()

Lý do tại sao tôi thích điều này nhất là, như blogger nói, nó chỉ xuất ra một thông điệp ngớ ngẩn nếu vô tình gọi mà không có đầu vào. Nó cũng có các vị trí độc đáo trong tất cả các tập lệnh Python hiện có của tôi mà tôi đã sửa đổi tất cả chúng để bao gồm nó.


3
Đôi khi bạn muốn nhập đầu vào tương tác từ một tty; kiểm tra isattyvà bảo lãnh không phù hợp với triết lý của các bộ lọc Unix.
musiphil

Ngoài isattymụn cóc, điều này bao gồm mặt bằng hữu ích và quan trọng không tìm thấy trong các câu trả lời khác, vì vậy nó nhận được sự ủng hộ của tôi.
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)

Đây là cách tôi sẽ viết nó, nếu /dev/stdinkhông có sẵn trên tất cả các hệ thống của tôi.
Mikel

0

Tôi đang sử dụng giải pháp này và nó hoạt động như một sự quyến rũ. Trên thực tế tôi đang sử dụng trong một tập lệnh calle unacent mà viết thường và loại bỏ các dấu từ một chuỗi nhất định

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

Tôi đoán lần đầu tiên tôi thấy giải pháp này là ở đây .


0

Nếu hệ thống của bạn không có /dev/stdinhoặc bạn muốn một giải pháp tổng quát hơn, bạn có thể thử một cái gì đó phức tạp hơn như:

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

Tại sao bạn di chuyển con trỏ tập tin khi thoát? Ý kiến ​​tồi. Nếu đầu vào được chuyển hướng từ một tệp, chương trình tiếp theo sẽ đọc lại. (Và nếu stdin là một thiết bị đầu cuối, tìm kiếm thường không làm gì cả, phải không?) Chỉ cần để nó một mình.
alexis

Vâng, xong rồi. Tôi chỉ nghĩ rằng nó là dễ thương để sử dụng -nhiều lần. :)
Mikel
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.