xargs với chuyển hướng stdin / stdout


21

Tôi muốn chạy:

./a.out < x.dat > x.ans

cho mỗi * .dat tập tin trong thư mục Một .

Chắc chắn, nó có thể được thực hiện bằng bash / python / bất kỳ kịch bản nào, nhưng tôi thích viết một đoạn gợi cảm. Tất cả những gì tôi có thể đạt được là (vẫn không có bất kỳ thiết bị xuất chuẩn nào):

ls A/*.dat | xargs -I file -a file ./a.out

Nhưng -a trong xargs không hiểu thay thế 'tập tin'.

Cảm ơn sự giúp đỡ của bạn.


Ngoài các câu trả lời hay khác ở đây, bạn có thể sử dụng tùy chọn -a của GNU xargs.
James Youngman

Câu trả lời:


30

Trước hết, không sử dụng lsđầu ra như một danh sách tập tin . Sử dụng mở rộng vỏ hoặc find. Xem dưới đây để biết hậu quả tiềm năng của ls + xargs lạm dụng và ví dụ về hợp xargssử dụng.

1. Cách đơn giản: cho vòng lặp

Nếu bạn muốn xử lý chỉ các tệp bên dưới A/, thì một forvòng lặp đơn giản là đủ:

for file in A/*.dat; do ./a.out < "$file" > "${file%.dat}.ans"; done

2. pre1 Tại sao không   ls | xargs ?

Dưới đây là một ví dụ về cách điều xấu có thể bật nếu bạn sử dụng lsvới xargscho công việc. Hãy xem xét một kịch bản sau đây:

  • đầu tiên, hãy tạo một số tệp trống:

    $ touch A/mypreciousfile.dat\ with\ junk\ at\ the\ end.dat
    $ touch A/mypreciousfile.dat
    $ touch A/mypreciousfile.dat.ans
    
  • xem các tập tin và chúng không chứa gì:

    $ ls -1 A/
    mypreciousfile.dat
    mypreciousfile.dat with junk at the end.dat
    mypreciousfile.dat.ans
    
    $ cat A/*
    
  • chạy một lệnh ma thuật bằng cách sử dụng xargs:

    $ ls A/*.dat | xargs -I file sh -c "echo TRICKED > file.ans"
    
  • kết quả:

    $ cat A/mypreciousfile.dat
    TRICKED with junk at the end.dat.ans
    
    $ cat A/mypreciousfile.dat.ans
    TRICKED
    

Vì vậy, bạn chỉ cần ghi đè lên cả hai mypreciousfile.datmypreciousfile.dat.ans. Nếu có bất kỳ nội dung nào trong các tệp đó, nó sẽ bị xóa.


2. Sử dụng   xargs : cách thích hợp với  find 

Nếu bạn muốn nhấn mạnh vào việc sử dụng xargs, hãy sử dụng -0(tên kết thúc null):

find A/ -name "*.dat" -type f -print0 | xargs -0 -I file sh -c './a.out < "file" > "file.ans"'

Lưu ý hai điều:

  1. bằng cách này, bạn sẽ tạo các tệp có .dat.anskết thúc;
  2. điều này sẽ phá vỡ nếu một số tên tệp chứa dấu ngoặc kép ( ").

Cả hai vấn đề có thể được giải quyết bằng cách gọi shell khác nhau:

find A/ -name "*.dat" -type f -print0 | xargs -0 -L 1 bash -c './a.out < "$0" > "${0%dat}ans"'

3. Tất cả được thực hiện trong find ... -exec

 find A/ -name "*.dat" -type f -exec sh -c './a.out < "{}" > "{}.ans"' \;

Điều này, một lần nữa, tạo ra .dat.anscác tệp và sẽ phá vỡ nếu tên tệp chứa ". Để thực hiện điều đó, hãy sử dụng bashvà thay đổi cách nó được gọi:

 find A/ -name "*.dat" -type f -exec bash -c './a.out < "$0" > "${0%dat}ans"' {} \;

+1 để đề cập không phân tích đầu ra của ls.
rahmu

2
Tùy chọn 2 bị ngắt khi tên tệp chứa ".
thiton

2
Điểm rất tốt, cảm ơn! Tôi sẽ cập nhật tương ứng.
rozcietrzewiacz

Tôi chỉ muốn đề cập rằng, nếu zshđược sử dụng làm shell (và SH_WORDClickLIT không được đặt), tất cả các trường hợp đặc biệt khó chịu (khoảng trắng, "trong tên tệp, v.v.) không cần phải xem xét. for file in A/*.dat; do ./a.out < $file > ${file%.dat}.ans ; doneTất cả đều hoạt động trong mọi trường hợp.
jofel

-1 vì tôi đang cố gắng tìm ra cách thực hiện xargs với stdin và câu hỏi của tôi không liên quan gì đến các tệp hoặc find.
Mehrdad

2

Hãy thử làm một cái gì đó như thế này (cú pháp có thể thay đổi một chút tùy thuộc vào trình bao bạn sử dụng):

$ for i in $(find A/ -name \*.dat); do ./a.out < ${i} > ${i%.dat}.ans; done


Điều đó sẽ không làm việc. Nó sẽ cố gắng hoạt động trên những thứ như somefile.dat.datvà chuyển hướng tất cả đầu ra sang một tệp duy nhất.
rozcietrzewiacz

Bạn đúng. Tôi chỉnh sửa giải pháp để sửa nó.
rahmu

OK - Hầu như tốt :) Chỉ là những somefile.dat.ansthứ đầu ra sẽ trông không đẹp lắm.
rozcietrzewiacz

1
Đã chỉnh sửa! Tôi không biết về '%'. Nó hoạt động như một lá bùa, cảm ơn vì tiền boa.
rahmu

1
Thêm một cái -type filesẽ tốt (không thể < directory), và điều này làm cho tên tập tin-cổ tích khác thường buồn.
Mat

2

Đối với các mẫu đơn giản, vòng lặp for là phù hợp:

for file in A/*.dat; do
    ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

Đối với các trường hợp phức tạp hơn khi bạn cần một tiện ích khác để liệt kê các tệp (zsh hoặc bash 4 có các mẫu đủ mạnh mà bạn hiếm khi cần tìm, nhưng nếu bạn muốn ở trong shell POSIX hoặc sử dụng shell nhanh như dash, bạn sẽ cần tìm bất cứ thứ gì không tầm thường), trong khi đọc là thích hợp nhất:

find A -name '*.dat' -print | while IFS= read -r file; do
   ./a.out < "${file}" > "${file%.dat}.ans" # Never forget the QUOTES!
done

Điều này sẽ xử lý các khoảng trắng, bởi vì đọc là (theo mặc định) hướng theo dòng. Nó sẽ không xử lý các dòng mới và nó sẽ không xử lý các dấu gạch chéo ngược, bởi vì theo mặc định, nó diễn giải các chuỗi thoát (điều này thực sự cho phép bạn chuyển qua một dòng mới, nhưng không thể tạo định dạng đó). Nhiều shell có -0tùy chọn để đọc vì vậy trong những shell bạn có thể xử lý tất cả các ký tự, nhưng tiếc là nó không phải là POSIX.



1

Tôi nghĩ rằng bạn cần ít nhất một lệnh gọi shell trong xargs:

ls A/*.dat | xargs -I file sh -c "./a.out < file > file.ans"

Chỉnh sửa: Cần lưu ý rằng phương pháp này không hoạt động khi tên tệp chứa khoảng trắng. Không thể làm việc. Ngay cả khi bạn đã sử dụng find -0 và xargs -0 để làm cho xargs hiểu chính xác các khoảng trắng, lệnh gọi shell -c sẽ vặn vẹo chúng. Tuy nhiên, OP rõ ràng đã yêu cầu một giải pháp xargs và đây là giải pháp xargs tốt nhất mà tôi nghĩ ra. Nếu khoảng trắng trong tên tệp có thể là một vấn đề, hãy sử dụng find -exec hoặc vòng lặp shell.



@rozcietrzewiacz: Nói chung, chắc chắn, nhưng tôi giả sử ai đó đang cố gắng làm xargs voodoo biết điều này. Vì các tên tệp này trải qua một lần mở rộng vỏ khác, dù sao chúng cũng phải được cư xử tốt.
thiton

1
Bạn đã sai về việc mở rộng vỏ. lsxuất ra những thứ như không gian mà không thoát ra và đó là vấn đề.
rozcietrzewiacz

@rozcietrzewiacz: Vâng, tôi hiểu vấn đề đó. Bây giờ, giả sử bạn sẽ thoát đúng các khoảng trắng này và đưa chúng vào xargs: Chúng được thay thế trong chuỗi -c của sh, sh mã hóa chuỗi -c và mọi thứ bị phá vỡ.
thiton

Vâng. Bạn thấy bây giờ, phân tích cú pháp lslà không tốt. Nhưng xargs có thể được sử dụng một cách an toàn với find - xem gợi ý số 2 trong câu trả lời của tôi.
rozcietrzewiacz

1

Không cần phải phức tạp hóa nó. Bạn có thể làm điều đó với một forvòng lặp:

for file in A/*.dat; do
  ./a.out < "$file" >"${file%.dat}.ans"
done

các ${file%.dat}.ansbit sẽ loại bỏ các .dathậu tố tên tập tin từ tên tập tin trong $filevà thay vào đó thêm .ansđến cùng.

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.