2013-02-22 21 views
11

Tôi có một tập lệnh chỉ ghi dữ liệu vào stdout. Tôi cần phải chạy nó cho nhiều tập tin và tạo ra một tập tin đầu ra khác nhau cho mỗi tập tin đầu vào và tôi đã tự hỏi làm thế nào để sử dụng find -exec cho điều đó. Vì vậy, tôi về cơ bản đã thử một số biến thể của này (tôi đã thay thế các script by cat chỉ dành riêng cho mục đích testability):Chuyển hướng stdout bằng find -exec và không tạo shell mới

tìm * -type f -exec mèo "{}"> "{} .stdout" \;

nhưng không thể hoạt động vì tất cả dữ liệu đã được ghi vào tệp có tên là {}.stdout.

Cuối cùng, tôi có thể làm cho nó làm việc với:

find * -type f -exec sh -c "mèo {}> {} .stdout" \;

Nhưng trong khi hình thức mới nhất này hoạt động tốt với cat, kịch bản của tôi đòi hỏi phải có các biến môi trường được nạp thông qua một số kịch bản khởi tạo, vì vậy tôi kết thúc với:

find * -type f -exec sh -c " initscript1; initscript2; ...; myscript {}> {} .stdout "\;

Có vẻ như lãng phí vì tôi đã khởi tạo mọi thứ trong trình bao hiện tại của mình.

Có cách nào tốt hơn để thực hiện việc này với find không? Một lớp lót khác được chào đón.

+2

Nếu chúng được khởi tạo trong vỏ ban đầu của bạn, nhưng không được đặt trong vỏ con, thì chúng không phải là biến môi trường. Viết 'set -a' ở đầu các bản inits của bạn. –

+0

Là ví dụ cuối cùng bạn đưa ra đúng, hoặc là lệnh: 'find. -type f -exec sh -c ". initscript1;. initscript2; ...; myscript {}> {} .stdout" \; '(Thay vì chỉ đơn giản gọi' initscript1', bạn đang thực sự gọi '. Initscript1', nghĩa là bạn đang tìm nguồn cung ứng tệp với lệnh dấu chấm). –

Trả lời

5

Một giải pháp đơn giản có thể đặt một wrapper xung quanh kịch bản của bạn:

#!/bin/sh 

myscript "$1" > "$1.stdout" 

Gọi nó myscript2 và gọi nó với tìm:

find . -type f -exec myscript2 {} \; 

Lưu ý rằng mặc dù hầu hết các trường của tìm cho phép bạn làm những gì bạn đã làm, về mặt kỹ thuật, hành vi tìm kiếm không được chỉ định nếu bạn sử dụng {} nhiều hơn một lần trong danh sách đối số của -exec.

+2

Nhưng trong 'find' manual, ở đâu đó trong' -exec', người ta nói rằng: _The string '{}' được thay thế bởi tên tệp hiện tại đang được xử lý ở mọi nơi nó xuất hiện trong các đối số cho lệnh, không chỉ trong các đối số là một mình, như trong một số phiên bản của find._ [link] (http://unixhelp.ed.ac.uk/CGI/man-cgi?find). Tuy nhiên, cảm ơn cho workaround. – jserras

+3

Hướng dẫn để bạn thực hiện cụ thể trạng thái 'find' mà nó hoạt động, nhưng tiêu chuẩn đọc:' Nếu có nhiều hơn một đối số chỉ chứa hai ký tự "{}" thì hành vi không xác định. , nhưng là cái gì đó có thể đốt cháy bạn (lúc đó nó đột nhiên trở thành một vấn đề lớn!) –

+3

Một bất lợi quan trọng hơn là những thứ như '-exec sh -c" myscript {}> {} .stdout "\;' có thể gây ra thực thi mã tùy ý khi đối mặt với tên tệp thù địch. Đó là an toàn hơn để làm '-exec sh -c 'myscript" $ 1 ">" $ 1.stdout "' sh {} \;'. – jilles

2

Bạn có thể làm điều đó với eval. Nó có thể xấu xí, nhưng vì vậy phải tạo ra một kịch bản shell cho việc này. Ngoài ra, tất cả đều trên một dòng. Ví dụ

find -type f -exec bash -c "eval md5sum {} > {}.sum " \; 
+0

'bash -c' là thịt bò ở đây, 'eval' không thực sự làm bất cứ điều gì hữu ích. Nhưng bạn không tránh vỏ. – tripleee

+0

Nếu bạn lấy ra 'eval' tôi nghĩ đây là câu trả lời được chấp nhận thực sự, mặc dù OP sẽ lilke để tránh một shell. (Đặt một kịch bản trong một tập tin riêng biệt tạo ra một vỏ khi chạy tập lệnh đó anyway. Điều gì OP được yêu cầu là không thực sự có thể.) – tripleee

+0

'eval' đang tích cực nguy hiểm ở đây. Nếu bạn có một tên tập tin có chứa '$ (rm -rf $ HOME)', điều này sẽ là ** rất ** tin xấu. –

2

Nếu bạn xuất khẩu biến môi trường của bạn, họ sẽ đã có mặt trong vỏ con (Nếu bạn sử dụng bash -c thay vì sh -c, và vỏ cha mẹ của bạn là chính nó bash, sau đó bạn cũng có thể xuất khẩu chức năng trong vỏ mẹ và để chúng có thể sử dụng được ở trẻ; xem export -f).

Hơn nữa, bằng cách sử dụng -exec ... {} +, bạn có thể hạn chế số lượng vỏ với số nhỏ nhất có thể cần thiết để vượt qua tất cả các đối trên dòng lệnh:

set -a # turn on automatic export of all variables 
source initscript1 
source initscript2 

# pass as many filenames as possible to each sh -c, iterating over them directly 
find * -name '*.stdout' -prune -o -type f \ 
    -exec sh -c 'for arg; do myscript "$arg" > "${arg}.stdout"' _ {} + 

Cách khác, bạn chỉ có thể thực hiện việc thực hiện trong hiện tại của bạn shell trực tiếp:

while IFS= read -r -d '' filename; do 
    myscript "$filename" >"${filename}.out" 
done < <(find * -name '*.stdout' -prune -o -type f -print0) 

Xem UsingFind thảo luận một cách an toàn và thực hiện một cách chính xác hành động hàng loạt qua find; và BashFAQ #24 thảo luận về việc sử dụng thay thế quy trình (cú pháp <(...)) để đảm bảo rằng các thao tác được thực hiện trong trình bao gốc.

+0

Sử dụng '_' là $ 0 đối với lệnh được gọi là một chút gây xáo trộn! –

+0

@WilliamPursell, đó là thành ngữ phổ biến - có thể tìm thấy các liên kết nếu bạn muốn. ('_' cũng là một giá trị không được sử dụng/giữ chỗ thông thường trong một số ngôn ngữ khác, chẳng hạn như Python, nhưng sự hiểu biết của tôi là nó phổ biến trong shell trước). –

+0

Tôi đã nhìn thấy nó được sử dụng trong đi và perl, nhưng không bao giờ trong cài đặt này. Tôi có xu hướng bỏ qua nó và thiết lập $ 0 đến {}, mà có lẽ là một thực tế tồi tệ hơn nhiều! –

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