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 grepcoi các dòng trong dupes.txt( -f dupes.txt) là các mẫu chuỗi cố định ( -F). grepcũ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 *.wordssẽ thất bại với Argument list too longhệ 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 tmpfilemà 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 tmpfiletệ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/ xargssẽ tự động thực hiện việc này).
shKị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 đè tmpfilengay 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ừ findhoặc xargs.
Sau đó, chỉ cần chạy uniq -dtrê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 findlạ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 greptrực tiếp từ đó -exec. Các -exectù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, findsẽ đặ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ế grepsẽ 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 greptừ findthực tế có thể chỉ chứa một tên tệp duy nhất, trong trường hợp đó grepsẽ không đề cập đến nó trong kết quả của nó.
Tài liệu khen thưởng:
Mổ xẻ find+ xargs+ shlệ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 1sau ., trước -type f.
-print0sẽ đả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 -0sẽ đọc \0danh 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 xargslà shvớ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 -ccờ 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ụ topnếu bạn đủ nhanh). Đây là lý do tại sao chúng ta chèn chuỗi shlàm đối số đầu tiên sau khi kết thúc tập lệnh thực tế. Chuỗi shlà 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' shgì?