Danh sách đối số quá dài khi sao chép tệp


26

Tôi chỉ hỏi một câu hỏi liên quan đến cách tôi có thể đếm các tệp của phần mở rộng cụ thể. Bây giờ tôi muốn cpnhững tập tin này mới dir.

Tôi đang cố gắng,

cp *.prj ../prjshp/

cp * | grep '\.prj$' ../prjshp/

nhưng họ đang đưa ra cùng một lỗi,

bash: / bin / cp: Danh sách đối số quá dài

Làm thế nào để tôi sao chép chúng?


Câu trả lời:


36

cp *.prj ../prjshp/là lệnh đúng, nhưng bạn đã gặp phải trường hợp hiếm gặp khi nó chạy trong giới hạn kích thước. Lệnh thứ hai bạn đã thử không có ý nghĩa gì.

Một phương pháp là chạy cptrên các tệp trong khối. Các findlệnh biết làm thế nào để làm điều này:

find -maxdepth 1 -name '*.prj' -exec mv -t ../prjshp {} +
  • find đi qua thư mục hiện tại và các thư mục bên dưới nó đệ quy.
  • -maxdepth 1 có nghĩa là dừng lại ở độ sâu 1, tức là không tái diễn vào các thư mục con.
  • -name '*.prj'có nghĩa là chỉ hành động trên các tệp có tên khớp với mẫu đã chỉ định. Lưu ý các trích dẫn xung quanh mẫu: nó sẽ được diễn giải bằng findlệnh chứ không phải bằng vỏ.
  • -exec … {} +có nghĩa là thực thi lệnh được chỉ định cho tất cả các tệp. Nó gọi lệnh nhiều lần nếu cần thiết, chú ý không vượt quá giới hạn dòng lệnh.
  • mv -t ../prjshpdi chuyển các tập tin được chỉ định vào ../prjshp. Các -ttùy chọn được sử dụng ở đây vì một hạn chế của các findlệnh: các tập tin được tìm thấy (tượng trưng bằng {}) được thông qua như là đối số cuối cùng của lệnh, bạn không thể thêm các điểm đến sau khi nó.

Một phương pháp khác là sử dụng rsync.

rsync -r --include='*.prj' --exclude='*' . ../prjshp
  • rsync -r … . ../prjshpsao chép thư mục hiện tại vào ../prjshpđệ quy.
  • --include='*.prj' --exclude='*'có nghĩa là sao chép các tệp khớp *.prjvà loại trừ mọi thứ khác (bao gồm các thư mục con, vì vậy .prjcác tệp trong thư mục con sẽ không được tìm thấy).

3
rsync, cho đến nay là giải pháp dễ nhất ở đây.
ntk4

Nói một cách khó hiểu, lệnh thứ hai cp * | grep '\.prj$' ../prjshp/ không có ý nghĩa gì, nhưng có thể hợp lệ về mặt cú pháp, nếu *mở rộng danh sách các tệp với lệnh cuối cùng là một thư mục (aka cp SOURCE1 SOURCE2....DEST). Đường ống không có ý nghĩa gì, chắc chắn, nhưng vẫn có giá trị về mặt cú pháp liên quan đến vỏ - nó sẽ dup()mô tả tập tin tốt, chỉ là đầu đọc của ống sẽ không nhận được bất kỳ dữ liệu nào vì cpkhông viết bất kỳ dữ liệu nào .
Sergiy Kolodyazhnyy

Cả find và rsync đều tạo ra cùng một danh sách đối số lỗi quá dài đối với tôi. Vòng lặp for là cách giải quyết đơn giản nhất.
Meezaan-ud-Din

Thật vậy, rsync là cách để thực hiện bất kỳ việc sao chép hàng loạt nào, mặc dù tôi không biết chúng ta đã đi với Linux bao xa và chúng ta có một lỗ hổng / lỗi ngớ ngẩn như thế này và vâng tôi sẽ coi đó là một lỗ hổng / lỗi.
Mitchell

22

Lệnh này sao chép từng tệp một và sẽ hoạt động ngay cả khi có quá nhiều trong số chúng *để mở rộng thành một cplệnh duy nhất :

for i in *; do cp "$i" ../prjshp/; done

Điều này làm việc cho tôi.
1rq3fea324wre

1
Đơn giản và hiệu quả. Tôi gặp vấn đề tương tự khi xóa ~ 1/4 triệu jpeg tôi đã trích xuất từ ​​video cho một dự án. Đây là cách tiếp cận tôi đã sử dụng.
Anh Cả Geek

5

Có 3 điểm chính cần lưu ý khi gặp Argument list too longlỗi:

  • Độ dài của các đối số dòng lệnh bị giới hạn bởi ARG_MAXbiến, theo định nghĩa POSIX là "... [m] độ dài tối đa của đối số đối với các hàm exec bao gồm dữ liệu môi trường" (nhấn mạnh thêm) ". Đó là khi shell thực thi lệnh không -built-it lệnh, nó phải gọi một trong số đó exec()để sinh ra quá trình của lệnh đó và đó là nơi ARG_MAXbắt đầu hoạt động. Ngoài ra, tên hoặc đường dẫn đến chính lệnh (ví dụ, /bin/echo) đóng vai trò.

  • Các lệnh dựng sẵn của Shell được thực thi bằng shell, có nghĩa là shell không sử dụng exec()họ các hàm và do đó không bị ảnh hưởng bởi ARG_MAXbiến.

  • Một số lệnh nhất định, chẳng hạn như xargsfindnhận thức được ARG_MAXbiến và liên tục thực hiện các hành động theo giới hạn đó

Từ những điểm trên và như thể hiện trong câu trả lời xuất sắc của Kusalananda về câu hỏi liên quan, điều Argument list too longnày cũng có thể xảy ra khi môi trường lớn. Vì vậy, xem xét rằng mỗi môi trường của người dùng có thể khác nhau và kích thước đối số tính theo byte có liên quan, thật khó để đưa ra một số tệp / đối số duy nhất.

Làm thế nào để xử lý lỗi như vậy?

Điều quan trọng là không tập trung vào số lượng tệp, mà tập trung vào việc lệnh bạn sẽ sử dụng có liên quan đến exec()họ chức năng và tiếp tuyến - không gian ngăn xếp.

Sử dụng vỏ tích hợp

Như đã thảo luận trước đây, các vỏ tích hợp có khả năng miễn dịch ARG_MAXgiới hạn, đó là những thứ như forvòng lặp, whilevòng lặp, tích hợp echovà tích hợp printf- tất cả những thứ đó sẽ hoạt động đủ tốt.

for i in /path/to/dir/*; do cp "$i" /path/to/other/dir/; done

Về câu hỏi liên quan về việc xóa các tập tin, có một giải pháp như sau:

printf '%s\0' *.jpg | xargs -0 rm --

Lưu ý rằng điều này sử dụng tích hợp sẵn của shell printf. Nếu chúng ta gọi bên ngoài printf, điều đó sẽ liên quan exec(), do đó sẽ thất bại với số lượng lớn đối số:

$ /usr/bin/printf "%s\0" {1..7000000}> /dev/null
bash: /usr/bin/printf: Argument list too long

mảng bash

Theo câu trả lời của jlliagre, bashkhông áp đặt các giới hạn cho các mảng, do đó, việc xây dựng các tên tệp và sử dụng các lát trên mỗi vòng lặp cũng có thể được thực hiện, như thể hiện trong câu trả lời của danjpreron :

files=( /path/to/old_dir/*.prj )
for((I=0;I<${#files[*]};I+=1000)); do 
    cp -t /path/to/new_dir/ "${files[@]:I:1000}" 
done

Điều này, tuy nhiên, có giới hạn là bash cụ thể và không POSIX.

Tăng không gian ngăn xếp

Đôi khi bạn có thể thấy mọi người đề nghị tăng không gian ngăn xếp với ulimit -s <NUM>; trên Linux ARG_MAX giá trị là 1/4 không gian ngăn xếp cho mỗi chương trình, có nghĩa là tăng không gian ngăn xếp tăng tỷ lệ không gian cho các đối số.

# getconf reports value in bytes, ulimit -s in kilobytes
$ getconf ARG_MAX
2097152
$ echo $((  $(getconf ARG_MAX)*4 ))
8388608
$ printf "%dK\n" $(ulimit -s) | numfmt --from=iec --to=none
8388608
# Increasing stack space results in increated ARG_MAX value
$ ulimit -s 16384
$ getconf ARG_MAX
4194304

Theo câu trả lời của Franck Dernoncourt , trích dẫn Tạp chí Linux, người ta cũng có thể biên dịch lại nhân Linux với giá trị lớn hơn cho các trang bộ nhớ tối đa cho các đối số, tuy nhiên, đó là công việc nhiều hơn cần thiết và mở ra tiềm năng khai thác như đã nêu trong bài báo Tạp chí Linux được trích dẫn.

Tránh vỏ

Một cách khác, là sử dụng pythonhoặc python3đi kèm theo mặc định với Ubuntu. Ví dụ python + here-doc bên dưới, là thứ mà cá nhân tôi đã sử dụng để sao chép một thư mục lớn các tệp ở đâu đó trong phạm vi 40.000 mục:

$ python <<EOF
> import shutil
> import os
> for f in os.listdir('.'):
>    if os.path.isfile(f):
>         shutil.copy(f,'./newdir/')
> EOF

Đối với truy cập đệ quy, bạn có thể sử dụng os.walk .

Xem thêm:


2

IMHO, các công cụ tối ưu để xử lý các tập tin là findxargs. Xem man find. Xem man xargs. find, với công -print0tắc của nó , tạo ra một NULdanh sách tên tệp riêng biệt (tên tệp có thể chứa bất kỳ ký tự nào được thực thi NULhoặc /) xargshiểu, sử dụng công -0tắc. xargssau đó xây dựng lệnh dài nhất được phép (tên tệp nhiều nhất, không có nửa tên tệp ở cuối) và thực thi nó. xargslặp lại điều này cho đến khi findnguồn cung cấp không có tên tập tin. Chạy xargs --show-limits </dev/nullđể xem giới hạn.

Để giải quyết vấn đề của bạn, (và sau khi kiểm tra man cpđể tìm --target-directory=):

find . -maxdepth 1 -type f -name '*.prj' -print0 | xargs -0 cp --target-directory=../prjshp/
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.