Danh sách đối số quá dài cho ls


48

Tôi gặp lỗi sau khi thử ls *.txt | wc -lthư mục chứa nhiều tệp:

-bash: /bin/ls: Argument list too long

Có ngưỡng của "Danh sách đối số" này phụ thuộc vào thông số kỹ thuật của distro hoặc máy tính không? Thông thường, tôi sẽ chuyển kết quả của kết quả lớn như vậy sang một số lệnh khác ( wc -lví dụ), vì vậy tôi không quan tâm đến giới hạn của thiết bị đầu cuối.


6
Điều đó được tính là đầu ra của phân tích cú phápls , đó là một ý tưởng tồi, vì vậy tốt hơn nên tránh nó. Để đếm, xem cách tốt nhất để đếm số lượng tệp trong một thư mục là gì? , đối với một cách giải quyết khó khăn, hãy xem tại sao vòng lặp không nâng cao đối số của Google quá lâu? .
manatwork

@manatwork Vâng, tôi cũng thấy những câu hỏi đó. Chỉ cần tự hỏi một cách tốt hơn để sử dụng hoặc chuyển hướng một đầu ra dài từ một lệnh theo cách tổng quát hơn.

bạn có thể sử dụng getconf ARG_MAX để đạt giới hạn trên hầu hết các hệ thống dựa trên unix
Prasanth

Câu trả lời:


49

Danh sách đối số thông báo lỗi của bạn quá dài xuất phát từ * của ls *.txt.

Giới hạn này là an toàn cho cả chương trình nhị phân và Kernel của bạn. Bạn sẽ thấy trên trang này thêm thông tin về nó, và cách nó được sử dụng và tính toán.

Không có giới hạn như vậy về kích thước ống. Vì vậy, bạn có thể chỉ cần ban hành lệnh này:

find -type f -name '*.txt'  | wc -l

Lưu ý: Trên Linux hiện đại, các ký tự lạ trong tên tệp (như dòng mới) sẽ được thoát bằng các công cụ như lshoặc find, nhưng vẫn được hiển thị từ * . Nếu bạn đang dùng Unix cũ, bạn sẽ cần lệnh này

find -type f -name '*.txt' -exec echo \;  | wc -l

NB2: Tôi đã tự hỏi làm thế nào một người có thể tạo một tập tin với một dòng mới trong tên của nó. Điều đó không khó lắm, một khi bạn biết mẹo:

touch "hello
world"

1
Tôi đã sửa đổi nó một chút để hoạt động trong trường hợp khi có tên tệp với dòng mới trong đó. Bạn cũng có thể muốn thêm một -maxdepth 1nếu bạn không có ý định đếm tệp trong thư mục con.
Shawn J. Goff

Bạn không cần -exec echo \;.
Mikel

@ ShawnJ.Goff Tôi đã thử nó. Không cần `echo` trong phiên bản hiện tại của GNU find
Coren

@Coren @Mikel - không phải ai cũng có GNU find. Trên findOS X và trên các hệ thống dựa trên busybox, và tôi đoán bất kỳ hệ thống dựa trên BSD nào cũng hiển thị tên tệp có dòng mới trong đó, điều này sẽ gây rối với số đếm.
Shawn J. Goff

Huh? wc -lđang đếm dòng mới. Vì vậy, chúng tôi muốn nó có dòng mới.
Mikel

11

Nó phụ thuộc chủ yếu vào phiên bản kernel Linux của bạn.

Bạn sẽ có thể thấy giới hạn cho hệ thống của mình bằng cách chạy

getconf ARG_MAX

cho bạn biết số byte tối đa mà một dòng lệnh có thể có sau khi được shell mở rộng.

Trong Linux <2.6,23, giới hạn thường là 128 KB.

Trong Linux> = 2.6.25, giới hạn là 128 KB hoặc 1/4 kích thước ngăn xếp của bạn (xem ulimit -s), tùy theo giá trị nào lớn hơn.

Xem trang man execve (2) để biết tất cả các chi tiết.


Thật không may, đường ống ls *.txtsẽ không khắc phục được sự cố, vì giới hạn nằm trong hệ điều hành chứ không phải vỏ.

Vỏ mở rộng *.txt, sau đó cố gắng gọi

exec("ls", "a.txt", "b.txt", ...)

và bạn có rất nhiều tệp khớp với nhau *.txtmà bạn vượt quá giới hạn 128 KB.

Bạn sẽ phải làm một cái gì đó như

find . -maxdepth 1 -name "*.txt" | wc -l

thay thế.

(Và xem ý kiến ​​của Shawn J. Goff bên dưới về tên tệp có chứa dòng mới.)


Xin lỗi vì không thể đưa ra một câu trả lời. Cần thêm danh tiếng. :( Cảm ơn tất cả các bạn !!

Bạn có thể giải thích những gì .-maxdepth 1ý nghĩa trong dòng cuối cùng? Cảm ơn! : D
Guilherme Salomé

2
@ GuilhermeSalomé .có nghĩa là thư mục hiện tại, -maxdepth 1có nghĩa là nó không tìm trong thư mục con. Điều này được dự định để phù hợp với các tập tin tương tự như *.txt.
Mikel

9

Một cách giải quyết khác:

ls | grep -c '\.txt$'

Mặc dù lstạo ra nhiều đầu ra hơn ls *.txtsản xuất (hoặc cố gắng sản xuất), nó không gặp phải vấn đề "đối số quá dài", bởi vì bạn không chuyển bất kỳ đối số nào cho ls. Lưu ý rằng grepcó một biểu thức chính quy thay vì một mẫu phù hợp với tệp.

Bạn có thể muốn sử dụng:

ls -U | grep -c '\.txt$'

(giả sử phiên bản lshỗ trợ của bạn tùy chọn này). Điều này nói lskhông sắp xếp đầu ra của nó, điều này có thể tiết kiệm cả thời gian và bộ nhớ - và trong trường hợp này, thứ tự không thành vấn đề, vì bạn chỉ đang đếm các tệp. Các tài nguyên dành cho việc sắp xếp đầu ra thường không đáng kể, nhưng trong trường hợp này chúng tôi đã biết bạn có số lượng *.txttệp rất lớn .

Và bạn nên xem xét sắp xếp lại các tệp của mình để bạn không có quá nhiều trong một thư mục. Điều này có thể hoặc không thể khả thi.


1

MAX_ARG_PAGES dường như là một tham số kernel. Sử dụng findxargslà một sự kết hợp điển hình để giải quyết giới hạn này nhưng tôi không chắc nó sẽ hoạt động wc.

Đường ống đầu ra của find . -name \*\.txtmột tệp và đếm các dòng trong tệp đó sẽ đóng vai trò là một cách giải quyết.


Bạn có thể làm bất cứ điều gì với lsđầu ra của, sẽ không giải quyết điều này. Miễn là ký tự đại diện * .txt được mở rộng vượt quá giới hạn, sẽ thất bại trước cả khi bắt đầu lsvà tạo bất kỳ đầu ra nào.
manatwork

Đúng, tôi đã cập nhật câu trả lời của mình.
Bram

Tốt hơn. Nhưng để làm cho nó thay thế cho lsbạn nên chỉ định -maxdepth 1để tránh quét đệ quy các thư mục con.
manatwork

Xin lỗi vì không thể đưa ra một câu trả lời. Cần thêm danh tiếng. :(

0

Điều này có thể bẩn nhưng nó hoạt động cho nhu cầu của tôi và trong khả năng của tôi. Tôi không nghĩ rằng nó thực hiện rất nhanh nhưng nó cho phép tôi tiếp tục với ngày của mình.

ls | grep jpg | <something>

Tôi đã nhận được một danh sách dài 90.000 jpg và chuyển chúng đến avconv để tạo ra một timelapse.

Trước đây tôi đã sử dụng ls * .jpg | avconv trước khi tôi gặp vấn đề này.

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.