Phân tích đầu ra của ls
là không đáng tin cậy .
Thay vào đó, sử dụng find
để xác định vị trí các tệp và sort
sắp xếp chúng theo dấu thời gian. Ví dụ:
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
# do something with $file here
done < <(find . -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Tất cả những điều này đang làm gì?
Đầu tiên, các find
lệnh định vị tất cả các tệp và thư .
mục trong thư mục hiện tại ( ), nhưng không nằm trong thư mục con của thư mục hiện tại ( -maxdepth 1
), sau đó in ra:
- Dấu thời gian
- Một không gian
- Đường dẫn tương đối đến tệp
- Một nhân vật NULL
Dấu thời gian là quan trọng. Trình %T@
xác định định dạng để -printf
phân tích thành T
, cho biết "Thời gian sửa đổi lần cuối" của tệp (mtime) và @
, cho biết "Giây kể từ năm 1970", bao gồm cả giây phân đoạn.
Không gian chỉ là một dấu phân cách tùy ý. Đường dẫn đầy đủ đến tệp để chúng ta có thể tham chiếu sau và ký tự NULL là dấu kết thúc vì đây là ký tự không hợp lệ trong tên tệp và do đó cho chúng tôi biết rằng chúng tôi đã đến cuối đường dẫn đến tập tin.
Tôi đã bao gồm 2>/dev/null
để các tệp mà người dùng không có quyền truy cập bị loại trừ, nhưng các thông báo lỗi về chúng bị loại trừ sẽ bị loại bỏ.
Kết quả của find
lệnh là một danh sách tất cả các thư mục trong thư mục hiện tại. Danh sách này sort
được dẫn đến:
-z
Coi NULL là ký tự kết thúc dòng thay vì dòng mới.
-n
Sắp xếp số
Vì giây từ năm 1970 luôn tăng lên, chúng tôi muốn tệp có dấu thời gian là số nhỏ nhất. Kết quả đầu tiên từ sort
sẽ là dòng chứa dấu thời gian được đánh số nhỏ nhất. Tất cả những gì còn lại là để trích xuất tên tập tin.
Các kết quả của find
, sort
đường ống được chuyển qua thay thế quá trình đến while
nơi nó được đọc như thể nó là một tệp trên stdin. while
lần lượt gọi read
để xử lý đầu vào.
Trong bối cảnh read
chúng ta đặt IFS
biến thành không có gì, điều đó có nghĩa là khoảng trắng sẽ không được hiểu một cách không thích hợp là một dấu phân cách. read
được kể -r
, giúp chặn đứng sự bành trướng thoát, và -d $'\0'
, mà làm cho end-of-line NULL delimiter, phù hợp với sản lượng từ của chúng tôi find
, sort
đường ống.
Đoạn dữ liệu đầu tiên, đại diện cho đường dẫn tệp cũ nhất trước dấu thời gian của nó và khoảng trắng, được đọc vào biến line
. Tiếp theo, thay thế tham số được sử dụng với biểu thức #*
, chỉ đơn giản là thay thế tất cả các ký tự từ đầu chuỗi cho đến khoảng trắng đầu tiên, bao gồm cả khoảng trắng, không có gì. Điều này loại bỏ dấu thời gian sửa đổi, chỉ để lại đường dẫn đầy đủ đến tệp.
Tại thời điểm này, tên tệp được lưu trữ $file
và bạn có thể làm bất cứ điều gì bạn muốn với nó. Khi bạn thực hiện xong làm điều gì đó với $file
những while
tuyên bố sẽ lặp và các read
lệnh sẽ được thực hiện một lần nữa, giải nén các đoạn tiếp theo và tên file tiếp theo.
Có cách nào đơn giản hơn không?
Không. Cách đơn giản hơn là lỗi.
Nếu bạn sử dụng ls -t
và ống để head
hoặc tail
(hoặc bất cứ điều gì ), bạn sẽ phá vỡ trên các tập tin với dòng mới trong tên tập tin. Nếu mv $(anything)
sau đó các tệp có khoảng trắng trong tên sẽ gây ra vỡ. Nếu mv "$(anything)"
sau đó bạn tập tin với dòng mới trong tên sẽ gây ra vỡ. Nếu bạn read
không có -d $'\0'
thì bạn sẽ phá vỡ các tệp có khoảng trắng trong tên của chúng.
Có lẽ trong các trường hợp cụ thể, bạn biết chắc chắn rằng một cách đơn giản hơn là đủ, nhưng bạn không bao giờ nên viết các giả định như vậy vào các tập lệnh nếu bạn có thể tránh làm như vậy.
Giải pháp
#!/usr/bin/env bash
# move to the first argument
dest="$1"
# move from the second argument or .
source="${2-.}"
# move the file count in the third argument or 20
limit="${3-20}"
while IFS= read -r -d $'\0' line ; do
file="${line#* }"
echo mv "$file" "$dest"
let limit-=1
[[ $limit -le 0 ]] && break
done < <(find "$source" -maxdepth 1 -printf '%T@ %p\0' \
2>/dev/null | sort -z -n)
Gọi như:
move-oldest /mnt/backup/ /var/log/foo/ 20
Để di chuyển 20 tập tin cũ nhất từ /var/log/foo/
đến /mnt/backup/
.
Lưu ý rằng tôi đang bao gồm các tập tin và thư mục. Đối với các tập tin chỉ thêm -type f
vào find
lời mời.
Cảm ơn
Nhờ enzotib và Павел Танков cho các cải tiến cho câu trả lời này.