Vì tất cả các tệp đầu vào đã được sắp xếp, chúng tôi có thể bỏ qua bước sắp xếp thực tế và chỉ sử dụng sort -m
để hợp nhất các tệp lại với nhau.
Trên một số hệ thống Unix (theo hiểu biết của tôi chỉ có Linux), có thể đủ để làm
sort -m *.words | uniq -d >dupes.txt
để có được các dòng trùng lặp được ghi vào tệp dupes.txt
.
Để tìm những tập tin mà những dòng này đến từ đâu, sau đó bạn có thể làm
grep -Fx -f dupes.txt *.words
Điều này sẽ hướng dẫn grep
coi các dòng trong dupes.txt
( -f dupes.txt
) là các mẫu chuỗi cố định ( -F
). grep
cũng sẽ yêu cầu toàn bộ dòng khớp hoàn hảo từ đầu đến cuối ( -x
). Nó sẽ in tên tập tin và dòng đến thiết bị đầu cuối.
Unice Linux (hoặc thậm chí nhiều tệp hơn )
Trên một số hệ thống Unix, 30000 tên tệp sẽ mở rộng thành một chuỗi quá dài để chuyển sang một tiện ích duy nhất (nghĩa là sort -m *.words
sẽ thất bại với Argument list too long
hệ thống OpenBSD của tôi). Ngay cả Linux cũng sẽ phàn nàn về điều này nếu số lượng tệp lớn hơn nhiều.
Tìm bản sao
Điều này có nghĩa là trong trường hợp chung (điều này cũng sẽ hoạt động với nhiều hơn 30000 tệp), người ta phải "chunk" việc sắp xếp:
rm -f tmpfile
find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh
Ngoài ra, tạo tmpfile
mà không có xargs
:
rm -f tmpfile
find . -type f -name '*.words' -exec sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh {} +
Điều này sẽ tìm thấy tất cả các tệp trong thư mục hiện tại (hoặc bên dưới) có tên trùng khớp *.words
. Đối với một đoạn có kích thước phù hợp của các tên này tại một thời điểm, kích thước được xác định bởi xargs
/ find
, nó sẽ hợp nhất chúng lại với nhau thành tmpfile
tệp được sắp xếp . Nếu tmpfile
đã tồn tại (đối với tất cả trừ đoạn đầu tiên), tệp này cũng được hợp nhất với các tệp khác trong đoạn hiện tại. Tùy thuộc vào độ dài tên tệp của bạn và độ dài tối đa được phép của một dòng lệnh, điều này có thể yêu cầu nhiều hơn hoặc nhiều hơn 10 lần chạy tập lệnh nội bộ ( find
/ xargs
sẽ tự động thực hiện việc này).
sh
Kịch bản "nội bộ" ,
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi
sử dụng sort -o tmpfile
để xuất ra tmpfile
(điều này sẽ không ghi đè tmpfile
ngay cả khi đây cũng là đầu vào sort
) và -m
để thực hiện hợp nhất. Trong cả hai nhánh, "$@"
sẽ mở rộng thành một danh sách các tên tệp được trích dẫn riêng được chuyển đến tập lệnh từ find
hoặc xargs
.
Sau đó, chỉ cần chạy uniq -d
trên tmpfile
để có được tất cả các dòng được sao chép:
uniq -d tmpfile >dupes.txt
Nếu bạn thích nguyên tắc "DRY" ("Đừng lặp lại chính mình"), bạn có thể viết tập lệnh nội bộ dưới dạng
if [ -f tmpfile ]; then
t=tmpfile
else
t=/dev/null
fi
sort -o tmpfile -m "$t" "$@"
hoặc là
t=tmpfile
[ ! -f "$t" ] && t=/dev/null
sort -o tmpfile -m "$t" "$@"
Họ đến từ đâu vậy?
Vì những lý do tương tự như trên, chúng tôi không thể sử dụng grep -Fx -f dupes.txt *.words
để tìm nơi các bản sao này đến từ đâu, vì vậy thay vào đó chúng tôi sử dụng find
lại:
find . -type f -name '*.words' \
-exec grep -Fx -f dupes.txt {} +
Vì không có quá trình xử lý "phức tạp" nào được thực hiện, chúng tôi có thể gọi grep
trực tiếp từ đó -exec
. Các -exec
tùy chọn có một lệnh tiện ích và sẽ đặt tên được tìm thấy trong {}
. Với +
lúc kết thúc, find
sẽ đặt như nhiều đối số thay {}
vì hỗ trợ vỏ hiện tại trong mỗi lời gọi của tiện ích.
Để hoàn toàn chính xác, người ta có thể muốn sử dụng một trong hai
find . -type f -name '*.words' \
-exec grep -H -Fx -f dupes.txt {} +
hoặc là
find . -type f -name '*.words' \
-exec grep -Fx -f dupes.txt /dev/null {} +
để chắc chắn rằng tên tệp luôn được bao gồm trong đầu ra từ grep
.
Biến thể đầu tiên sử dụng grep -H
để luôn xuất ra tên tệp phù hợp. Biến thể cuối cùng sử dụng thực tế grep
sẽ bao gồm tên của tệp phù hợp nếu có nhiều hơn một tệp được đưa ra trên dòng lệnh.
Điều này quan trọng vì đoạn tên cuối cùng được gửi đến grep
từ find
thực tế có thể chỉ chứa một tên tệp duy nhất, trong trường hợp đó grep
sẽ không đề cập đến nó trong kết quả của nó.
Tài liệu khen thưởng:
Mổ xẻ find
+ xargs
+ sh
lệnh:
find . -type f -name '*.words' -print0 |
xargs -0 sh -c '
if [ -f tmpfile ]; then
sort -o tmpfile -m tmpfile "$@"
else
sort -o tmpfile -m "$@"
fi' sh
find . -type f -name '*.words'
sẽ chỉ tạo một danh sách các tên đường dẫn từ thư mục hiện tại (hoặc bên dưới) trong đó mỗi tên đường dẫn là một tệp thông thường ( -type f
) và có một thành phần tên tệp ở cuối phù hợp *.words
. Nếu chỉ tìm kiếm thư mục hiện tại , người ta có thể thêm -maxdepth 1
sau .
, trước -type f
.
-print0
sẽ đảm bảo rằng tất cả các tên đường dẫn được tìm thấy được xuất ra với một ký tự \0
( nul
) là dấu phân cách. Đây là một ký tự không hợp lệ trong đường dẫn Unix và nó cho phép chúng tôi xử lý tên đường dẫn ngay cả khi chúng có chứa các ký tự dòng mới (hoặc những thứ kỳ lạ khác).
find
ống đầu ra của nó để xargs
.
xargs -0
sẽ đọc \0
danh sách tên đường dẫn được phân tách và sẽ thực thi tiện ích đã cho lặp đi lặp lại với các khối này, đảm bảo rằng tiện ích được thực thi với các đối số vừa đủ để không khiến shell phàn nàn về danh sách đối số quá dài, cho đến khi không còn đầu vào nữa từ find
.
Tiện ích được gọi bởi xargs
là sh
với một tập lệnh được đưa ra trên dòng lệnh dưới dạng một chuỗi sử dụng -c
cờ của nó .
Khi gọi sh -c '...some script...'
với các đối số theo sau, các đối số sẽ có sẵn cho tập lệnh $@
, ngoại trừ đối số đầu tiên sẽ được đặt vào $0
(đây là "tên lệnh" mà bạn có thể phát hiện ra, ví dụ top
nếu bạn đủ nhanh). Đây là lý do tại sao chúng ta chèn chuỗi sh
làm đối số đầu tiên sau khi kết thúc tập lệnh thực tế. Chuỗi sh
là một đối số giả và có thể là bất kỳ từ nào (một số dường như thích _
hoặc sh-find
).
fi' sh
gì?