2010-01-18 27 views
7

tôi thường có nhiệm vụ lập trình shell nơi tôi chạy vào mô hình này:Cách tốt nhất để sửa đổi tệp khi sử dụng đường ống?

cat file | some_script > file 

này là không an toàn - mèo có thể không đọc toàn bộ tập tin trước khi some_script bắt đầu bằng văn bản cho nó. Tôi không thực sự muốn viết kết quả vào một tập tin tạm thời (chậm chạp của nó, và tôi không muốn biến chứng thêm về suy nghĩ về một tên mới duy nhất).

Có lẽ, có một lệnh shell tiêu chuẩn sẽ đệm toàn bộ luồng cho đến khi đạt được EOF? Một cái gì đó như:

cat file | bufferUntilEOF | script > file 

Ý tưởng?

+0

Um, xargs nên thực hiện thủ thuật, đúng không? –

+0

Tôi không tin như vậy. Vâng, có thể nó, nhưng tài liệu của nó nói rằng vấn đề nó giải quyết là xử lý các trường hợp giới hạn đối số lệnh được thực hiện. Nó không nói rằng nó đệm tất cả các stdin trước khi mở stdout. – user48956

+0

Tôi nghĩ rằng có các tùy chọn để xargs đối phó với kích thước bộ đệm. –

Trả lời

1

Sử dụng tệp tạm thời là IMO tốt hơn là cố gắng đệm dữ liệu trong đường ống.

Nó gần như đánh bại mục đích của đường ống để đệm chúng.

+0

Vâng, có thể. Nghe như một lập luận tôn giáo. Tôi biết tất cả các tập tin dễ dàng phù hợp trong một phần nhỏ của bộ nhớ chính (kịch bản shell của tôi sẽ hoạt động trên mỗi tập tin nguồn trong một kho SVN rất lớn). Tệp tạm thời sẽ làm cho tệp chạy nhanh gấp hai lần khi cần thiết (ít nhất là trong Cygwin). – user48956

+0

Điều đó có thể xảy ra. Nếu mã của bạn sẽ luôn được sử dụng theo cách bạn mong đợi, thì sẽ có ý nghĩa khi thực hiện các giao dịch khôn ngoan ... –

+0

@stuartreynolds: Sử dụng tệp tạm thời sẽ KHÔNG làm cho tệp chạy chậm hơn, ngoại trừ có thể một số không đáng kể liên tục thời gian để đổi tên tệp trở lại tên ban đầu của nó. – Juliano

3

Bạn đang tìm kiếm sponge.

+0

Điều đó trông giống như một giải pháp tốt ngoại trừ việc tôi không muốn yêu cầu tất cả người dùng tập lệnh của tôi cài đặt các phụ thuộc bổ sung (hoặc biên dịch bất kỳ mã nào). -Không phải là một sự thay thế bằng cách sử dụng các tiện ích tiêu chuẩn hoặc các tính năng shell được xây dựng sẵn? – user48956

+1

Tôi không khuyên dùng miếng bọt biển. Nếu bất kỳ lệnh nào trong đường dẫn của bạn (ngoài miếng bọt biển) không thành công (ví dụ, do lỗi cú pháp, đối số không hợp lệ, v.v.), nó sẽ xóa tệp và bạn kết thúc mà không cần cả tệp gốc và tệp đích. – Juliano

+0

/tmp có thể được gắn trong bộ nhớ (ít nhất là dưới Linux). Trong trường hợp này, tôi hy vọng rằng điều này có thể rất nhanh. Không chắc chắn về/tmp trong Cygwin mặc dù. Cygwin có giữ nó trong bộ nhớ không? – user48956

4

Sử dụng tệp tạm thời là giải pháp đúng ở đây. Khi bạn sử dụng một chuyển hướng như '>', nó được xử lý bởi shell và không cần biết có bao nhiêu lệnh trong đường dẫn của bạn, shell sẽ tự do xóa và ghi đè lên tệp đầu ra trước khi bất kỳ lệnh nào được thực thi (trong khi thiết lập đường dẫn).

2

Sử dụng mktemp(1) hoặc tempfile(1) giúp bạn tiết kiệm chi phí phải nghĩ ra tên tệp duy nhất.

+0

bỏ phiếu, công cụ tuyệt vời. – Anders

1

Tôi nghĩ rằng cách tốt nhất là sử dụng tệp tạm thời. Tuy nhiên, nếu bạn muốn một cách tiếp cận khác, bạn có thể sử dụng một cái gì đó như awk để đệm đầu vào vào bộ nhớ trước khi ứng dụng của bạn bắt đầu nhận đầu vào. Kịch bản sau sẽ đệm tất cả các đầu vào vào mảng lines trước khi nó bắt đầu xuất nó cho người tiêu dùng tiếp theo trong đường ống.

{ lines[NR] = $0; } 
END { 
    for (line_no=1; line_no<=NR; ++line_no) { 
     print lines[line_no]; 
    } 
} 

Bạn có thể sụp đổ nó vào một lớp lót nếu bạn muốn:

cat file | awk '{lines[NR]=$0;} END {for(i=1;i<=NR;++i) print lines[i];}' > file 

Với tất cả điều đó, tôi vẫn sẽ khuyên bạn sử dụng một tập tin tạm thời cho đầu ra và sau đó ghi đè lên các tập tin ban đầu với nó.

2

Giống như nhiều người khác, tôi thích sử dụng các tệp tạm thời. Tôi sử dụng quá trình shell-id như một phần của tên tạm thời để nếu nhiều bản sao của tập lệnh đang chạy cùng một lúc, chúng sẽ không xung đột. Cuối cùng, sau đó tôi chỉ ghi đè lên tệp gốc nếu kịch bản thành công (sử dụng toán tử boolean short-circuiting - nó hơi dày đặc nhưng rất tốt cho các dòng lệnh đơn giản). Đặt tất cả lại với nhau, nó sẽ trông giống như:

some_script <file> smscrpt.$$ && mv smscrpt.$$ file 

Điều này sẽ để lại tệp tạm thời nếu lệnh không thành công. Nếu bạn muốn dọn dẹp về lỗi, bạn có thể thay đổi điều đó để:

some_script <file> smscrpt.$$ && mv smscrpt.$$ file || rm smscrpt.$$ 

BTW, tôi đã thoát khỏi việc sử dụng kém của mèo và thay thế nó với chuyển hướng đầu vào.

+0

Cảm ơn - đó là một mẹo hay. Bạn sẽ bị rò rỉ một tệp nếu some_script thất bại.Cần xử lý trường hợp đó: "(some_script < file > smscrpt. $$ && mv smscrpt. $$ tệp) || \ rm -f smscrpt. $$" Tuy nhiên, sẽ thích một cái gì đó như: "(some_script file "bởi vì (i) cách dễ đọc hơn, (ii) Tôi không phải nhớ kiểm tra lỗi (iii) Tôi tin rằng nó sẽ chạy nhanh hơn một chút dưới Cygwin vì tập tin chậm chạp của Godawful truy cập. – user48956

+2

@stuartreynolds - người khác đăng về miếng bọt biển và bạn đã từ chối điều đó vì nó không phải là tiêu chuẩn. Không có tiêu chuẩn nào làm những gì bạn muốn. –

+1

@klatchko - Tôi nghĩ rằng một cái gì đó giống như Sponge * là * câu trả lời tôi đang tìm kiếm (với những lời khuyên tôi đã đề cập - nó không thực sự dễ dàng cho tôi để sử dụng nó rộng rãi). IMO, nếu thực sự không có gì để làm gì, * và * chức năng của miếng bọt biển là nền tảng cho kịch bản lệnh shell (đệm để tránh các tệp tham nhũng âm thanh khá cơ bản đối với tôi), thì có lẽ nó phải là một phần của bash hoặc GNU chuẩn toolset (trong trường hợp này tôi hy vọng một người nào đó sẽ chỉ ra tại sao chúng ta không cần miếng bọt biển ở tất cả ... bất cứ ai?). Tôi có * thực sự * phải tạo một tệp tạm thời để làm điều này không? – user48956

1

Để đối phó với the OP's question above về việc sử dụng sponge mà không phụ thuộc bên ngoài, và xây dựng trên @D.Shawley's answer, bạn có thể có tác dụng miếng bọt biển với chỉ một sự phụ thuộc vào gawk, mà không phải là không phổ biến trên Unix hoặc các hệ thống Unix-like:

cat foo | gawk -voutfn=foo '{lines[NR]=$0;} END {if(NR>0){print lines[1]>outfn;} for(i=2;i<=NR;++i) print lines[i] >> outfn;}' 

Việc kiểm tra NR>0 là để cắt bớt tệp đầu vào.

Để sử dụng tính năng này trong tập lệnh hệ vỏ, hãy thay đổi -voutfn=foo thành -voutfn="$1" hoặc bất kỳ cú pháp nào mà trình bao của bạn sử dụng cho đối số tên tệp. Ví dụ:

#!/bin/bash 
cat "$1" | gawk -voutfn="$1" '{lines[NR]=$0;} END {if(NR>0){print lines[1]>outfn;} for(i=2;i<=NR;++i) print lines[i] >> outfn;}' 

Lưu ý rằng, không giống như thực tế sponge, điều này có thể bị giới hạn ở kích thước RAM. sponge bộ đệm thực sự trong tệp tạm thời nếu cần.

Các vấn đề liên quan