Làm thế nào để vượt qua các tập tin được tìm thấy bằng cách tìm làm đối số?


9

Đầu tiên để cắt bỏ các câu trả lời tầm thường nhưng không thể áp dụng: Tôi không thể sử dụng cả mẹo find+ xargscũng như các biến thể của nó (như findvới -exec) vì tôi cần sử dụng một vài biểu thức như vậy cho mỗi cuộc gọi. Tôi sẽ trở lại điều này vào cuối.


Bây giờ để có một ví dụ tốt hơn, hãy xem xét:

$ find -L some/dir -name \*.abc | sort
some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

Làm thế nào để tôi vượt qua những điều đó như là đối số program?

Chỉ làm điều đó không phải là lừa

$ ./program $(find -L some/dir -name \*.abc | sort)

thất bại vì programnhận được các đối số sau:

[0]: ./program
[1]: some/dir/1.abc
[2]: some/dir/2.abc
[3]: some/dir/a
[4]: space.abc

Có thể thấy, đường dẫn với không gian đã được phân chia và programcoi đó là hai đối số khác nhau.

Trích dẫn cho đến khi nó hoạt động

Có vẻ như những người dùng mới làm quen như tôi, khi gặp phải những vấn đề như vậy, có xu hướng thêm các trích dẫn ngẫu nhiên cho đến khi cuối cùng nó hoạt động - chỉ ở đây, nó dường như không giúp ích gì cho

"$(…)"

$ ./program "$(find -L some/dir -name \*.abc | sort)"
[0]: ./program
[1]: some/dir/1.abc
some/dir/2.abc
some/dir/a space.abc

Vì các trích dẫn ngăn chia tách từ, tất cả các tệp được truyền dưới dạng một đối số.

Trích dẫn từng con đường

Một cách tiếp cận đầy hứa hẹn:

$ ./program $(find -L some/dir -name \*.abc -printf '"%p"\n' | sort)
[1]: "some/dir/1.abc"
[2]: "some/dir/2.abc"
[3]: "some/dir/a
[4]: space.abc"

Các trích dẫn là có, chắc chắn. Nhưng chúng không còn được giải thích. Họ chỉ là một phần của chuỗi. Vì vậy, không chỉ họ không ngăn chặn việc chia tách từ mà còn gây tranh cãi!

Thay đổi IFS

Sau đó, tôi đã thử chơi xung quanh với IFS. Tôi muốn find-print0sortvới -zanyway - để họ sẽ không có vấn đề trên "đường dây" bản thân. Vậy tại sao không buộc từ chia tách trên nullnhân vật và có tất cả?

$ ./program $(IFS=$'\0' find -L some/dir -name \*.abc -print0 | sort -z)
[0]: ./program
[1]: some/dir/1.abcsome/dir/2.abcsome/dir/a
[2]: space.abc

Vì vậy, nó vẫn phân chia trên không gian và không phân chia trên null.

Tôi đã cố gắng đặt IFSbài tập cả trong $(…)(như hình trên) và trước đó ./program. Ngoài ra tôi đã cố gắng cú pháp khác như \0, \x0, \x00cả hai được trích dẫn với '"cũng như có và không có $. Không ai trong số họ dường như tạo ra bất kỳ sự khác biệt nào


Và ở đây tôi hết ý tưởng. Tôi đã thử thêm vài thứ nhưng tất cả dường như gặp vấn đề tương tự như được liệt kê.

Tôi có thể làm gì khác? Có thể làm được gì không?

Chắc chắn, tôi có thể programchấp nhận các mẫu và tự tìm kiếm. Nhưng nó là rất nhiều công việc gấp đôi trong khi sửa nó thành một cú pháp cụ thể. (Điều gì về việc cung cấp các tệp bằng một grepví dụ?).

Ngoài ra tôi có thể programchấp nhận một tập tin với một danh sách các đường dẫn. Sau đó, tôi có thể dễ dàng kết xuất findbiểu thức vào một số tệp tạm thời và chỉ cung cấp đường dẫn đến tệp đó. Điều này có thể được hỗ trợ dọc theo các đường dẫn trực tiếp để nếu người dùng chỉ có một đường dẫn đơn giản thì nó có thể được cung cấp mà không cần tệp trung gian. Nhưng điều này có vẻ không hay - người ta cần tạo thêm tệp và chăm sóc chúng, chưa kể đến việc thực hiện thêm. (Tuy nhiên, về mặt tích cực, nó có thể là một giải cứu cho các trường hợp trong đó số lượng tệp làm đối số bắt đầu gây ra sự cố với độ dài dòng lệnh


Cuối cùng, hãy để tôi nhắc bạn một lần nữa rằng các thủ thuật find+ xargs(và tương tự) sẽ không hoạt động trong trường hợp của tôi. Để mô tả đơn giản, tôi chỉ hiển thị một đối số. Nhưng trường hợp thực sự của tôi trông giống như thế này:

$ ABC_FILES=$(find -L some/dir -name \*.abc | sort)
$ XYZ_FILES=$(find -L other/dir -name \*.xyz | sort)
$ ./program --abc-files $ABC_FILES --xyz-files $XYZ_FILES

Vì vậy, thực hiện một xargstìm kiếm từ một tìm kiếm vẫn còn cho tôi cách đối phó với một tìm kiếm khác

Câu trả lời:


13

Sử dụng mảng.

Nếu bạn không cần phải xử lý khả năng của dòng mới trong tên tệp của mình, thì bạn có thể thoát khỏi

mapfile -t ABC_FILES < <(find -L some/dir -name \*.abc | sort)
mapfile -t XYZ_FILES < <(find -L other/dir -name \*.xyz | sort)

sau đó

./program --abc-files "${ABC_FILES[@]}" --xyz-files "${XYZ_FILES[@]}"

Nếu bạn làm cần thiết phải xử lý dòng mới trong tên tập tin, và có bash> = 4.4, bạn có thể sử dụng -print0-d ''để null-chấm dứt những cái tên trong quá trình thi mảng:

mapfile -td '' ABC_FILES < <(find -L some/dir -name \*.abc -print0 | sort -z)

(và tương tự cho XYZ_FILES). Nếu bạn không có bash mới hơn, thì bạn có thể sử dụng vòng đọc kết thúc null để nối tên tệp vào mảng, ví dụ:

ABC_FILES=()
while IFS= read -rd '' f; do ABC_FILES+=( "$f" ); done < <(find -L some/dir -name \*.abc -print0 | sort -z)

Thông minh! Tôi đã suy nghĩ về mảng. Nhưng bằng cách nào đó tôi đã không tìm thấy bất cứ điều gì trên đó mapfile(hoặc từ đồng nghĩa của nó readarray). Nhưng nó làm việc!
Adam Badura

Tuy nhiên, bạn có thể cải thiện nó một chút. Phiên bản Bash <4.4 (mà tôi tình cờ có ...) với một whilevòng lặp không xóa mảng. Điều đó có nghĩa là nếu không tìm thấy tập tin thì mảng không được xác định. Trong khi nếu nó đã được xác định thì các tệp mới sẽ được nối thêm (thay vì thay thế các tệp cũ). Có vẻ như thêm declare -a ABC_FILES='()';trước khi whilelừa. (Trong khi chỉ thêm ABC_FILES='()';không.)
Adam Badura

Ngoài ra < <có nghĩa là gì ở đây? Có giống như <<vậy không? Tôi không nghĩ như vậy khi thay đổi nó để <<mang lại lỗi cú pháp ("mã thông báo không mong đợi` ('"). Vậy nó là gì và nó hoạt động như thế nào?
Adam Badura

Một cải tiến khác (cùng với cách sử dụng cụ thể của tôi) là xây dựng một mảng khác. Vì vậy, chúng tôi có những người ABC_FILES. Điều đó là tốt. Nhưng nó cũng hữu ích để làm cho ABS_ARGSđó là một mảng trống nếu ABC_FILEStrống hoặc nếu không nó là một mảng ('--abc-files' "${ABC_FILES[@]}"). Cách này sau này tôi có thể sử dụng nó như thế này: ./program "${ABC_ARGS[@]}" "${XYZ_ARGS[@]}"và chắc chắn rằng nó sẽ hoạt động chính xác bất kể nhóm nào (nếu có) là trống. Hoặc để nói khác đi: cách này --abc-files(và --xyz-files) sẽ chỉ được cung cấp nếu nó được theo sau bởi một số đường dẫn thực tế.
Adam Badura

1
@AdamBadura: while read ... done < <(find blah)là chuyển hướng shell bình thường <từ một tệp đặc biệt được tạo bởi CHẾ ĐỘ XỬ . Điều này khác với đường ống find blah | while read ... donevì đường ống chạy whilevòng lặp trong một lớp con nên var (s) được đặt trong nó không được giữ lại cho các lệnh tiếp theo.
dave_thndry_085

3

Bạn có thể sử dụng IFS = newline (giả sử không có tên tệp nào chứa dòng mới) nhưng bạn phải đặt nó ở lớp vỏ ngoài TRƯỚC KHI thay thế:

$ ls -1
a file with spaces
able
alpha
baker
boo hoo hoo
bravo
$ # note semicolon here; it's not enough to be in the environment passed
$ # to printf, it must be in the environment OF THE SHELL WHILE PARSING
$ IFS=$'\n'; printf '%s\n' --afiles $(find . -name 'a*') --bfiles $(find . -name 'b*')
--afiles
./able
./a file with spaces
./alpha
--bfiles
./bravo
./boo hoo hoo
./baker

Với zshnhưng không phải bashbạn cũng có thể sử dụng null $'\0'. Ngay cả trong bashbạn cũng có thể xử lý dòng mới nếu có một ký tự đủ lạ không bao giờ được sử dụng như

 IFS=$'\1'; ... $(find ... -print0 | tr '\0' '\1') ...

Tuy nhiên, cách tiếp cận này không xử lý yêu cầu bổ sung mà bạn đã đưa ra trong các nhận xét về câu trả lời của @ Steeldo để bỏ qua --afiles nếu tìm thấy trống.


Vì vậy, như tôi hiểu trong Bash, không có cách nào để buộc IFSphải tách ra null?
Adam Badura

@AdamBadura: Tôi khá chắc chắn là không; bash không cho phép null byte trong bất kỳ biến nào, kể cả IFS. Lưu ý rằng read -d ''phương thức được sử dụng trong các phương thức của Steeldo là một chuỗi rỗng không phải là một chuỗi chứa byte rỗng. (Và tùy chọn lệnh không phải là var như vậy.)
dave_thedom_085

Bạn cũng phải vô hiệu hóa globalbing ( set -o noglob) trước khi sử dụng toán tử split + global đó (ngoại trừ trong zsh).
Stéphane Chazelas


@AdamBadura Có, trong bash, một null hoàn toàn giống $'\0'và cũng như ''.
Isaac

1

Tôi không chắc tại sao bạn lại từ bỏ xargs.

Vì vậy, thực hiện một xargstìm kiếm từ một tìm kiếm vẫn còn cho tôi cách đối phó với một tìm kiếm khác

Chuỗi --xyz-fileschỉ là một trong nhiều đối số và không có lý do gì để xem xét nó đặc biệt trước khi nó được chương trình của bạn diễn giải. Tôi nghĩ bạn có thể vượt qua xargscả hai findkết quả:

{ find -L some/dir -name \*.abc -print0 | sort -z; echo -ne "--xyz-files\0"; find -L other/dir -name \*.xyz -print0 | sort -z; } | xargs -0 ./program --abc-files

Bạn đúng rồi! Điều này cũng hoạt động! Tuy nhiên lưu ý rằng bạn đã bỏ lỡ -print0trong thứ hai find. Ngoài ra nếu đi theo cách này tôi cũng sẽ đặt nó --abc-filesnhư một sự echonhất quán.
Adam Badura

Cách tiếp cận này có vẻ đơn giản hơn và có phần nhiều hơn so với cách tiếp cận mảng. Tuy nhiên, nó sẽ yêu cầu một số logic bổ sung để bao gồm trường hợp nếu không có .abctệp thì cũng không nên có --abc-files(cùng với .xyz). Các giải pháp mảng dựa trên bằng steeldriver cũng đòi hỏi thêm logic cho nó nhưng logic đó là tầm thường ở đó trong khi nó có thể là không quá tầm thường ở đây phá hủy Ưu điểm chính của giải pháp này - sự đơn giản.
Adam Badura

Ngoài ra tôi không thực sự chắc chắn nhưng tôi cho rằng xargssẽ không bao giờ cố gắng phân tách các đối số và thực hiện một số lệnh thay vì một lệnh, trừ khi nó được hướng dẫn rõ ràng để làm như vậy với các đối số -L, --max-lines( -l), --max-args( -n) hoặc --max-chars( -s). Tôi có đúng không Hoặc có một số mặc định? Vì chương trình của tôi sẽ không xử lý việc phân chia như vậy một cách chính xác và tôi thà không gọi nó là ...
Adam Badura

1
@AdamBadura Mất tích -print0- đã sửa, cảm ơn. Tôi không biết tất cả các câu trả lời nhưng tôi đồng ý giải pháp của tôi khiến việc đưa vào logic thêm khó khăn. Tôi có thể sẽ đi với mảng bản thân mình, bây giờ khi tôi biết phương pháp này. Câu trả lời của tôi không thực sự dành cho bạn. Bạn đã chấp nhận câu trả lời khác và tôi cho rằng vấn đề của bạn đã được giải quyết. Tôi chỉ muốn chỉ ra rằng bạn có thể chuyển các đối số từ nhiều nguồn thông qua xargs, điều này không rõ ràng ngay từ cái nhìn đầu tiên. Bạn có thể coi nó như một bằng chứng về khái niệm. Bây giờ tất cả chúng ta đều biết một vài cách tiếp cận khác nhau và chúng ta có thể có ý thức lựa chọn những gì phù hợp với chúng ta trong mọi trường hợp cụ thể.
Kamil Maciorowski

Vâng, tôi đã thực hiện các giải pháp dựa trên mảng và nó hoạt động như sự quyến rũ. Tôi đặc biệt tự hào về cách nó xử lý sạch sẽ với tính tùy chọn (nếu không có tệp thì không --abc-files). Nhưng bạn đã đúng - thật tốt khi biết những sự thay thế của bạn! Đặc biệt là tôi đã lầm tưởng rằng điều đó là không thể.
Adam Badura
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.