Làm cách nào để sử dụng find khi tên tệp chứa dấu cách?


17

Tôi muốn chuyển tên tập tin sang các chương trình khác, nhưng tất cả đều bị nghẹt thở khi tên chứa khoảng trắng.

Hãy nói rằng tôi có một tập tin được gọi.

foo bar

Làm thế nào tôi có findthể trả lại tên chính xác?

Rõ ràng tôi muốn:

foo\ bar

hoặc là:

"foo bar"

EDIT : Tôi không muốn thông qua xargs, tôi muốn lấy một chuỗi được định dạng chính xác ra findđể tôi có thể chuyển chuỗi tên tệp trực tiếp sang chương trình khác.


5
bạn đang làm gì với nó? Bạn có biết -execcờ với find? bạn có khả năng có thể giảm bớt lỗi này và làm cho lệnh của bạn hiệu quả hơn bằng cách thực hiện -execthay vì chuyển nó sang các lệnh khác. Chỉ cần $ 0,02
h3rrmiller của tôi

6
@ bọ: findđịnh dạng tên tập tin tốt; họ được viết một tên trên mỗi dòng. . .
rici

2
Những gì bạn gọi là "được định dạng đúng" thực sự là "thoát cho tiêu dùng bởi vỏ". Hầu hết các tiện ích có thể đọc một loạt các tên tệp sẽ bị nghẹt tên thoát vỏ, nhưng trên thực tế sẽ có ý nghĩa đối với (giả sử) findđể cung cấp tùy chọn xuất tên tệp theo định dạng phù hợp với trình bao. Tuy nhiên, nói chung, phần mở rộng -print0GNU cũng findhoạt động tốt đối với nhiều tình huống khác và bạn nên học cách sử dụng nó trong bất kỳ sự kiện nào.
tripleee

2
@ bọ: Nhân tiện, ls $(command...)không cung cấp danh sách thông qua stdin. Nó đặt đầu ra của $(command...)trực tiếp vào dòng lệnh. Trong trường hợp đó, nó là shell đang đọc từ c và nó sẽ sử dụng giá trị hiện tại của $IFSđể quyết định cách ghép từ đầu ra. Nói chung, bạn tốt hơn nên sử dụng xargs. Bạn sẽ không nhận thấy một hiệu suất hit.
rici

2
find -printf '"%p"\n'sẽ thêm dấu ngoặc kép xung quanh mỗi tên được tìm thấy, nhưng sẽ không trích dẫn chính xác bất kỳ dấu ngoặc kép nào trong tên tệp. Nếu tên tệp của bạn không có bất kỳ dấu ngoặc kép nhúng nào, bạn có thể bỏ qua vấn đề: hoặc chuyển qua sed 's/"/&&/g;s/^""/"/;s/""$/"/'. Nếu tên tệp của bạn cuối cùng được xử lý bởi shell, có lẽ bạn nên sử dụng dấu ngoặc đơn thay vì dấu ngoặc kép (nếu không sweet$HOMEsẽ trở thành một cái gì đó như sheet/home/you). Và điều này vẫn không mạnh mẽ đối với các tên tệp có dòng mới trong đó. Bạn muốn xử lý chúng như thế nào?
tripleee

Câu trả lời:


18

VỊ TRÍ:

find . -type f -exec sh -c '
  for f do
    : command "$f"
  done
' sh {} +

Với sự findhỗ trợ -print0xargshỗ trợ -0:

find . -type f -print0 | xargs -0 <command>

-0 tùy chọn yêu cầu xargs sử dụng ký tự ASCII NUL thay vì khoảng trắng để kết thúc (tách biệt) tên tệp.

Thí dụ:

find . -maxdepth 1 -type f -print0 | xargs -0 ls -l

Không hoạt động. Khi tôi chạy, ls $(find . -maxdepth 1 -type f -print0 | xargs -0)tôi nhận được ls: cannot access ./foo: No such file or directory ls: cannot access bar: No such file or directory
lỗi

1
Bạn đã thử nó theo cách mà Gnouc thực sự đã viết nó chưa? Nếu bạn khăng khăng làm theo cách của mình, hãy thử đặt $(..)trong dấu ngoặc kép"$(..)"
evilsoup

3
@ bọ: lệnh của bạn sai. Hãy thử chính xác tôi worte và đọc trang chủ của findxargs.
cuonglm

Tôi thấy, sau đó một lần nữa tôi muốn có được một chuỗi định dạng mà tôi có thể ống trực tiếp.
lỗi

1
@ bọ: Chỉ cần sử dụng xargs -0 <chương trình của bạn>
cuonglm

10

Sử dụng -print0là một tùy chọn, nhưng không phải tất cả các chương trình đều hỗ trợ sử dụng luồng dữ liệu được phân tách bằng nullbyte, vì vậy bạn sẽ phải sử dụng xargsvới -0tùy chọn cho một số điều, như câu trả lời của Gnouc đã lưu ý.

Một thay thế sẽ được sử dụng find's -exechoặc -execdirtùy chọn. Cái đầu tiên sau đây sẽ cung cấp tên tệp cho somecommandtừng cái một, trong khi cái thứ hai sẽ mở rộng thành một danh sách các tệp:

find . -type f -exec somecommand '{}' \;
find . -type f -exec somecommand '{}' +

Bạn có thể thấy rằng bạn tốt hơn khi sử dụng Globing trong nhiều trường hợp. Nếu bạn có trình bao hiện đại (bash 4+, zsh, ksh), bạn có thể nhận được đệ quy đệ quy với globstar( **). Trong bash, bạn phải đặt điều này:

shopt -s globstar
somecommand ./**/*.txt ## feeds all *.txt files to somecommand, recursively

Tôi có một dòng nói shopt -s globstar extglobtrong .bashrc của tôi, vì vậy điều này luôn được kích hoạt cho tôi (và vì vậy, các khoảng trống được mở rộng, cũng rất hữu ích).

Nếu bạn không muốn đệ quy, rõ ràng chỉ cần sử dụng ./*.txtthay thế, để sử dụng mọi * .txt trong thư mục làm việc. findcó một số khả năng tìm kiếm chi tiết rất hữu ích và bắt buộc đối với hàng chục nghìn tệp (tại thời điểm đó bạn sẽ chạy vào số lượng đối số tối đa của trình bao), nhưng đối với việc sử dụng hàng ngày thì thường không cần thiết.


Này @evilsoup {} làm gì trong tập lệnh này?
Ayusman

3

Cá nhân, tôi sẽ sử dụng -exechành động tìm để giải quyết loại vấn đề này. Hoặc, nếu cần, xargscho phép thực hiện song song.

Tuy nhiên, có một cách findđể tạo ra một danh sách tên tệp có thể đọc được. Không có gì đáng ngạc nhiên, nó sử dụng -execbashđặc biệt là một phần mở rộng cho printflệnh:

find ... -exec bash -c 'printf "%q " "$@"' printf {} ';'

Tuy nhiên, trong khi điều đó sẽ in ra các từ thoát vỏ chính xác, nó sẽ không thể sử dụng được $(...), bởi vì $(...)không diễn giải các trích dẫn hoặc thoát. (Resut of $(...)chịu sự phân tách từ và mở rộng tên đường dẫn, trừ khi được bao quanh bởi dấu ngoặc kép.) Vì vậy, những điều sau đây sẽ không làm những gì bạn muốn:

ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)

Những gì bạn sẽ phải làm là:

eval "ls $(find ... -exec bash -c 'printf "%q " "$@"' printf {} +)"

(Lưu ý rằng tôi đã không thực sự cố gắng để kiểm tra sự quái dị ở trên.)

Nhưng sau đó bạn cũng có thể làm:

find ... -exec ls {} +

Tôi không nghĩ lskịch bản nắm bắt đầy đủ trường hợp sử dụng của OP, nhưng đây chỉ là suy đoán, vì chúng tôi chưa thể hiện những gì anh ấy thực sự đang cố gắng thực hiện. Giải pháp này thực sự hoạt động rất độc đáo; Tôi nhận được đầu ra mà tôi (mơ hồ) mong đợi cho tất cả các tên tệp vui nhộn mà tôi đã thử, bao gồmtouch "$(tr a-z '\001-\026' <<<'the quick brown fox jumped over the lazy dogs')"
tripleee

@triplee: Tôi cũng không biết OP muốn làm gì. Lợi thế thực sự duy nhất của việc xây dựng chuỗi trích dẫn để chuyển đến evallà bạn chưa phải chuyển nó đến eval; bạn có thể lưu nó trong một tham số và sử dụng nó sau này, có thể nhiều lần với các lệnh khác nhau. Tuy nhiên, OP không đưa ra dấu hiệu nào cho thấy đó là trường hợp sử dụng (và nếu có, tốt hơn là đặt tên tệp vào một mảng, mặc dù điều đó cũng khó khăn.)
rici

0
find ./  | grep " "

sẽ giúp bạn có được các tập tin và thư mục chứa khoảng trắng

find ./ -type f  | grep " " 

sẽ giúp bạn có được các tập tin chứa khoảng trắng

find ./ -type d | grep " "

sẽ giúp bạn có các thư mục chứa khoảng trắng


-2
    find . -type f -name \*\  | sed -e 's/ /<thisisspace>/g'

Đây là một câu trả lời thú vị, nhưng nó không phải là một câu trả lời cho câu hỏi này.
Scott
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.