Làm thế nào để có được các tập tin N cuối cùng trong một thư mục?


15

Tôi có nhiều tệp được sắp xếp theo tên tệp trong một thư mục. Tôi muốn sao chép các tệp N (giả sử, N = 4) cuối cùng vào thư mục chính của tôi. Tôi nên làm thế nào?

cp ./<the final 4 files> ~/


1
Trên tất cả các câu trả lời bên dưới, bạn có thể cần thêm "LC_ALL = ..." trước các lệnh bằng cách sử dụng phạm vi, để phạm vi của chúng sử dụng ngôn ngữ phù hợp cho bạn (địa phương có thể thay đổi và thứ tự các ký tự có thể thay đổi Rất nhiều. Ví dụ, ở một số địa phương, [az] có thể chứa tất cả các chữ cái trong bảng chữ cái ngoại trừ "Z", vì các chữ cái được sắp xếp: aAbBcC .... zZ. Các biến thể khác tồn tại (các chữ cái có dấu ). Một lựa chọn thông thường là:LC_ALL=C
Olivier Dulac

Câu trả lời:


23

Điều này có thể dễ dàng thực hiện với mảng bash / ksh93 / zsh:

a=(*)
cp -- "${a[@]: -4}" ~/

Điều này hoạt động cho tất cả các tên tệp không bị ẩn ngay cả khi chúng chứa khoảng trắng, tab, dòng mới hoặc các ký tự khó khác (giả sử có ít nhất 4 tệp không bị ẩn trong thư mục hiện tại với bash).

Làm thế nào nó hoạt động

  • a=(*)

    Điều này tạo ra một mảng avới tất cả các tên tệp. Tên tệp được trả về bởi bash được sắp xếp theo thứ tự abc. (Tôi cho rằng đây là ý của bạn khi "sắp xếp theo tên tệp." )

  • ${a[@]: -4}

    Điều này trả về bốn phần tử cuối cùng của mảng a(với điều kiện mảng chứa ít nhất 4 phần tử với bash).

  • cp -- "${a[@]: -4}" ~/

    Điều này sao chép bốn tên tập tin cuối cùng vào thư mục nhà của bạn.

Sao chép và đổi tên cùng một lúc

Điều này sẽ chỉ sao chép bốn tệp cuối cùng vào thư mục chính và đồng thời, thêm chuỗi a_vào tên của tệp đã sao chép:

a=(*)
for fname in "${a[@]: -4}"; do cp -- "$fname" ~/a_"$fname"; done

Sao chép từ một thư mục khác và cũng đổi tên

Nếu chúng ta sử dụng a=(./some_dir/*)thay vì a=(*), thì chúng ta có vấn đề về thư mục được gắn vào tên tệp. Một giải pháp là:

a=(./some_dir/*) 
for f in "${a[@]: -4}"; do cp "$f"  ~/a_"${f##*/}"; done

Một giải pháp khác là sử dụng một subshell và cdvào thư mục trong subshell:

(cd ./some_dir && a=(*) && for f in "${a[@]: -4}"; do cp -- "$f" ~/a_"$f"; done) 

Khi subshell hoàn thành, shell sẽ đưa chúng ta trở lại thư mục gốc.

Đảm bảo rằng thứ tự phù hợp

Câu hỏi yêu cầu các tệp "được sắp xếp theo tên tệp". Thứ tự đó, Olivier Dulac chỉ ra trong các bình luận, sẽ thay đổi từ địa phương này sang địa phương khác. Nếu điều quan trọng là có kết quả cố định độc lập với cài đặt máy, thì tốt nhất là chỉ định rõ ràng miền địa phương khi mảng ađược xác định. Ví dụ:

LC_ALL=C a=(*)

Bạn có thể tìm ra địa điểm mà bạn hiện đang ở bằng cách chạy localelệnh.


9

Nếu bạn đang sử dụng, zshbạn có thể gửi kèm trong ngoặc đơn ()một danh sách cái gọi là vòng loại toàn cầu để chọn các tệp mong muốn. Trong trường hợp của bạn, đó sẽ là

cp *(On[1,4]) ~/

Ở đây Onsắp xếp tên tệp theo thứ tự abc theo thứ tự ngược lại và [1,4]chỉ mất 4 trong số chúng.

Bạn có thể làm cho điều này mạnh mẽ hơn bằng cách chỉ chọn các tệp đơn giản (không bao gồm các thư mục, đường ống, v.v.) .và cũng bằng cách thêm --vào cplệnh để xử lý các tệp có tên bắt đầu -đúng, vì vậy:

cp -- *(.On[1,4]) ~

Thêm Dvòng loại nếu bạn cũng muốn xem xét các tệp ẩn (tệp chấm):

cp -- *(D.On[1,4]) ~

2
Hoặc : *([-4,-1]).
Stéphane Chazelas

2
@ StéphaneChazelas có, cho phép các độc giả tương lai sắp xếp theo thứ tự bảng chữ cái theo hướng thông thường ( on) là hành vi mặc định, do đó không cần chỉ định điều này và -trước các số đếm tên tệp từ phía dưới.
jimmij

7

Đây là một giải pháp sử dụng các lệnh bash cực kỳ đơn giản.

find . -maxdepth 1 -type f | sort | tail -n 4 | while read -r file; do cp "$file" ~/; done

Giải trình:

find . -maxdepth 1 -type f

Tìm tất cả các tập tin trong thư mục hiện tại.

sort

Sắp xếp theo thứ tự abc.

tail -n 4

Chỉ hiển thị 4 dòng cuối cùng.

while read -r file; do cp "$file" ~/; done

Vòng lặp trên mỗi dòng thực hiện lệnh sao chép.


6

Vì vậy, miễn là bạn tìm thấy loại vỏ dễ chịu, bạn có thể làm:

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
cp "$@" /path/to/target/dir

Điều này rất giống với bashgiải pháp mảng cụ thể được cung cấp, nhưng phải có thể mang theo mọi vỏ tương thích POSIX. Một số lưu ý về cả hai phương pháp:

  1. Điều quan trọng là bạn dẫn dắt các cpđối số của mình w / cp --hoặc bạn nhận được một trong hai .dấu chấm hoặc /ở đầu mỗi tên. Nếu bạn không làm điều này, bạn có nguy cơ một -dấu gạch đầu dòng trong cpđối số đầu tiên có thể được hiểu là một tùy chọn và khiến thao tác không thành công hoặc làm cho kết quả không mong muốn.

    • Ngay cả khi làm việc với thư mục hiện tại là thư mục nguồn, điều này vẫn dễ dàng được thực hiện như ... set -- ./*hoặc array=(./*).
  2. Điều quan trọng khi sử dụng cả hai phương pháp để đảm bảo bạn có ít nhất nhiều mục trong mảng arg khi bạn cố gắng loại bỏ - Tôi làm điều đó ở đây với một mở rộng toán học. Điều này shiftchỉ xảy ra nếu có ít nhất 4 mục trong mảng arg - và sau đó chỉ loại bỏ những đối số đầu tiên tạo ra thặng dư 4 ..

    • Vì vậy, trong một set 1 2 3 4 5trường hợp, nó sẽ thay đổi 1đi, nhưng trong một set 1 2 3trường hợp, nó sẽ không thay đổi gì.
    • Ví dụ: a=(1 2 3); echo "${a[@]: -4}"in một dòng trống.

Nếu bạn đang sao chép từ thư mục này sang thư mục khác, bạn có thể sử dụng pax:

set -- /path/to/source/dir/*
[ "$#" -le 4 ] || shift "$(($#-4))"
pax -rws '|.*/|new_prefix|' "$@" /path/to/target/dir

... sẽ áp dụng sedthay thế kiểu cho tất cả tên tệp khi nó sao chép chúng.


4

Nếu chỉ có các tệp và tên của chúng không chứa khoảng trắng hoặc dòng mới (và bạn chưa sửa đổi $IFS) hoặc ký tự toàn cầu (hoặc một số ký tự không in được với một số triển khai ls) và không bắt đầu ., thì bạn có thể làm điều này :

cp -- $(ls | tail -n 4) ~/

Điều này được gọi là thay thế lệnh và nó thực sự tiện dụng. tldp.org/LDP/abs/html/commandsub.html#CSPARENS
iyrin

Cảm ơn! Nó hoạt động. Có cách nào để nhanh chóng thêm tiền tố a_vào TẤT CẢ bốn tệp không? Tôi đã thử echo "a_$(ls | tail -n 4)", nhưng nó chỉ chuẩn bị các tập tin đầu tiên.
Đánh bạc Sibbs

@SibbsGambled Cách tiếp cận của tôi không phù hợp với điều đó nhưng câu trả lời của John1024 có thể dễ dàng thích nghi với điều đó.
Hauke ​​Laging

5
Nói chung, lsđầu ra phân tích cú pháp được coi là hình thức xấu vì nó thường thất bại khủng khiếp đối với các tên tệp không chỉ chứa khoảng trắng, mà còn các tab, dòng mới hoặc các ký tự hợp lệ nhưng "khó" khác.
HBruijn

2
@HaukeLaging Ngay cả khi hiện tại nó không phải là vấn đề (vì tôi không có tên lửa "phức tạp" trong thư mục của mình), tôi có thể có 2 năm và đột nhiên mọi thứ rơi vào chân tôi ...
glglgl

1

Đây là một giải pháp bash đơn giản mà không cần bất kỳ mã vòng lặp. Sao chép 4 tập tin cuối cùng từ thư mục hiện tại đến đích:

$ find . -maxdepth 1 -type f |tail -n -4|xargs cp -t "$destdir"

1
Làm thế nào để bạn đảm bảo rằng findcung cấp cho bạn các tập tin theo thứ tự mong muốn? Làm thế nào để bạn đảm bảo rằng các tệp chứa các ký tự khoảng trắng (bao gồm cả dòng mới) trong tên của chúng được xử lý chính xác?
Kusalananda
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.